@narumitw/pi-sync 0.51.0 → 0.52.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.
- package/README.md +30 -5
- package/dist/chunks/{chunk-7KHKWL3Q.ts → chunk-2CBF6SMT.ts} +8 -7
- package/dist/chunks/chunk-2CBF6SMT.ts.map +7 -0
- package/dist/chunks/{chunk-YFADF5ZQ.ts → chunk-2DI6PDZV.ts} +5 -1
- package/dist/chunks/chunk-2DI6PDZV.ts.map +7 -0
- package/dist/chunks/chunk-2GUXI2XT.ts +1702 -0
- package/dist/chunks/chunk-2GUXI2XT.ts.map +7 -0
- package/dist/chunks/{chunk-4MPIH46N.ts → chunk-3B4Z2SKV.ts} +40 -6
- package/dist/chunks/chunk-3B4Z2SKV.ts.map +7 -0
- package/dist/chunks/{chunk-YPXGU5OR.ts → chunk-4QVPTIZB.ts} +16 -6
- package/dist/chunks/{chunk-YPXGU5OR.ts.map → chunk-4QVPTIZB.ts.map} +2 -2
- package/dist/chunks/{chunk-UA7RXOIT.ts → chunk-4SBTAVFL.ts} +6 -6
- package/dist/chunks/{chunk-6546TS54.ts → chunk-5RZPINUQ.ts} +7 -4
- package/dist/chunks/{chunk-6546TS54.ts.map → chunk-5RZPINUQ.ts.map} +2 -2
- package/dist/chunks/chunk-76NB4R7U.ts +107 -0
- package/dist/chunks/chunk-76NB4R7U.ts.map +7 -0
- package/dist/chunks/{chunk-DM4X4FIA.ts → chunk-7D6RDCVC.ts} +8 -10
- package/dist/chunks/{chunk-DM4X4FIA.ts.map → chunk-7D6RDCVC.ts.map} +2 -2
- package/dist/chunks/chunk-A6HU4ERM.ts +124 -0
- package/dist/chunks/chunk-A6HU4ERM.ts.map +7 -0
- package/dist/chunks/chunk-AMJRUXFT.ts +17 -0
- package/dist/chunks/chunk-AMJRUXFT.ts.map +7 -0
- package/dist/chunks/chunk-ANWNQIR7.ts +1013 -0
- package/dist/chunks/chunk-ANWNQIR7.ts.map +7 -0
- package/dist/chunks/{chunk-T56RNRZJ.ts → chunk-BDB4IDM3.ts} +19 -10
- package/dist/chunks/chunk-BDB4IDM3.ts.map +7 -0
- package/dist/chunks/{chunk-2RZSQWBH.ts → chunk-CHSIAC4N.ts} +5 -5
- package/dist/chunks/{chunk-2RZSQWBH.ts.map → chunk-CHSIAC4N.ts.map} +2 -2
- package/dist/chunks/chunk-HUZ5CR4W.ts +110 -0
- package/dist/chunks/chunk-HUZ5CR4W.ts.map +7 -0
- package/dist/chunks/{chunk-QLSN7AGX.ts → chunk-IJ6VV6FN.ts} +81 -19
- package/dist/chunks/chunk-IJ6VV6FN.ts.map +7 -0
- package/dist/chunks/chunk-ORQRHEEO.ts +362 -0
- package/dist/chunks/chunk-ORQRHEEO.ts.map +7 -0
- package/dist/chunks/chunk-QE7NZZWT.ts +380 -0
- package/dist/chunks/chunk-QE7NZZWT.ts.map +7 -0
- package/dist/chunks/{chunk-HGJH56KR.ts → chunk-QHSQCK7L.ts} +4 -4
- package/dist/chunks/chunk-QOFMXPM5.ts +735 -0
- package/dist/chunks/chunk-QOFMXPM5.ts.map +7 -0
- package/dist/chunks/{chunk-DZUSTGQV.ts → chunk-UQ3ALBIB.ts} +4 -4
- package/dist/chunks/{chunk-Q32CQWPM.ts → chunk-V6VZ5TLG.ts} +13 -6
- package/dist/chunks/chunk-V6VZ5TLG.ts.map +7 -0
- package/dist/chunks/{chunk-E42GXD6D.ts → chunk-WKPX2SIC.ts} +4 -4
- package/dist/chunks/chunk-ZW5ZLGBI.ts +154 -0
- package/dist/chunks/chunk-ZW5ZLGBI.ts.map +7 -0
- package/dist/chunks/{chunk-6NT3YFGY.ts → chunk-ZX2Q6THB.ts} +8 -3
- package/dist/chunks/{chunk-6NT3YFGY.ts.map → chunk-ZX2Q6THB.ts.map} +2 -2
- package/dist/chunks/conflict-artifacts-BB2FZMIE.ts +27 -0
- package/dist/chunks/conflict-review-T3PB7ZSD.ts +175 -0
- package/dist/chunks/conflict-review-T3PB7ZSD.ts.map +7 -0
- package/dist/chunks/{file-selection-RAVHRYFG.ts → file-selection-WZX7HAY2.ts} +8 -7
- package/dist/chunks/{file-selection-RAVHRYFG.ts.map → file-selection-WZX7HAY2.ts.map} +1 -1
- package/dist/chunks/{git-backend-IAANDL3K.ts → git-backend-HLGKUQJT.ts} +19 -7
- package/dist/chunks/git-backend-HLGKUQJT.ts.map +7 -0
- package/dist/chunks/local-fields-6DGYOOIK.ts +22 -0
- package/dist/chunks/manager-result-dispatcher-VIDT3QBU.ts +23 -0
- package/dist/chunks/{manager-ui-763NEKJY.ts → manager-ui-HSMHS3OK.ts} +236 -57
- package/dist/chunks/manager-ui-HSMHS3OK.ts.map +7 -0
- package/dist/chunks/merge-baseline-store-EVQAAML6.ts +22 -0
- package/dist/chunks/remote-selection-ui-SMF5CLFO.ts +22 -0
- package/dist/chunks/{s3-backend-XGXUOPUN.ts → s3-backend-DJ2NT3SZ.ts} +16 -9
- package/dist/chunks/s3-backend-DJ2NT3SZ.ts.map +7 -0
- package/dist/chunks/{setup-switch-5G2VNJHL.ts → setup-switch-KTUWL6DE.ts} +9 -6
- package/dist/chunks/{setup-wizard-YIEWGX32.ts → setup-wizard-GSDPDFC4.ts} +10 -7
- package/dist/chunks/{setup-wizard-YIEWGX32.ts.map → setup-wizard-GSDPDFC4.ts.map} +1 -1
- package/dist/chunks/{snapshot-JL5DFSXI.ts → snapshot-THS5EY2L.ts} +6 -9
- package/dist/chunks/snapshot-THS5EY2L.ts.map +7 -0
- package/dist/chunks/{sync-inspection-P3ENJLNF.ts → sync-inspection-VBJF2FWC.ts} +10 -10
- package/dist/chunks/sync-inspection-VBJF2FWC.ts.map +7 -0
- package/dist/chunks/{sync-operations-AR76EB6C.ts → sync-operations-27VQUDH2.ts} +309 -508
- package/dist/chunks/sync-operations-27VQUDH2.ts.map +7 -0
- package/dist/chunks/{sync-state-VHJUZ4YG.ts → sync-state-CFL4RRRB.ts} +6 -6
- package/dist/chunks/sync-state-CFL4RRRB.ts.map +7 -0
- package/dist/chunks/{webdav-backend-3EGUONAT.ts → webdav-backend-ETQGSI6I.ts} +13 -7
- package/dist/chunks/webdav-backend-ETQGSI6I.ts.map +7 -0
- package/dist/index.ts +142 -43
- package/dist/index.ts.map +2 -2
- package/docs/content-merge-audit.md +156 -0
- package/docs/merge-implementation-audit.md +312 -0
- package/docs/settings-merge-audit.md +153 -0
- package/docs/settings.md +36 -0
- package/package.json +6 -6
- package/src/backends/git/git-backend.ts +2 -0
- package/src/backends/git/git-storage.ts +8 -3
- package/src/backends/s3/s3-backend.ts +2 -1
- package/src/backends/webdav/webdav-backend.ts +2 -1
- package/src/commands/command-execution.ts +10 -0
- package/src/commands/command.ts +4 -0
- package/src/settings/config.ts +12 -0
- package/src/settings/settings-management.ts +2 -2
- package/src/settings/settings-types.ts +14 -1
- package/src/settings/settings-validation.ts +24 -3
- package/src/snapshot/session-paths.ts +34 -7
- package/src/snapshot/snapshot-apply.ts +81 -32
- package/src/snapshot/snapshot-case-replacement.ts +80 -0
- package/src/snapshot/snapshot-codec.ts +6 -1
- package/src/snapshot/snapshot-root-transition.ts +154 -0
- package/src/snapshot/snapshot-transaction-plan.ts +72 -0
- package/src/snapshot/snapshot-transaction.ts +844 -102
- package/src/snapshot/snapshot-types.ts +5 -0
- package/src/state/json-file.ts +32 -5
- package/src/state/merge-baseline-store.ts +182 -0
- package/src/state/mutation-directory-sync.ts +34 -0
- package/src/state/state-types.ts +4 -0
- package/src/state/sync-state-store.ts +14 -1
- package/src/sync/automatic-sync.ts +11 -3
- package/src/sync/conflict-artifact-ownership.ts +63 -0
- package/src/sync/conflict-artifacts.ts +195 -0
- package/src/sync/conflict-groups.ts +68 -0
- package/src/sync/conflict-preview.ts +56 -0
- package/src/sync/conflict-resolution.ts +93 -0
- package/src/sync/conflict-retention.ts +72 -0
- package/src/sync/conflict-review.ts +95 -0
- package/src/sync/content-conflicts.ts +73 -0
- package/src/sync/field-migration.ts +39 -0
- package/src/sync/file-merge-planner.ts +143 -0
- package/src/sync/json-document.ts +184 -0
- package/src/sync/local-fields.ts +134 -0
- package/src/sync/merge-apply.ts +380 -0
- package/src/sync/merge-journal.ts +219 -0
- package/src/sync/merged-sync.ts +723 -0
- package/src/sync/partial-progress.ts +54 -0
- package/src/sync/remote-snapshot.ts +6 -0
- package/src/sync/session-merge.ts +194 -0
- package/src/sync/settings-conflicts.ts +52 -0
- package/src/sync/settings-merge.ts +57 -0
- package/src/sync/startup-check.ts +90 -5
- package/src/sync/sync-inspection.ts +1 -0
- package/src/sync/sync-local.ts +61 -0
- package/src/sync/sync-mutations.ts +256 -114
- package/src/sync/sync-queries.ts +6 -2
- package/src/sync/sync-state.ts +14 -5
- package/src/sync/text-merge.ts +133 -0
- package/src/sync-extension.ts +8 -0
- package/src/ui/manager-state.ts +12 -1
- package/src/ui/manager-ui.ts +10 -0
- package/src/ui/merge-review.ts +28 -0
- package/src/ui/remote-selection-ui.ts +1 -0
- package/src/ui/settings-ui.ts +183 -2
- package/src/ui/setup/setup-actions.ts +2 -2
- package/src/ui/setup/setup-switcher.ts +2 -1
- package/src/ui/storage-connections-ui.ts +11 -7
- package/src/ui/sync-attention.ts +3 -0
- package/src/ui/sync-format.ts +16 -12
- package/src/ui/sync-setups-ui.ts +1 -0
- package/src/ui/terminal-text.ts +9 -2
- package/dist/chunks/chunk-4AIHXRKE.ts +0 -197
- package/dist/chunks/chunk-4AIHXRKE.ts.map +0 -7
- package/dist/chunks/chunk-4MPIH46N.ts.map +0 -7
- package/dist/chunks/chunk-7KHKWL3Q.ts.map +0 -7
- package/dist/chunks/chunk-EEJPYTWE.ts +0 -348
- package/dist/chunks/chunk-EEJPYTWE.ts.map +0 -7
- package/dist/chunks/chunk-IVXEDPAD.ts +0 -370
- package/dist/chunks/chunk-IVXEDPAD.ts.map +0 -7
- package/dist/chunks/chunk-MSSW6F3R.ts +0 -12
- package/dist/chunks/chunk-MSSW6F3R.ts.map +0 -7
- package/dist/chunks/chunk-OVDTF5CH.ts +0 -90
- package/dist/chunks/chunk-OVDTF5CH.ts.map +0 -7
- package/dist/chunks/chunk-Q32CQWPM.ts.map +0 -7
- package/dist/chunks/chunk-QLSN7AGX.ts.map +0 -7
- package/dist/chunks/chunk-SWUXA3MH.ts +0 -18
- package/dist/chunks/chunk-SWUXA3MH.ts.map +0 -7
- package/dist/chunks/chunk-T56RNRZJ.ts.map +0 -7
- package/dist/chunks/chunk-YFADF5ZQ.ts.map +0 -7
- package/dist/chunks/git-backend-IAANDL3K.ts.map +0 -7
- package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +0 -23
- package/dist/chunks/manager-ui-763NEKJY.ts.map +0 -7
- package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +0 -22
- package/dist/chunks/s3-backend-XGXUOPUN.ts.map +0 -7
- package/dist/chunks/sync-operations-AR76EB6C.ts.map +0 -7
- package/dist/chunks/webdav-backend-3EGUONAT.ts.map +0 -7
- /package/dist/chunks/{chunk-UA7RXOIT.ts.map → chunk-4SBTAVFL.ts.map} +0 -0
- /package/dist/chunks/{chunk-HGJH56KR.ts.map → chunk-QHSQCK7L.ts.map} +0 -0
- /package/dist/chunks/{chunk-DZUSTGQV.ts.map → chunk-UQ3ALBIB.ts.map} +0 -0
- /package/dist/chunks/{chunk-E42GXD6D.ts.map → chunk-WKPX2SIC.ts.map} +0 -0
- /package/dist/chunks/{manager-result-dispatcher-OTUMQM26.ts.map → conflict-artifacts-BB2FZMIE.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-TZTJF32Y.ts.map → local-fields-6DGYOOIK.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-5G2VNJHL.ts.map → manager-result-dispatcher-VIDT3QBU.ts.map} +0 -0
- /package/dist/chunks/{snapshot-JL5DFSXI.ts.map → merge-baseline-store-EVQAAML6.ts.map} +0 -0
- /package/dist/chunks/{sync-inspection-P3ENJLNF.ts.map → remote-selection-ui-SMF5CLFO.ts.map} +0 -0
- /package/dist/chunks/{sync-state-VHJUZ4YG.ts.map → setup-switch-KTUWL6DE.ts.map} +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/settings/config-file.ts", "../../src/settings/settings-validation.ts", "../../src/settings/settings-store.ts", "../../src/settings/config.ts"],
|
|
4
|
+
"sourcesContent": ["import { randomUUID } from \"node:crypto\";\nimport {\n mkdir,\n mkdirSync,\n realpath,\n realpathSync,\n rmdir,\n rmdirSync,\n stat,\n statSync,\n utimes,\n utimesSync,\n} from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport lockfile from \"proper-lockfile\";\n\nconst CONFIG_FILE_NAME = \"pi-sync.json\";\nconst LEGACY_CONFIG_FILE_NAME = \"pi-sync.local.json\";\nconst configMigrationNotices = new Map<string, string>();\nconst legacyPresenceNoticed = new Set<string>();\nconst CONFIG_LOCK_STALE_MS = 30_000;\nconst CONFIG_LOCK_UPDATE_MS = 10_000;\nconst LOCKFILE_FS_ADAPTER = {\n mkdir,\n mkdirSync,\n realpath,\n realpathSync,\n rmdir,\n rmdirSync,\n stat,\n statSync,\n utimes,\n utimesSync,\n};\n\ntype PublishFile = (source: string, destination: string) => Promise<void>;\nlet publishConfigFile: PublishFile = publishFileWithoutReplacement;\nlet beforeConfigPublicationHook: () => Promise<void> = async () => undefined;\nlet afterMissingConfigReadProbeHook: () => Promise<void> = async () => undefined;\nlet afterReplacementInstalledHook: () => Promise<void> = async () => undefined;\nlet afterConfigQuarantinedHook: () => Promise<void> = async () => undefined;\n\nexport type FileIdentity = { dev: number; ino: number };\n\nexport async function withConfigFilePublicationForTest<T>(publish: PublishFile, run: () => Promise<T>): Promise<T> {\n const previous = publishConfigFile;\n publishConfigFile = publish;\n try {\n return await run();\n } finally {\n publishConfigFile = previous;\n }\n}\n\nexport async function withConfigPublicationReadyHookForTest<T>(\n hook: () => Promise<void>,\n run: () => Promise<T>,\n): Promise<T> {\n const previous = beforeConfigPublicationHook;\n beforeConfigPublicationHook = hook;\n try {\n return await run();\n } finally {\n beforeConfigPublicationHook = previous;\n }\n}\n\nexport async function withMissingConfigReadProbeHookForTest<T>(\n hook: () => Promise<void>,\n run: () => Promise<T>,\n): Promise<T> {\n const previous = afterMissingConfigReadProbeHook;\n afterMissingConfigReadProbeHook = hook;\n try {\n return await run();\n } finally {\n afterMissingConfigReadProbeHook = previous;\n }\n}\n\nexport async function withConfigReplacementInstalledHookForTest<T>(\n hook: () => Promise<void>,\n run: () => Promise<T>,\n): Promise<T> {\n const previous = afterReplacementInstalledHook;\n afterReplacementInstalledHook = hook;\n try {\n return await run();\n } finally {\n afterReplacementInstalledHook = previous;\n }\n}\n\nexport async function withConfigQuarantinedHookForTest<T>(\n hook: () => Promise<void>,\n run: () => Promise<T>,\n): Promise<T> {\n const previous = afterConfigQuarantinedHook;\n afterConfigQuarantinedHook = hook;\n try {\n return await run();\n } finally {\n afterConfigQuarantinedHook = previous;\n }\n}\n\ntype ConfigSnapshot = {\n bytes: Buffer;\n identity: FileIdentity;\n parsed: Record<string, unknown>;\n};\n\nexport type LocalConfigDocument = ConfigSnapshot & { path: string };\n\nexport function localConfigPath() {\n return path.join(getAgentDir(), CONFIG_FILE_NAME);\n}\n\nexport function legacyLocalConfigPath() {\n return path.join(getAgentDir(), LEGACY_CONFIG_FILE_NAME);\n}\n\nexport async function activeLocalConfigPath() {\n const canonicalPath = localConfigPath();\n const legacyPath = legacyLocalConfigPath();\n return withLocalConfigReadLockIfNeeded(async () => {\n if (await pathExists(canonicalPath)) return canonicalPath;\n return (await pathExists(legacyPath)) ? legacyPath : canonicalPath;\n });\n}\n\nexport function consumeLocalConfigMigrationNotice() {\n const configPath = localConfigPath();\n const notice = configMigrationNotices.get(configPath);\n configMigrationNotices.delete(configPath);\n return notice;\n}\n\nexport async function readActiveLocalConfigDocumentForRepair(): Promise<LocalConfigDocument | undefined> {\n const canonicalPath = localConfigPath();\n const legacyPath = legacyLocalConfigPath();\n return withLocalConfigReadLockIfNeeded(async () => {\n const filePath = (await pathExists(canonicalPath)) ? canonicalPath : legacyPath;\n const snapshot = await readConfigSnapshotIfExists(filePath);\n return snapshot ? { path: filePath, ...snapshot } : undefined;\n });\n}\n\nexport async function readMigratingLocalConfigDocument(\n validateForMigration: (settings: Record<string, unknown>) => void,\n): Promise<LocalConfigDocument | undefined> {\n return withLocalConfigReadLockIfNeeded(async () => {\n const configPath = await prepareLocalConfigPath(validateForMigration);\n const snapshot = await readConfigSnapshotIfExists(configPath);\n return snapshot ? { path: configPath, ...snapshot } : undefined;\n });\n}\n\nasync function withLocalConfigReadLockIfNeeded<T>(read: () => Promise<T>): Promise<T> {\n if ((await pathExists(localConfigPath())) || (await pathExists(legacyLocalConfigPath()))) {\n return withLocalConfigFileLock(read);\n }\n await afterMissingConfigReadProbeHook();\n if (await pathExists(configMutationLockPath())) return withLocalConfigFileLock(read);\n return read();\n}\n\nexport function updateLocalConfigDocument<T extends Record<string, unknown>>(\n defaultValue: T,\n update: (current: T) => T,\n validate: (value: Record<string, unknown>) => void,\n signal?: AbortSignal,\n): Promise<T> {\n return withLocalConfigFileLock(async () => {\n signal?.throwIfAborted();\n const configPath = await prepareLocalConfigPath(validate);\n const snapshot = await readConfigSnapshotIfExists(configPath);\n const document = snapshot ? { path: configPath, ...snapshot } : undefined;\n const current = document ? (structuredClone(document.parsed) as T) : structuredClone(defaultValue);\n const next = update(current);\n validate(next);\n signal?.throwIfAborted();\n if (document && JSON.stringify(document.parsed) === JSON.stringify(next)) return next;\n if (document) await replaceLocalConfigDocumentUnlocked(document, next);\n else await installPrivateConfigExclusively(localConfigPath(), serializedConfig(next));\n return next;\n });\n}\n\nexport function createLocalConfigDocument(value: Record<string, unknown>) {\n return withLocalConfigFileLock(async () => {\n const bytes = serializedConfig(value);\n try {\n await installPrivateConfigExclusively(localConfigPath(), bytes);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n throw new Error(\"Pi-sync settings were created concurrently; reopen settings and retry.\");\n }\n throw error;\n }\n });\n}\n\nexport function replaceLocalConfigDocument(document: LocalConfigDocument, value: Record<string, unknown>) {\n return withLocalConfigFileLock(() => replaceLocalConfigDocumentUnlocked(document, value));\n}\n\nasync function replaceLocalConfigDocumentUnlocked(document: LocalConfigDocument, value: Record<string, unknown>) {\n const nextBytes = serializedConfig(value);\n const canonicalPath = localConfigPath();\n if (document.path !== canonicalPath) {\n if (!(await configDocumentStillMatches(document))) throw settingsChangedError();\n let installed: FileIdentity;\n try {\n installed = await installPrivateConfigExclusively(canonicalPath, nextBytes);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n throw new Error(\"Canonical settings were created concurrently; no settings were replaced.\");\n }\n throw error;\n }\n if (!(await configDocumentStillMatches(document))) {\n await quarantineAndRemoveConfigIfMatchesUnlocked(canonicalPath, installed, nextBytes);\n throw settingsChangedError();\n }\n return;\n }\n\n const quarantinePath = await claimCanonicalConfigDocument(document);\n let installed: FileIdentity;\n try {\n installed = await installPrivateConfigExclusively(canonicalPath, nextBytes);\n } catch (error) {\n await restoreQuarantinedConfig(canonicalPath, quarantinePath);\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n throw new Error(\"Canonical settings changed concurrently; no settings were replaced.\");\n }\n throw error;\n }\n try {\n await afterReplacementInstalledHook();\n if (!(await fileIdentityAndContentsMatch(quarantinePath, document.identity, document.bytes))) {\n throw settingsChangedError();\n }\n if (process.platform !== \"win32\") await fs.chmod(quarantinePath, 0o600);\n } catch (error) {\n await quarantineAndRemoveConfigIfMatchesUnlocked(canonicalPath, installed, nextBytes);\n await restoreQuarantinedConfig(canonicalPath, quarantinePath);\n throw error;\n }\n await fs.rm(quarantinePath).catch(() => undefined);\n await syncParentDirectory(canonicalPath).catch(() => undefined);\n}\n\nasync function prepareLocalConfigPath(validateForMigration: (settings: Record<string, unknown>) => void) {\n const canonicalPath = localConfigPath();\n const legacyPath = legacyLocalConfigPath();\n if (await pathExists(canonicalPath)) {\n const legacyStatus = await secureIgnoredLegacyIfPresent(legacyPath);\n if (legacyStatus !== \"missing\" && !legacyPresenceNoticed.has(canonicalPath)) {\n legacyPresenceNoticed.add(canonicalPath);\n recordConfigMigrationNotice(\n canonicalPath,\n legacyStatus === \"private\"\n ? `${LEGACY_CONFIG_FILE_NAME} legacy settings were ignored because ${CONFIG_FILE_NAME} takes precedence. Delete ${LEGACY_CONFIG_FILE_NAME} after confirming your settings.`\n : `${LEGACY_CONFIG_FILE_NAME} legacy settings were ignored because ${CONFIG_FILE_NAME} takes precedence, but pi-sync could not verify them as a private regular file. Secure or delete the legacy path after confirming your settings.`,\n );\n }\n return canonicalPath;\n }\n\n const legacy = await readConfigSnapshotIfExists(legacyPath);\n if (!legacy) return canonicalPath;\n validateForMigration(legacy.parsed);\n\n let installedIdentity: FileIdentity;\n try {\n installedIdentity = await installPrivateConfigExclusively(canonicalPath, legacy.bytes);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n recordConfigMigrationNotice(\n canonicalPath,\n `${LEGACY_CONFIG_FILE_NAME} legacy settings were ignored because ${CONFIG_FILE_NAME} was created concurrently and takes precedence.`,\n );\n return canonicalPath;\n }\n recordConfigMigrationNotice(\n canonicalPath,\n `Could not migrate ${LEGACY_CONFIG_FILE_NAME} to ${CONFIG_FILE_NAME}; the legacy settings were used for this session and were not changed.`,\n );\n return legacyPath;\n }\n\n if (!(await configSnapshotStillMatches(legacyPath, legacy))) {\n const removed = await quarantineAndRemoveConfigIfMatchesUnlocked(canonicalPath, installedIdentity, legacy.bytes);\n recordConfigMigrationNotice(\n canonicalPath,\n removed\n ? `${LEGACY_CONFIG_FILE_NAME} changed during migration; the stale ${CONFIG_FILE_NAME} copy was removed and the legacy settings were used for this session.`\n : `${LEGACY_CONFIG_FILE_NAME} changed during migration, but ${CONFIG_FILE_NAME} was replaced concurrently and takes precedence.`,\n );\n return removed ? legacyPath : canonicalPath;\n }\n\n legacyPresenceNoticed.add(canonicalPath);\n recordConfigMigrationNotice(\n canonicalPath,\n `pi-sync settings migrated from ${LEGACY_CONFIG_FILE_NAME} to ${CONFIG_FILE_NAME}; the private legacy file was retained as a recovery copy and can be deleted after verification.`,\n );\n return canonicalPath;\n}\n\nasync function secureIgnoredLegacyIfPresent(filePath: string) {\n let pathStat: Awaited<ReturnType<typeof fs.lstat>>;\n try {\n pathStat = await fs.lstat(filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return \"missing\" as const;\n return \"unsafe\" as const;\n }\n if (pathStat.isSymbolicLink() || !pathStat.isFile()) return \"unsafe\" as const;\n\n let handle: fs.FileHandle | undefined;\n try {\n handle = await fs.open(filePath, \"r\");\n const openedStat = await handle.stat();\n if (openedStat.dev !== pathStat.dev || openedStat.ino !== pathStat.ino) {\n return \"unsafe\" as const;\n }\n if (process.platform !== \"win32\" && (openedStat.mode & 0o777) !== 0o600) {\n await handle.chmod(0o600);\n }\n return \"private\" as const;\n } catch {\n return \"unsafe\" as const;\n } finally {\n await handle?.close().catch(() => undefined);\n }\n}\n\nasync function readConfigSnapshotIfExists(filePath: string): Promise<ConfigSnapshot | undefined> {\n let pathStat: Awaited<ReturnType<typeof fs.lstat>>;\n try {\n pathStat = await fs.lstat(filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw error;\n }\n if (pathStat.isSymbolicLink()) {\n throw new Error(`Refusing to read symlinked pi-sync config: ${filePath}`);\n }\n if (!pathStat.isFile()) throw new Error(`pi-sync config is not a regular file: ${filePath}`);\n\n const handle = await fs.open(filePath, \"r\");\n try {\n const openedStat = await handle.stat();\n if (openedStat.dev !== pathStat.dev || openedStat.ino !== pathStat.ino) {\n throw new Error(`pi-sync config changed while opening: ${filePath}`);\n }\n if (process.platform !== \"win32\" && (openedStat.mode & 0o777) !== 0o600) {\n await handle.chmod(0o600);\n }\n const bytes = await handle.readFile();\n return {\n bytes,\n identity: { dev: openedStat.dev, ino: openedStat.ino },\n parsed: parseConfigObject(bytes, filePath),\n };\n } finally {\n await handle.close();\n }\n}\n\nfunction serializedConfig(value: Record<string, unknown>) {\n return Buffer.from(`${JSON.stringify(value, null, \"\\t\")}\\n`, \"utf8\");\n}\n\nfunction parseConfigObject(bytes: Buffer, filePath: string) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(bytes.toString(\"utf8\"));\n } catch {\n throw new SyntaxError(`Invalid JSON in pi-sync config: ${filePath}`);\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`pi-sync config must contain a JSON object: ${filePath}`);\n }\n return parsed as Record<string, unknown>;\n}\n\nasync function installPrivateConfigExclusively(filePath: string, bytes: Buffer) {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n const temporaryPath = path.join(\n path.dirname(filePath),\n `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.migrate`,\n );\n let handle: fs.FileHandle | undefined;\n try {\n handle = await fs.open(temporaryPath, \"wx\", 0o600);\n await handle.writeFile(bytes);\n if (process.platform !== \"win32\") await handle.chmod(0o600);\n await handle.sync();\n await handle.close();\n handle = undefined;\n await publishConfigFile(temporaryPath, filePath);\n const installed = await fs.lstat(filePath);\n let publishedHandle: fs.FileHandle | undefined;\n let publicationError: unknown;\n try {\n if (installed.isSymbolicLink() || !installed.isFile()) {\n throw new Error(`Published pi-sync settings are not a regular file: ${filePath}`);\n }\n publishedHandle = await fs.open(filePath, \"r+\");\n const published = await publishedHandle.stat();\n if (published.dev !== installed.dev || published.ino !== installed.ino) {\n throw new Error(`Published pi-sync settings changed while opening: ${filePath}`);\n }\n if (process.platform !== \"win32\") await publishedHandle.chmod(0o600);\n await publishedHandle.sync();\n await syncParentDirectory(filePath);\n } catch (error) {\n publicationError = error;\n } finally {\n await publishedHandle?.close().catch(() => undefined);\n }\n if (publicationError) {\n await quarantineAndRemoveConfigIfMatchesUnlocked(filePath, { dev: installed.dev, ino: installed.ino }, bytes);\n throw publicationError;\n }\n return { dev: installed.dev, ino: installed.ino };\n } finally {\n await handle?.close().catch(() => undefined);\n await fs.rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nasync function configDocumentStillMatches(document: LocalConfigDocument) {\n return fileIdentityAndContentsMatch(document.path, document.identity, document.bytes);\n}\n\nasync function claimCanonicalConfigDocument(document: LocalConfigDocument) {\n const quarantinePath = path.join(\n path.dirname(document.path),\n `.${path.basename(document.path)}.${randomUUID()}.schema-migration-source`,\n );\n try {\n await fs.rename(document.path, quarantinePath);\n await syncParentDirectory(document.path);\n } catch (error) {\n await restoreQuarantinedConfig(document.path, quarantinePath);\n throw error;\n }\n if (!(await fileIdentityAndContentsMatch(quarantinePath, document.identity, document.bytes))) {\n await restoreQuarantinedConfig(document.path, quarantinePath);\n throw settingsChangedError();\n }\n if (await pathExists(document.path)) {\n await restoreQuarantinedConfig(document.path, quarantinePath);\n throw new Error(\"Canonical settings changed concurrently; no settings were replaced.\");\n }\n return quarantinePath;\n}\n\nfunction settingsChangedError() {\n return new Error(\"pi-sync settings changed during migration; no settings were replaced.\");\n}\n\nasync function configSnapshotStillMatches(filePath: string, snapshot: ConfigSnapshot) {\n return fileIdentityAndContentsMatch(filePath, snapshot.identity, snapshot.bytes);\n}\n\nexport function quarantineAndRemoveConfigIfMatches(filePath: string, identity: FileIdentity, expectedBytes: Buffer) {\n return withLocalConfigFileLock(() => quarantineAndRemoveConfigIfMatchesUnlocked(filePath, identity, expectedBytes));\n}\n\nasync function quarantineAndRemoveConfigIfMatchesUnlocked(\n filePath: string,\n identity: FileIdentity,\n expectedBytes: Buffer,\n) {\n const quarantinePath = path.join(\n path.dirname(filePath),\n `.${path.basename(filePath)}.${randomUUID()}.migration-retired`,\n );\n try {\n await fs.rename(filePath, quarantinePath);\n } catch {\n return false;\n }\n await afterConfigQuarantinedHook();\n try {\n await syncParentDirectory(filePath);\n } catch {\n await restoreQuarantinedConfig(filePath, quarantinePath);\n return false;\n }\n\n const matches = await fileIdentityAndContentsMatch(quarantinePath, identity, expectedBytes);\n if (!matches) {\n await restoreQuarantinedConfig(filePath, quarantinePath);\n return false;\n }\n if (await pathExists(filePath)) {\n await fs.rm(quarantinePath, { force: true });\n await syncParentDirectory(filePath);\n return false;\n }\n await fs.rm(quarantinePath);\n await syncParentDirectory(filePath);\n return true;\n}\n\nasync function fileIdentityAndContentsMatch(filePath: string, identity: FileIdentity, expectedBytes: Buffer) {\n try {\n const current = await fs.lstat(filePath);\n if (current.isSymbolicLink()) return false;\n if (current.dev !== identity.dev || current.ino !== identity.ino) return false;\n return (await fs.readFile(filePath)).equals(expectedBytes);\n } catch {\n return false;\n }\n}\n\nasync function restoreQuarantinedConfig(filePath: string, quarantinePath: string) {\n try {\n await renameFileWithoutReplacement(quarantinePath, filePath);\n if (process.platform !== \"win32\") await fs.chmod(filePath, 0o600);\n await syncParentDirectory(filePath);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") return;\n }\n try {\n if (process.platform !== \"win32\") await fs.chmod(quarantinePath, 0o600);\n await syncParentDirectory(filePath);\n } catch {\n // Preserve the quarantine rather than risk deleting settings that changed concurrently.\n }\n}\n\nfunction configMutationLockPath() {\n return `${localConfigPath()}.mutation-lock`;\n}\n\nexport async function withLocalConfigFileLock<T>(run: () => Promise<T>): Promise<T> {\n const configPath = localConfigPath();\n await fs.mkdir(path.dirname(configPath), { recursive: true });\n let compromisedError: Error | undefined;\n const release = await lockfile.lock(configPath, {\n fs: LOCKFILE_FS_ADAPTER,\n lockfilePath: configMutationLockPath(),\n realpath: false,\n stale: CONFIG_LOCK_STALE_MS,\n update: CONFIG_LOCK_UPDATE_MS,\n retries: { retries: 100, factor: 1.2, minTimeout: 10, maxTimeout: 100 },\n onCompromised: (error) => {\n compromisedError = error;\n },\n });\n try {\n const result = await run();\n if (compromisedError) throw compromisedError;\n return result;\n } finally {\n await release();\n }\n}\n\nasync function syncParentDirectory(filePath: string) {\n if (process.platform === \"win32\") return;\n let handle: fs.FileHandle | undefined;\n try {\n handle = await fs.open(path.dirname(filePath), \"r\");\n await handle.sync();\n } finally {\n await handle?.close().catch(() => undefined);\n }\n}\n\nasync function publishFileWithoutReplacement(source: string, destination: string) {\n await beforeConfigPublicationHook();\n await renameFileWithoutReplacement(source, destination);\n}\n\nasync function renameFileWithoutReplacement(source: string, destination: string) {\n if (await pathExists(destination)) {\n throw Object.assign(new Error(`Settings already exist: ${destination}`), { code: \"EEXIST\" });\n }\n await fs.rename(source, destination);\n}\n\nasync function pathExists(filePath: string) {\n try {\n await fs.lstat(filePath);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw error;\n }\n}\n\nfunction recordConfigMigrationNotice(configPath: string, notice: string) {\n if (!configMigrationNotices.has(configPath)) configMigrationNotices.set(configPath, notice);\n}\n", "import path from \"node:path\";\nimport { normalizeEndpointIdentity } from \"../backends/backend-identity.js\";\nimport {\n normalizeGitBranch,\n normalizeGitDirectory,\n normalizeGitRemote,\n normalizeGitRemoteIdentity,\n} from \"../backends/git/git-config.js\";\nimport {\n normalizeWebDavIdentityUrl,\n normalizeWebDavPath,\n normalizeWebDavUrl,\n validateWebDavCredentials,\n} from \"../backends/webdav/webdav-config.js\";\nimport { normalizeLocalFields } from \"../sync/local-fields.js\";\nimport { normalizeSyncInclude } from \"../sync/sync-policy.js\";\nimport { localConfigPath } from \"./config-file.js\";\nimport type {\n OnSwitchAction,\n PiSyncSettingsV3,\n StorageConnectionSettings,\n SyncSetupSettings,\n} from \"./settings-types.js\";\n\nexport const DEFAULT_ON_SWITCH: OnSwitchAction = \"ask-before-pull\";\n\nexport function normalizeOnSwitch(value: unknown): OnSwitchAction {\n if (value === \"ask-before-pull\" || value === \"pull-after-switch\" || value === \"switch-only\") {\n return value;\n }\n throw new Error(\n 'Invalid pi-sync settings: onSwitch must be \"ask-before-pull\", \"pull-after-switch\", or \"switch-only\".',\n );\n}\n\nexport function validateSettingsDocument(value: Record<string, unknown>): PiSyncSettingsV3 {\n if (value.version !== 3 && value.version !== 4 && value.version !== 5) {\n throw new Error(\n `Unsupported pi-sync settings: version 3 or 4 or 5 is required. Keep the existing ${path.basename(localConfigPath())} for recovery and use a compatible pi-sync client or review a separate supported setup; do not downgrade portable field policies. pi-sync will not migrate or overwrite unsupported settings.`,\n );\n }\n rejectLegacyFields(\n value,\n [\n \"profiles\",\n \"targets\",\n \"activeTarget\",\n \"targetSwitchAction\",\n \"endpoint\",\n \"bucket\",\n \"region\",\n \"accessKeyId\",\n \"secretAccessKey\",\n \"sessionToken\",\n \"profile\",\n \"prefix\",\n \"autoSync\",\n \"syncFiles\",\n \"syncSessions\",\n \"extraFiles\",\n ],\n \"top level\",\n );\n normalizeOnSwitch(value.onSwitch);\n if (value.skipSecretScan !== undefined && typeof value.skipSecretScan !== \"boolean\") {\n throw new Error(\"Invalid pi-sync settings: skipSecretScan must be boolean.\");\n }\n if (value.showStatus !== undefined && typeof value.showStatus !== \"boolean\") {\n throw new Error(\"Invalid pi-sync settings: showStatus must be boolean.\");\n }\n const storageConnections = requireNamedObjectMap(\n value.storageConnections,\n \"storageConnections\",\n \"storage connection\",\n );\n const syncSetups = requireNamedObjectMap(value.syncSetups, \"syncSetups\", \"sync setup\");\n for (const name of Object.keys(storageConnections)) {\n validateStorageConnection(\n name,\n requireOwnObject(storageConnections, name, \"storage connection\") as Record<string, unknown>,\n );\n }\n for (const name of Object.keys(syncSetups)) {\n validateSyncSetup(\n name,\n requireOwnObject(syncSetups, name, \"sync setup\") as Record<string, unknown>,\n storageConnections,\n );\n }\n const names = Object.keys(syncSetups);\n const activeSyncSetup = optionalCanonicalReference(value.activeSyncSetup, \"activeSyncSetup\");\n if (names.length === 0) {\n if (activeSyncSetup !== undefined) {\n throw new Error(\"Invalid pi-sync settings: empty syncSetups cannot have activeSyncSetup.\");\n }\n } else if (!activeSyncSetup || !Object.hasOwn(syncSetups, activeSyncSetup)) {\n throw new Error(\"Invalid pi-sync settings: activeSyncSetup must reference an existing own-property sync setup.\");\n }\n for (const setup of Object.values(syncSetups)) {\n const policy = (setup as SyncSetupSettings).sync;\n if (policy.localFields !== undefined && value.version !== 4 && value.version !== 5)\n throw new Error(\"localFields requires explicit settings version 4; older binaries must refuse portable state.\");\n }\n for (const setup of Object.values(syncSetups)) {\n const policy = (setup as SyncSetupSettings).sync;\n if ((policy.mergeContent || policy.partialSync) && value.version !== 5)\n throw new Error(\"Content/partial sync requires explicit settings version 5.\");\n }\n validateUniqueRemoteSyncSetups(syncSetups, storageConnections);\n return value as PiSyncSettingsV3;\n}\n\nfunction validateStorageConnection(name: string, value: Record<string, unknown>) {\n rejectLegacyFields(\n value,\n [\"kind\", \"accessKeyId\", \"secretAccessKey\", \"sessionToken\", \"username\", \"password\"],\n `storage connection \u201C${name}\u201D`,\n );\n const type = requiredString(value.type, `storage connection \u201C${name}\u201D type`);\n if (type !== \"s3\" && type !== \"git\" && type !== \"webdav\") {\n throw new Error(`Invalid pi-sync settings: storage connection \u201C${name}\u201D has unsupported type.`);\n }\n const known = [\"endpoint\", \"region\", \"remote\", \"url\", \"credentials\"];\n const allowed =\n type === \"s3\"\n ? new Set([\"endpoint\", \"region\", \"credentials\"])\n : type === \"git\"\n ? new Set([\"remote\"])\n : new Set([\"url\", \"credentials\"]);\n if (known.some((field) => Object.hasOwn(value, field) && !allowed.has(field))) {\n throw new Error(\n `Invalid pi-sync settings: ${type.toUpperCase()} storage connection \u201C${name}\u201D mixes backend fields.`,\n );\n }\n if (type === \"git\") {\n if (!normalizeGitRemote(requiredString(value.remote, `Git remote for \u201C${name}\u201D`))) {\n throw new Error(`Invalid pi-sync settings: Git remote for \u201C${name}\u201D is required.`);\n }\n return;\n }\n const credentials = requireRecord(value.credentials, `credentials for storage connection \u201C${name}\u201D`);\n if (type === \"webdav\") {\n normalizeWebDavUrl(requiredString(value.url, `WebDAV URL for \u201C${name}\u201D`));\n const username = requiredString(credentials.username, `WebDAV username for \u201C${name}\u201D`);\n const password = requiredSecret(credentials.password, `WebDAV password for \u201C${name}\u201D`);\n if ([\"accessKeyId\", \"secretAccessKey\", \"sessionToken\"].some((field) => Object.hasOwn(credentials, field))) {\n throw new Error(`Invalid pi-sync settings: WebDAV credentials for \u201C${name}\u201D mix fields.`);\n }\n validateWebDavCredentials(username, password);\n return;\n }\n normalizeS3Endpoint(requiredString(value.endpoint, `S3 endpoint for \u201C${name}\u201D`));\n requiredString(value.region, `S3 region for \u201C${name}\u201D`);\n requiredString(credentials.accessKeyId, `S3 access key id for \u201C${name}\u201D`);\n requiredSecret(credentials.secretAccessKey, `S3 secret access key for \u201C${name}\u201D`);\n optionalString(credentials.sessionToken, `S3 session token for \u201C${name}\u201D`);\n if ([\"username\", \"password\"].some((field) => Object.hasOwn(credentials, field))) {\n throw new Error(`Invalid pi-sync settings: S3 credentials for \u201C${name}\u201D mix fields.`);\n }\n}\n\nfunction validateSyncSetup(name: string, value: Record<string, unknown>, connections: Record<string, unknown>) {\n rejectLegacyFields(\n value,\n [\n \"profile\",\n \"bucket\",\n \"branch\",\n \"path\",\n \"prefix\",\n \"directory\",\n \"namespace\",\n \"autoSync\",\n \"syncFiles\",\n \"syncSessions\",\n \"extraFiles\",\n ],\n `sync setup \u201C${name}\u201D`,\n );\n const storage = requireRecord(value.storage, `storage for sync setup \u201C${name}\u201D`);\n const sync = requireRecord(value.sync, `sync policy for sync setup \u201C${name}\u201D`);\n rejectLegacyFields(storage, [\"profile\", \"prefix\", \"directory\", \"namespace\"], `storage for sync setup \u201C${name}\u201D`);\n rejectLegacyFields(\n sync,\n [\"autoSync\", \"syncFiles\", \"syncSessions\", \"extraFiles\"],\n `sync policy for sync setup \u201C${name}\u201D`,\n );\n const connectionName = requiredCanonicalReference(\n storage.connection,\n `storage connection reference for sync setup \u201C${name}\u201D`,\n );\n validateConfigName(connectionName, \"storage connection reference\");\n const connection = ownObject<Record<string, unknown>>(connections, connectionName);\n if (!connection) {\n throw new Error(\n `Invalid pi-sync settings: sync setup \u201C${name}\u201D references missing storage connection \u201C${connectionName}\u201D.`,\n );\n }\n const type = connection.type;\n normalizeStoragePath(requiredString(storage.path, `storage path for sync setup \u201C${name}\u201D`));\n if (type === \"s3\") {\n normalizeS3Bucket(requiredString(storage.bucket, `S3 bucket for sync setup \u201C${name}\u201D`));\n if (Object.hasOwn(storage, \"branch\")) mixedSetupError(\"S3\", name);\n } else if (type === \"git\") {\n normalizeGitBranch(requiredString(storage.branch, `Git branch for sync setup \u201C${name}\u201D`));\n if (Object.hasOwn(storage, \"bucket\")) mixedSetupError(\"Git\", name);\n } else if (type === \"webdav\") {\n if (Object.hasOwn(storage, \"bucket\") || Object.hasOwn(storage, \"branch\")) {\n mixedSetupError(\"WebDAV\", name);\n }\n }\n if (!Object.hasOwn(sync, \"include\")) {\n throw new Error(`Invalid pi-sync settings: sync setup \u201C${name}\u201D is missing sync.include.`);\n }\n const include = normalizeSyncInclude(sync.include);\n const localFields = normalizeLocalFields(sync.localFields);\n if (localFields.length > 0 && !include.includes(\"settings.json\"))\n throw new Error(\"Nonempty localFields requires settings.json in sync.include.\");\n for (const key of [\"mergeContent\", \"partialSync\"])\n if (sync[key] !== undefined && typeof sync[key] !== \"boolean\") throw new Error(`${key} must be boolean.`);\n if (sync.mergeSettings !== undefined && typeof sync.mergeSettings !== \"boolean\")\n throw new Error(\"mergeSettings must be boolean.\");\n if (sync.automaticTransfer !== undefined && typeof sync.automaticTransfer !== \"boolean\") {\n throw new Error(`Invalid pi-sync settings: sync setup \u201C${name}\u201D sync.automaticTransfer must be boolean.`);\n }\n if (typeof sync.automatic !== \"boolean\") {\n throw new Error(`Invalid pi-sync settings: sync setup \u201C${name}\u201D sync.automatic must be boolean.`);\n }\n}\n\nfunction mixedSetupError(type: string, name: string): never {\n throw new Error(`Invalid pi-sync settings: ${type} sync setup \u201C${name}\u201D mixes backend fields.`);\n}\n\nexport function validateUniqueRemoteSyncSetups(setups: Record<string, unknown>, connections: Record<string, unknown>) {\n const identities = new Map<string, string>();\n for (const name of Object.keys(setups)) {\n const setup = requireOwnObject(setups, name, \"sync setup\") as unknown as SyncSetupSettings;\n const connection = requireOwnObject(\n connections,\n setup.storage.connection,\n \"storage connection\",\n ) as unknown as StorageConnectionSettings;\n const identity = effectiveSyncSetupRemoteIdentity(setup, connection);\n const existing = identities.get(identity);\n if (existing) {\n throw new Error(\n `Invalid pi-sync settings: sync setups \u201C${existing}\u201D and \u201C${name}\u201D use the same normalized remote location.`,\n );\n }\n identities.set(identity, name);\n }\n}\n\nexport function effectiveSyncSetupRemoteIdentity(setup: SyncSetupSettings, connection: StorageConnectionSettings) {\n const storagePath = normalizeStoragePath(setup.storage.path);\n if (connection.type === \"git\") {\n return JSON.stringify([\n \"git\",\n normalizeGitRemoteIdentity(connection.remote),\n normalizeGitBranch(setup.storage.branch),\n normalizeGitDirectory(storagePath),\n ]);\n }\n if (connection.type === \"webdav\") {\n return JSON.stringify([\n \"webdav\",\n normalizeWebDavIdentityUrl(connection.url),\n connection.credentials.username.trim(),\n normalizeWebDavPath(storagePath),\n ]);\n }\n return JSON.stringify([\n \"s3\",\n normalizeEndpointIdentity(connection.endpoint),\n normalizeS3Bucket(setup.storage.bucket),\n storagePath,\n ]);\n}\n\nfunction rejectLegacyFields(value: Record<string, unknown>, fields: readonly string[], context: string) {\n const field = fields.find((candidate) => Object.hasOwn(value, candidate));\n if (field) {\n throw new Error(`Invalid pi-sync settings: ${context} contains unsupported version 1/2 field \u201C${field}\u201D.`);\n }\n}\n\nfunction requireNamedObjectMap(value: unknown, field: string, itemLabel: string) {\n const result = requireRecord(value, field);\n for (const name of Object.keys(result)) validateConfigName(name, itemLabel);\n return result;\n}\n\nfunction requireOwnObject(value: Record<string, unknown>, key: string, label: string) {\n const item = ownObject(value, key);\n if (!item) throw new Error(`Invalid pi-sync settings: ${label} \u201C${key}\u201D must be an object.`);\n return item;\n}\n\nexport function ownObject<T extends object>(value: Record<string, unknown>, key: string): T | undefined {\n if (!Object.hasOwn(value, key)) return undefined;\n const item = value[key];\n return item && typeof item === \"object\" && !Array.isArray(item) ? (item as T) : undefined;\n}\n\nfunction requireRecord(value: unknown, field: string): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`Invalid pi-sync settings: ${field} must be an object.`);\n }\n return value as Record<string, unknown>;\n}\n\nexport function validateConfigName(value: string, field: string) {\n if (\n !value.trim() ||\n value !== value.trim() ||\n value.length > 100 ||\n value === \"__proto__\" ||\n value === \"prototype\" ||\n value === \"constructor\" ||\n hasControlCharacter(value)\n ) {\n throw new Error(`Invalid pi-sync settings: invalid ${field} name.`);\n }\n}\n\nexport function requiredString(value: unknown, field: string) {\n if (typeof value !== \"string\" || !value.trim() || hasControlCharacter(value)) {\n throw new Error(`Invalid pi-sync settings: ${field} must be a non-empty string.`);\n }\n return value.trim();\n}\n\nfunction requiredCanonicalReference(value: unknown, field: string) {\n const normalized = requiredString(value, field);\n if (value !== normalized) {\n throw new Error(`Invalid pi-sync settings: ${field} must not have surrounding whitespace.`);\n }\n return normalized;\n}\n\nfunction optionalCanonicalReference(value: unknown, field: string) {\n const normalized = optionalString(value, field);\n if (normalized !== undefined && value !== normalized) {\n throw new Error(`Invalid pi-sync settings: ${field} must not have surrounding whitespace.`);\n }\n return normalized;\n}\n\nfunction requiredSecret(value: unknown, field: string) {\n if (typeof value !== \"string\" || !value || hasControlCharacter(value)) {\n throw new Error(`Invalid pi-sync settings: ${field} must be configured.`);\n }\n return value;\n}\n\nexport function optionalString(value: unknown, field: string) {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\" || hasControlCharacter(value)) {\n throw new Error(`Invalid pi-sync settings: ${field} must be a string.`);\n }\n return value.trim() || undefined;\n}\n\nexport function normalizeStoragePath(value: string) {\n if (value.trim() === \".\" || value.trim() === \"./\") return \"./\";\n const normalized = value.trim().replace(/^\\/+|\\/+$/gu, \"\");\n if (\n !normalized ||\n normalized.length > 1024 ||\n normalized.startsWith(\"-\") ||\n normalized.includes(\"\\\\\") ||\n hasControlCharacter(normalized) ||\n normalized.split(\"/\").some((segment) => !segment || segment === \".\" || segment === \"..\")\n ) {\n throw new Error(\"Invalid pi-sync settings: storage.path must be a safe relative path.\");\n }\n return normalized;\n}\n\nexport function normalizeS3Endpoint(value: string) {\n let url: URL;\n try {\n url = new URL(value.trim());\n } catch {\n throw new Error(\"Invalid pi-sync S3 endpoint.\");\n }\n const loopback = url.hostname === \"127.0.0.1\" || url.hostname === \"localhost\" || url.hostname === \"[::1]\";\n if (\n (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && loopback)) ||\n url.username ||\n url.password ||\n url.search ||\n url.hash\n ) {\n throw new Error(\"Invalid pi-sync S3 endpoint: HTTPS is required except for loopback.\");\n }\n url.pathname = url.pathname.replace(/\\/+$/gu, \"\");\n return url.toString().replace(/\\/$/u, \"\");\n}\n\nexport function normalizeS3Bucket(value: string | undefined) {\n const bucket = requiredString(value, \"S3 bucket\");\n if (bucket.includes(\"/\") || bucket.includes(\"\\\\\") || bucket.startsWith(\"-\")) {\n throw new Error(\"Invalid pi-sync S3 bucket.\");\n }\n return bucket;\n}\n\nfunction hasControlCharacter(value: string) {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: Stored settings cannot contain controls.\n return /[\\u0000-\\u001f\\u007f-\\u009f]/u.test(value);\n}\n\nexport function isCloudflareR2Endpoint(endpoint: string | undefined) {\n const value = endpoint?.trim();\n if (!value) return false;\n try {\n const hostname = new URL(value).hostname.toLowerCase();\n return hostname === \"r2.cloudflarestorage.com\" || hostname.endsWith(\".r2.cloudflarestorage.com\");\n } catch {\n return false;\n }\n}\n\nexport function ownRecord(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;\n}\n", "import { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { localConfigPath, readMigratingLocalConfigDocument, updateLocalConfigDocument } from \"./config-file.js\";\nimport type { OnSwitchAction, PiSyncSettingsV3 } from \"./settings-types.js\";\nimport { DEFAULT_ON_SWITCH, validateSettingsDocument } from \"./settings-validation.js\";\n\nlet configUpdateQueue: Promise<void> = Promise.resolve();\n\nexport async function requireSettings() {\n const settings = await readLocalConfigObject();\n if (!settings) {\n throw new Error(`Missing pi-sync settings. Open /sync and choose Set up sync, or create ${localConfigPath()}.`);\n }\n return settings;\n}\n\nexport async function configuredSyncSetupNames() {\n const settings = await readLocalConfigObject();\n return settings ? Object.keys(settings.syncSetups).sort((left, right) => left.localeCompare(right)) : [];\n}\n\nexport async function loadOnSwitch(): Promise<OnSwitchAction> {\n return (await requireSettings()).onSwitch;\n}\n\nexport function localConfigTemplate(): PiSyncSettingsV3 {\n return {\n version: 3,\n onSwitch: DEFAULT_ON_SWITCH,\n skipSecretScan: false,\n showStatus: true,\n storageConnections: {},\n syncSetups: {},\n };\n}\n\nexport async function readLocalConfigDocument() {\n const document = await readMigratingLocalConfigDocument((settings) => {\n validateSettingsDocument(settings);\n });\n if (document) validateSettingsDocument(document.parsed);\n return document;\n}\n\nexport async function readLocalConfigObject(): Promise<PiSyncSettingsV3 | undefined> {\n return (await readLocalConfigDocument())?.parsed as PiSyncSettingsV3 | undefined;\n}\n\nexport function updateLocalConfig(update: (current: PiSyncSettingsV3) => PiSyncSettingsV3, signal?: AbortSignal) {\n const operation = configUpdateQueue.then(() => {\n signal?.throwIfAborted();\n return performLocalConfigUpdate(update, signal);\n });\n configUpdateQueue = operation.then(\n () => undefined,\n () => undefined,\n );\n return operation;\n}\n\nasync function performLocalConfigUpdate(update: (current: PiSyncSettingsV3) => PiSyncSettingsV3, signal?: AbortSignal) {\n return updateLocalConfigDocument(localConfigTemplate(), update, validateSettingsDocument, signal);\n}\n\nexport async function writeLocalConfigObject(value: PiSyncSettingsV3 | Record<string, unknown>) {\n validateSettingsDocument(value as Record<string, unknown>);\n const configPath = localConfigPath();\n await fs.mkdir(path.dirname(configPath), { recursive: true });\n try {\n const stat = await fs.lstat(configPath);\n if (stat.isSymbolicLink()) throw new Error(`Refusing to overwrite symlinked pi-sync settings: ${configPath}`);\n if (!stat.isFile()) throw new Error(`pi-sync settings are not a regular file: ${configPath}`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n const temporaryPath = path.join(\n path.dirname(configPath),\n `.${path.basename(configPath)}.${process.pid}.${randomUUID()}.tmp`,\n );\n let handle: fs.FileHandle | undefined;\n try {\n handle = await fs.open(temporaryPath, \"wx\", 0o600);\n await handle.writeFile(`${JSON.stringify(value, null, \"\\t\")}\\n`, \"utf8\");\n if (process.platform !== \"win32\") await handle.chmod(0o600);\n await handle.sync();\n await handle.close();\n handle = undefined;\n await fs.rename(temporaryPath, configPath);\n } catch (error) {\n await handle?.close().catch(() => undefined);\n await fs.rm(temporaryPath, { force: true }).catch(() => undefined);\n throw error;\n }\n}\n", "import { createHash } from \"node:crypto\";\nimport { backendIdentityCoordinates } from \"../backends/backend-identity.js\";\nimport { normalizeGitBranch, normalizeGitDirectory, normalizeGitRemote } from \"../backends/git/git-config.js\";\nimport { normalizeWebDavPath, normalizeWebDavUrl } from \"../backends/webdav/webdav-config.js\";\nimport { normalizeLocalFields } from \"../sync/local-fields.js\";\nimport { normalizeSyncInclude } from \"../sync/sync-policy.js\";\nimport { localConfigPath, readActiveLocalConfigDocumentForRepair } from \"./config-file.js\";\nimport { requireSettings } from \"./settings-store.js\";\nimport type {\n AnySyncConfig,\n OnSwitchAction,\n PartialConfig,\n PiSyncSettingsV3,\n StorageConnectionSettings,\n SyncSetupSettings,\n} from \"./settings-types.js\";\nimport {\n DEFAULT_ON_SWITCH,\n isCloudflareR2Endpoint,\n normalizeS3Bucket,\n normalizeS3Endpoint,\n normalizeStoragePath,\n optionalString,\n ownObject,\n requiredString,\n validateConfigName,\n validateSettingsDocument,\n} from \"./settings-validation.js\";\n\nexport async function loadConfig(setupName?: string): Promise<AnySyncConfig> {\n return configFromSettings(await requireSettings(), setupName);\n}\n\n/** A background freshness read must never trigger legacy-file publication. */\nexport async function loadConfigForCheck(): Promise<AnySyncConfig> {\n const document = await readActiveLocalConfigDocumentForRepair();\n if (!document) throw new Error(`Missing pi-sync settings: ${localConfigPath()}`);\n validateSettingsDocument(document.parsed);\n return configFromSettings(document.parsed as PiSyncSettingsV3);\n}\n\nfunction configFromSettings(settings: PiSyncSettingsV3, setupName?: string): AnySyncConfig {\n const selectedName = setupName ?? settings.activeSyncSetup;\n if (!selectedName) throw new Error(\"No sync setups are configured.\");\n validateConfigName(selectedName, \"sync setup\");\n const setup = ownObject<SyncSetupSettings>(settings.syncSetups, selectedName);\n if (!setup) throw new Error(`Invalid pi-sync settings: sync setup \u201C${selectedName}\u201D was not found.`);\n const connectionName = setup.storage.connection;\n const connection = ownObject<StorageConnectionSettings>(settings.storageConnections, connectionName);\n if (!connection) {\n throw new Error(\n `Invalid pi-sync settings: sync setup \u201C${selectedName}\u201D references missing storage connection \u201C${connectionName}\u201D.`,\n );\n }\n return resolveSyncConfig(\n selectedName,\n setup,\n connectionName,\n connection,\n settings.onSwitch,\n settings.skipSecretScan ?? false,\n settings.showStatus ?? true,\n );\n}\n\nexport type SyncSetupStorageReview = Pick<\n PartialConfig,\n \"connectionName\" | \"storageKind\" | \"storagePath\" | \"bucket\" | \"branch\"\n>;\n\n/** A validated setup-facing projection used by manager and settings UI. */\nexport async function loadPartialConfig(setupName?: string): Promise<PartialConfig> {\n const config = await loadConfig(setupName);\n return {\n setupName: config.setupName,\n ...storageReviewFromConfig(config),\n include: [...config.include],\n automatic: config.automatic,\n automaticTransfer: config.automaticTransfer ?? false,\n onSwitch: config.onSwitch,\n showStatus: config.showStatus,\n };\n}\n\nexport function syncSetupStorageReview(\n setupName: string,\n setup: SyncSetupSettings,\n connectionName: string,\n connection: StorageConnectionSettings,\n): SyncSetupStorageReview {\n return storageReviewFromConfig(\n resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false, true),\n );\n}\n\nexport function syncSetupReviewIdentity(\n setupName: string,\n setup: SyncSetupSettings,\n connectionName: string,\n connection: StorageConnectionSettings,\n) {\n return syncConfigReviewIdentity(\n resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false, true),\n );\n}\n\nexport function syncConfigReviewIdentity(config: AnySyncConfig) {\n return JSON.stringify([\n config.setupName,\n config.connectionName,\n backendIdentityCoordinates(config),\n config.include,\n config.automatic,\n config.automaticTransfer ?? false,\n config.localFields,\n config.mergeSettings ?? false,\n config.mergeContent ?? false,\n config.partialSync ?? false,\n ]);\n}\n\nexport function syncConfigReviewFingerprint(config: AnySyncConfig) {\n return createHash(\"sha256\").update(syncConfigReviewIdentity(config)).digest(\"hex\");\n}\n\n/** Internal freshness token, including credentials; never display or persist it. */\nexport function syncCheckConfigFingerprint(config: AnySyncConfig) {\n return createHash(\"sha256\")\n .update(JSON.stringify({ ...config, showStatus: undefined }))\n .digest(\"hex\");\n}\n\nfunction storageReviewFromConfig(config: AnySyncConfig): SyncSetupStorageReview {\n return {\n connectionName: config.connectionName,\n storageKind: config.backend.type,\n storagePath: config.storagePath,\n ...(config.backend.type === \"s3\"\n ? { bucket: config.backend.destination.bucket }\n : config.backend.type === \"git\"\n ? { branch: config.backend.destination.branch }\n : {}),\n };\n}\n\nfunction resolveSyncConfig(\n setupName: string,\n setup: SyncSetupSettings,\n connectionName: string,\n connection: StorageConnectionSettings,\n onSwitch: OnSwitchAction,\n skipSecretScan: boolean,\n showStatus: boolean,\n): AnySyncConfig {\n const storagePath = normalizeStoragePath(setup.storage.path);\n const namespace = storagePath === \"./\" ? \"root\" : storagePath.slice(storagePath.lastIndexOf(\"/\") + 1);\n const include = normalizeSyncInclude(setup.sync.include);\n const common = {\n setupName,\n connectionName,\n storagePath,\n snapshotIdentity: namespace,\n include,\n automatic: setup.sync.automatic,\n automaticTransfer: setup.sync.automaticTransfer ?? false,\n localFields: setup.sync.localFields === undefined ? undefined : normalizeLocalFields(setup.sync.localFields),\n mergeSettings: setup.sync.mergeSettings ?? false,\n mergeContent: setup.sync.mergeContent ?? false,\n partialSync: setup.sync.partialSync ?? false,\n onSwitch,\n skipSecretScan,\n showStatus,\n };\n if (connection.type === \"git\") {\n return {\n ...common,\n backend: {\n type: \"git\",\n profile: { kind: \"git\", remote: normalizeGitRemote(connection.remote) as string },\n destination: {\n branch: normalizeGitBranch(setup.storage.branch),\n directory: normalizeGitDirectory(storagePath),\n namespace,\n },\n },\n };\n }\n if (connection.type === \"webdav\") {\n return {\n ...common,\n backend: {\n type: \"webdav\",\n profile: {\n kind: \"webdav\",\n url: normalizeWebDavUrl(connection.url) as string,\n username: connection.credentials.username,\n password: connection.credentials.password,\n },\n destination: { path: normalizeWebDavPath(storagePath), namespace },\n },\n };\n }\n return {\n ...common,\n backend: {\n type: \"s3\",\n profile: {\n kind: isCloudflareR2Endpoint(connection.endpoint) ? \"r2\" : \"s3-compatible\",\n endpoint: normalizeS3Endpoint(connection.endpoint),\n region: requiredString(connection.region, \"S3 region\"),\n accessKeyId: connection.credentials.accessKeyId,\n secretAccessKey: connection.credentials.secretAccessKey,\n sessionToken: optionalString(connection.credentials.sessionToken, \"S3 session token\"),\n },\n destination: {\n bucket: normalizeS3Bucket(setup.storage.bucket),\n prefix: storagePath,\n namespace,\n },\n },\n };\n}\n\nexport function sessionTokenWarnings(config: { endpoint?: string; sessionToken?: string }) {\n if (!isCloudflareR2Endpoint(config.endpoint) || !config.sessionToken) return [];\n return [\n \"session token: configured for Cloudflare R2; if R2 rejects X-Amz-Security-Token, pi-sync retries once without it. R2 static access keys usually do not need a session token.\",\n ];\n}\n\nexport function syncSessionsWarnings(config: { include: readonly string[] }) {\n if (!config.include.includes(\"sessions\")) return [];\n return [\n \"sessions: included; Pi session JSONL can contain prompts, tool output, file paths, images, and secrets. Sync sessions only to storage you trust.\",\n ];\n}\n\nexport function isEnabled(value: boolean | string | undefined, defaultValue: boolean) {\n if (value === undefined) return defaultValue;\n if (typeof value === \"boolean\") return value;\n return ![\"0\", \"false\", \"no\", \"off\"].includes(value.trim().toLowerCase());\n}\n\nexport function isExplicitlyEnabled(value: boolean | string | undefined) {\n if (typeof value === \"boolean\") return value;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(value?.trim().toLowerCase() ?? \"\");\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,mBAAmB;AAC5B,OAAO,cAAc;AAErB,IAAM,mBAAmB;AACzB,IAAM,0BAA0B;AAChC,IAAM,yBAAyB,oBAAI,IAAoB;AACvD,IAAM,wBAAwB,oBAAI,IAAY;AAC9C,IAAM,uBAAuB;AAC7B,IAAM,wBAAwB;AAC9B,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAI,oBAAiC;AACrC,IAAI,8BAAmD,YAAY;AACnE,IAAI,kCAAuD,YAAY;AACvE,IAAI,gCAAqD,YAAY;AACrE,IAAI,6BAAkD,YAAY;AA0E3D,SAAS,kBAAkB;AAChC,SAAO,KAAK,KAAK,YAAY,GAAG,gBAAgB;AAClD;AAEO,SAAS,wBAAwB;AACtC,SAAO,KAAK,KAAK,YAAY,GAAG,uBAAuB;AACzD;AAEA,eAAsB,wBAAwB;AAC5C,QAAM,gBAAgB,gBAAgB;AACtC,QAAM,aAAa,sBAAsB;AACzC,SAAO,gCAAgC,YAAY;AACjD,QAAI,MAAM,WAAW,aAAa,EAAG,QAAO;AAC5C,WAAQ,MAAM,WAAW,UAAU,IAAK,aAAa;AAAA,EACvD,CAAC;AACH;AAEO,SAAS,oCAAoC;AAClD,QAAM,aAAa,gBAAgB;AACnC,QAAM,SAAS,uBAAuB,IAAI,UAAU;AACpD,yBAAuB,OAAO,UAAU;AACxC,SAAO;AACT;AAEA,eAAsB,yCAAmF;AACvG,QAAM,gBAAgB,gBAAgB;AACtC,QAAM,aAAa,sBAAsB;AACzC,SAAO,gCAAgC,YAAY;AACjD,UAAM,WAAY,MAAM,WAAW,aAAa,IAAK,gBAAgB;AACrE,UAAM,WAAW,MAAM,2BAA2B,QAAQ;AAC1D,WAAO,WAAW,EAAE,MAAM,UAAU,GAAG,SAAS,IAAI;AAAA,EACtD,CAAC;AACH;AAEA,eAAsB,iCACpB,sBAC0C;AAC1C,SAAO,gCAAgC,YAAY;AACjD,UAAM,aAAa,MAAM,uBAAuB,oBAAoB;AACpE,UAAM,WAAW,MAAM,2BAA2B,UAAU;AAC5D,WAAO,WAAW,EAAE,MAAM,YAAY,GAAG,SAAS,IAAI;AAAA,EACxD,CAAC;AACH;AAEA,eAAe,gCAAmC,MAAoC;AACpF,MAAK,MAAM,WAAW,gBAAgB,CAAC,KAAO,MAAM,WAAW,sBAAsB,CAAC,GAAI;AACxF,WAAO,wBAAwB,IAAI;AAAA,EACrC;AACA,QAAM,gCAAgC;AACtC,MAAI,MAAM,WAAW,uBAAuB,CAAC,EAAG,QAAO,wBAAwB,IAAI;AACnF,SAAO,KAAK;AACd;AAEO,SAAS,0BACd,cACA,QACA,UACA,QACY;AACZ,SAAO,wBAAwB,YAAY;AACzC,YAAQ,eAAe;AACvB,UAAM,aAAa,MAAM,uBAAuB,QAAQ;AACxD,UAAM,WAAW,MAAM,2BAA2B,UAAU;AAC5D,UAAM,WAAW,WAAW,EAAE,MAAM,YAAY,GAAG,SAAS,IAAI;AAChE,UAAM,UAAU,WAAY,gBAAgB,SAAS,MAAM,IAAU,gBAAgB,YAAY;AACjG,UAAM,OAAO,OAAO,OAAO;AAC3B,aAAS,IAAI;AACb,YAAQ,eAAe;AACvB,QAAI,YAAY,KAAK,UAAU,SAAS,MAAM,MAAM,KAAK,UAAU,IAAI,EAAG,QAAO;AACjF,QAAI,SAAU,OAAM,mCAAmC,UAAU,IAAI;AAAA,QAChE,OAAM,gCAAgC,gBAAgB,GAAG,iBAAiB,IAAI,CAAC;AACpF,WAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,0BAA0B,OAAgC;AACxE,SAAO,wBAAwB,YAAY;AACzC,UAAM,QAAQ,iBAAiB,KAAK;AACpC,QAAI;AACF,YAAM,gCAAgC,gBAAgB,GAAG,KAAK;AAAA,IAChE,SAAS,OAAO;AACd,UAAK,MAAgC,SAAS,UAAU;AACtD,cAAM,IAAI,MAAM,wEAAwE;AAAA,MAC1F;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AACH;AAMA,eAAe,mCAAmC,UAA+B,OAAgC;AAC/G,QAAM,YAAY,iBAAiB,KAAK;AACxC,QAAM,gBAAgB,gBAAgB;AACtC,MAAI,SAAS,SAAS,eAAe;AACnC,QAAI,CAAE,MAAM,2BAA2B,QAAQ,EAAI,OAAM,qBAAqB;AAC9E,QAAIA;AACJ,QAAI;AACF,MAAAA,aAAY,MAAM,gCAAgC,eAAe,SAAS;AAAA,IAC5E,SAAS,OAAO;AACd,UAAK,MAAgC,SAAS,UAAU;AACtD,cAAM,IAAI,MAAM,0EAA0E;AAAA,MAC5F;AACA,YAAM;AAAA,IACR;AACA,QAAI,CAAE,MAAM,2BAA2B,QAAQ,GAAI;AACjD,YAAM,2CAA2C,eAAeA,YAAW,SAAS;AACpF,YAAM,qBAAqB;AAAA,IAC7B;AACA;AAAA,EACF;AAEA,QAAM,iBAAiB,MAAM,6BAA6B,QAAQ;AAClE,MAAI;AACJ,MAAI;AACF,gBAAY,MAAM,gCAAgC,eAAe,SAAS;AAAA,EAC5E,SAAS,OAAO;AACd,UAAM,yBAAyB,eAAe,cAAc;AAC5D,QAAK,MAAgC,SAAS,UAAU;AACtD,YAAM,IAAI,MAAM,qEAAqE;AAAA,IACvF;AACA,UAAM;AAAA,EACR;AACA,MAAI;AACF,UAAM,8BAA8B;AACpC,QAAI,CAAE,MAAM,6BAA6B,gBAAgB,SAAS,UAAU,SAAS,KAAK,GAAI;AAC5F,YAAM,qBAAqB;AAAA,IAC7B;AACA,QAAI,QAAQ,aAAa,QAAS,OAAM,GAAG,MAAM,gBAAgB,GAAK;AAAA,EACxE,SAAS,OAAO;AACd,UAAM,2CAA2C,eAAe,WAAW,SAAS;AACpF,UAAM,yBAAyB,eAAe,cAAc;AAC5D,UAAM;AAAA,EACR;AACA,QAAM,GAAG,GAAG,cAAc,EAAE,MAAM,MAAM,MAAS;AACjD,QAAM,oBAAoB,aAAa,EAAE,MAAM,MAAM,MAAS;AAChE;AAEA,eAAe,uBAAuB,sBAAmE;AACvG,QAAM,gBAAgB,gBAAgB;AACtC,QAAM,aAAa,sBAAsB;AACzC,MAAI,MAAM,WAAW,aAAa,GAAG;AACnC,UAAM,eAAe,MAAM,6BAA6B,UAAU;AAClE,QAAI,iBAAiB,aAAa,CAAC,sBAAsB,IAAI,aAAa,GAAG;AAC3E,4BAAsB,IAAI,aAAa;AACvC;AAAA,QACE;AAAA,QACA,iBAAiB,YACb,GAAG,uBAAuB,yCAAyC,gBAAgB,6BAA6B,uBAAuB,qCACvI,GAAG,uBAAuB,yCAAyC,gBAAgB;AAAA,MACzF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,MAAM,2BAA2B,UAAU;AAC1D,MAAI,CAAC,OAAQ,QAAO;AACpB,uBAAqB,OAAO,MAAM;AAElC,MAAI;AACJ,MAAI;AACF,wBAAoB,MAAM,gCAAgC,eAAe,OAAO,KAAK;AAAA,EACvF,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,UAAU;AACtD;AAAA,QACE;AAAA,QACA,GAAG,uBAAuB,yCAAyC,gBAAgB;AAAA,MACrF;AACA,aAAO;AAAA,IACT;AACA;AAAA,MACE;AAAA,MACA,qBAAqB,uBAAuB,OAAO,gBAAgB;AAAA,IACrE;AACA,WAAO;AAAA,EACT;AAEA,MAAI,CAAE,MAAM,2BAA2B,YAAY,MAAM,GAAI;AAC3D,UAAM,UAAU,MAAM,2CAA2C,eAAe,mBAAmB,OAAO,KAAK;AAC/G;AAAA,MACE;AAAA,MACA,UACI,GAAG,uBAAuB,wCAAwC,gBAAgB,0EAClF,GAAG,uBAAuB,kCAAkC,gBAAgB;AAAA,IAClF;AACA,WAAO,UAAU,aAAa;AAAA,EAChC;AAEA,wBAAsB,IAAI,aAAa;AACvC;AAAA,IACE;AAAA,IACA,kCAAkC,uBAAuB,OAAO,gBAAgB;AAAA,EAClF;AACA,SAAO;AACT;AAEA,eAAe,6BAA6B,UAAkB;AAC5D,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,GAAG,MAAM,QAAQ;AAAA,EACpC,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,WAAO;AAAA,EACT;AACA,MAAI,SAAS,eAAe,KAAK,CAAC,SAAS,OAAO,EAAG,QAAO;AAE5D,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,GAAG,KAAK,UAAU,GAAG;AACpC,UAAM,aAAa,MAAM,OAAO,KAAK;AACrC,QAAI,WAAW,QAAQ,SAAS,OAAO,WAAW,QAAQ,SAAS,KAAK;AACtE,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,aAAa,YAAY,WAAW,OAAO,SAAW,KAAO;AACvE,YAAM,OAAO,MAAM,GAAK;AAAA,IAC1B;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,UAAM,QAAQ,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,EAC7C;AACF;AAEA,eAAe,2BAA2B,UAAuD;AAC/F,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,GAAG,MAAM,QAAQ;AAAA,EACpC,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,UAAM;AAAA,EACR;AACA,MAAI,SAAS,eAAe,GAAG;AAC7B,UAAM,IAAI,MAAM,8CAA8C,QAAQ,EAAE;AAAA,EAC1E;AACA,MAAI,CAAC,SAAS,OAAO,EAAG,OAAM,IAAI,MAAM,yCAAyC,QAAQ,EAAE;AAE3F,QAAM,SAAS,MAAM,GAAG,KAAK,UAAU,GAAG;AAC1C,MAAI;AACF,UAAM,aAAa,MAAM,OAAO,KAAK;AACrC,QAAI,WAAW,QAAQ,SAAS,OAAO,WAAW,QAAQ,SAAS,KAAK;AACtE,YAAM,IAAI,MAAM,yCAAyC,QAAQ,EAAE;AAAA,IACrE;AACA,QAAI,QAAQ,aAAa,YAAY,WAAW,OAAO,SAAW,KAAO;AACvE,YAAM,OAAO,MAAM,GAAK;AAAA,IAC1B;AACA,UAAM,QAAQ,MAAM,OAAO,SAAS;AACpC,WAAO;AAAA,MACL;AAAA,MACA,UAAU,EAAE,KAAK,WAAW,KAAK,KAAK,WAAW,IAAI;AAAA,MACrD,QAAQ,kBAAkB,OAAO,QAAQ;AAAA,IAC3C;AAAA,EACF,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;AAEA,SAAS,iBAAiB,OAAgC;AACxD,SAAO,OAAO,KAAK,GAAG,KAAK,UAAU,OAAO,MAAM,GAAI,CAAC;AAAA,GAAM,MAAM;AACrE;AAEA,SAAS,kBAAkB,OAAe,UAAkB;AAC1D,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,MAAM,SAAS,MAAM,CAAC;AAAA,EAC5C,QAAQ;AACN,UAAM,IAAI,YAAY,mCAAmC,QAAQ,EAAE;AAAA,EACrE;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAClE,UAAM,IAAI,MAAM,8CAA8C,QAAQ,EAAE;AAAA,EAC1E;AACA,SAAO;AACT;AAEA,eAAe,gCAAgC,UAAkB,OAAe;AAC9E,QAAM,GAAG,MAAM,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,QAAM,gBAAgB,KAAK;AAAA,IACzB,KAAK,QAAQ,QAAQ;AAAA,IACrB,IAAI,KAAK,SAAS,QAAQ,CAAC,IAAI,QAAQ,GAAG,IAAI,WAAW,CAAC;AAAA,EAC5D;AACA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,GAAG,KAAK,eAAe,MAAM,GAAK;AACjD,UAAM,OAAO,UAAU,KAAK;AAC5B,QAAI,QAAQ,aAAa,QAAS,OAAM,OAAO,MAAM,GAAK;AAC1D,UAAM,OAAO,KAAK;AAClB,UAAM,OAAO,MAAM;AACnB,aAAS;AACT,UAAM,kBAAkB,eAAe,QAAQ;AAC/C,UAAM,YAAY,MAAM,GAAG,MAAM,QAAQ;AACzC,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,UAAI,UAAU,eAAe,KAAK,CAAC,UAAU,OAAO,GAAG;AACrD,cAAM,IAAI,MAAM,sDAAsD,QAAQ,EAAE;AAAA,MAClF;AACA,wBAAkB,MAAM,GAAG,KAAK,UAAU,IAAI;AAC9C,YAAM,YAAY,MAAM,gBAAgB,KAAK;AAC7C,UAAI,UAAU,QAAQ,UAAU,OAAO,UAAU,QAAQ,UAAU,KAAK;AACtE,cAAM,IAAI,MAAM,qDAAqD,QAAQ,EAAE;AAAA,MACjF;AACA,UAAI,QAAQ,aAAa,QAAS,OAAM,gBAAgB,MAAM,GAAK;AACnE,YAAM,gBAAgB,KAAK;AAC3B,YAAM,oBAAoB,QAAQ;AAAA,IACpC,SAAS,OAAO;AACd,yBAAmB;AAAA,IACrB,UAAE;AACA,YAAM,iBAAiB,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,IACtD;AACA,QAAI,kBAAkB;AACpB,YAAM,2CAA2C,UAAU,EAAE,KAAK,UAAU,KAAK,KAAK,UAAU,IAAI,GAAG,KAAK;AAC5G,YAAM;AAAA,IACR;AACA,WAAO,EAAE,KAAK,UAAU,KAAK,KAAK,UAAU,IAAI;AAAA,EAClD,UAAE;AACA,UAAM,QAAQ,MAAM,EAAE,MAAM,MAAM,MAAS;AAC3C,UAAM,GAAG,GAAG,eAAe,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM,MAAS;AAAA,EACnE;AACF;AAEA,eAAe,2BAA2B,UAA+B;AACvE,SAAO,6BAA6B,SAAS,MAAM,SAAS,UAAU,SAAS,KAAK;AACtF;AAEA,eAAe,6BAA6B,UAA+B;AACzE,QAAM,iBAAiB,KAAK;AAAA,IAC1B,KAAK,QAAQ,SAAS,IAAI;AAAA,IAC1B,IAAI,KAAK,SAAS,SAAS,IAAI,CAAC,IAAI,WAAW,CAAC;AAAA,EAClD;AACA,MAAI;AACF,UAAM,GAAG,OAAO,SAAS,MAAM,cAAc;AAC7C,UAAM,oBAAoB,SAAS,IAAI;AAAA,EACzC,SAAS,OAAO;AACd,UAAM,yBAAyB,SAAS,MAAM,cAAc;AAC5D,UAAM;AAAA,EACR;AACA,MAAI,CAAE,MAAM,6BAA6B,gBAAgB,SAAS,UAAU,SAAS,KAAK,GAAI;AAC5F,UAAM,yBAAyB,SAAS,MAAM,cAAc;AAC5D,UAAM,qBAAqB;AAAA,EAC7B;AACA,MAAI,MAAM,WAAW,SAAS,IAAI,GAAG;AACnC,UAAM,yBAAyB,SAAS,MAAM,cAAc;AAC5D,UAAM,IAAI,MAAM,qEAAqE;AAAA,EACvF;AACA,SAAO;AACT;AAEA,SAAS,uBAAuB;AAC9B,SAAO,IAAI,MAAM,uEAAuE;AAC1F;AAEA,eAAe,2BAA2B,UAAkB,UAA0B;AACpF,SAAO,6BAA6B,UAAU,SAAS,UAAU,SAAS,KAAK;AACjF;AAMA,eAAe,2CACb,UACA,UACA,eACA;AACA,QAAM,iBAAiB,KAAK;AAAA,IAC1B,KAAK,QAAQ,QAAQ;AAAA,IACrB,IAAI,KAAK,SAAS,QAAQ,CAAC,IAAI,WAAW,CAAC;AAAA,EAC7C;AACA,MAAI;AACF,UAAM,GAAG,OAAO,UAAU,cAAc;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,2BAA2B;AACjC,MAAI;AACF,UAAM,oBAAoB,QAAQ;AAAA,EACpC,QAAQ;AACN,UAAM,yBAAyB,UAAU,cAAc;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MAAM,6BAA6B,gBAAgB,UAAU,aAAa;AAC1F,MAAI,CAAC,SAAS;AACZ,UAAM,yBAAyB,UAAU,cAAc;AACvD,WAAO;AAAA,EACT;AACA,MAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,UAAM,GAAG,GAAG,gBAAgB,EAAE,OAAO,KAAK,CAAC;AAC3C,UAAM,oBAAoB,QAAQ;AAClC,WAAO;AAAA,EACT;AACA,QAAM,GAAG,GAAG,cAAc;AAC1B,QAAM,oBAAoB,QAAQ;AAClC,SAAO;AACT;AAEA,eAAe,6BAA6B,UAAkB,UAAwB,eAAuB;AAC3G,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,MAAM,QAAQ;AACvC,QAAI,QAAQ,eAAe,EAAG,QAAO;AACrC,QAAI,QAAQ,QAAQ,SAAS,OAAO,QAAQ,QAAQ,SAAS,IAAK,QAAO;AACzE,YAAQ,MAAM,GAAG,SAAS,QAAQ,GAAG,OAAO,aAAa;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,yBAAyB,UAAkB,gBAAwB;AAChF,MAAI;AACF,UAAM,6BAA6B,gBAAgB,QAAQ;AAC3D,QAAI,QAAQ,aAAa,QAAS,OAAM,GAAG,MAAM,UAAU,GAAK;AAChE,UAAM,oBAAoB,QAAQ;AAClC;AAAA,EACF,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU;AAAA,EAC1D;AACA,MAAI;AACF,QAAI,QAAQ,aAAa,QAAS,OAAM,GAAG,MAAM,gBAAgB,GAAK;AACtE,UAAM,oBAAoB,QAAQ;AAAA,EACpC,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,yBAAyB;AAChC,SAAO,GAAG,gBAAgB,CAAC;AAC7B;AAEA,eAAsB,wBAA2B,KAAmC;AAClF,QAAM,aAAa,gBAAgB;AACnC,QAAM,GAAG,MAAM,KAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,MAAI;AACJ,QAAM,UAAU,MAAM,SAAS,KAAK,YAAY;AAAA,IAC9C,IAAI;AAAA,IACJ,cAAc,uBAAuB;AAAA,IACrC,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS,EAAE,SAAS,KAAK,QAAQ,KAAK,YAAY,IAAI,YAAY,IAAI;AAAA,IACtE,eAAe,CAAC,UAAU;AACxB,yBAAmB;AAAA,IACrB;AAAA,EACF,CAAC;AACD,MAAI;AACF,UAAM,SAAS,MAAM,IAAI;AACzB,QAAI,iBAAkB,OAAM;AAC5B,WAAO;AAAA,EACT,UAAE;AACA,UAAM,QAAQ;AAAA,EAChB;AACF;AAEA,eAAe,oBAAoB,UAAkB;AACnD,MAAI,QAAQ,aAAa,QAAS;AAClC,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,GAAG,KAAK,KAAK,QAAQ,QAAQ,GAAG,GAAG;AAClD,UAAM,OAAO,KAAK;AAAA,EACpB,UAAE;AACA,UAAM,QAAQ,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,EAC7C;AACF;AAEA,eAAe,8BAA8B,QAAgB,aAAqB;AAChF,QAAM,4BAA4B;AAClC,QAAM,6BAA6B,QAAQ,WAAW;AACxD;AAEA,eAAe,6BAA6B,QAAgB,aAAqB;AAC/E,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,OAAO,OAAO,IAAI,MAAM,2BAA2B,WAAW,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAAA,EAC7F;AACA,QAAM,GAAG,OAAO,QAAQ,WAAW;AACrC;AAEA,eAAe,WAAW,UAAkB;AAC1C,MAAI;AACF,UAAM,GAAG,MAAM,QAAQ;AACvB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU,QAAO;AAC/D,UAAM;AAAA,EACR;AACF;AAEA,SAAS,4BAA4B,YAAoB,QAAgB;AACvE,MAAI,CAAC,uBAAuB,IAAI,UAAU,EAAG,wBAAuB,IAAI,YAAY,MAAM;AAC5F;;;AC7lBA,OAAOC,WAAU;AAwBV,IAAM,oBAAoC;AAE1C,SAAS,kBAAkB,OAAgC;AAChE,MAAI,UAAU,qBAAqB,UAAU,uBAAuB,UAAU,eAAe;AAC3F,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAEO,SAAS,yBAAyB,OAAkD;AACzF,MAAI,MAAM,YAAY,KAAK,MAAM,YAAY,KAAK,MAAM,YAAY,GAAG;AACrE,UAAM,IAAI;AAAA,MACR,oFAAoFC,MAAK,SAAS,gBAAgB,CAAC,CAAC;AAAA,IACtH;AAAA,EACF;AACA;AAAA,IACE;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACA,oBAAkB,MAAM,QAAQ;AAChC,MAAI,MAAM,mBAAmB,UAAa,OAAO,MAAM,mBAAmB,WAAW;AACnF,UAAM,IAAI,MAAM,2DAA2D;AAAA,EAC7E;AACA,MAAI,MAAM,eAAe,UAAa,OAAO,MAAM,eAAe,WAAW;AAC3E,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AACA,QAAM,qBAAqB;AAAA,IACzB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF;AACA,QAAM,aAAa,sBAAsB,MAAM,YAAY,cAAc,YAAY;AACrF,aAAW,QAAQ,OAAO,KAAK,kBAAkB,GAAG;AAClD;AAAA,MACE;AAAA,MACA,iBAAiB,oBAAoB,MAAM,oBAAoB;AAAA,IACjE;AAAA,EACF;AACA,aAAW,QAAQ,OAAO,KAAK,UAAU,GAAG;AAC1C;AAAA,MACE;AAAA,MACA,iBAAiB,YAAY,MAAM,YAAY;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAQ,OAAO,KAAK,UAAU;AACpC,QAAM,kBAAkB,2BAA2B,MAAM,iBAAiB,iBAAiB;AAC3F,MAAI,MAAM,WAAW,GAAG;AACtB,QAAI,oBAAoB,QAAW;AACjC,YAAM,IAAI,MAAM,yEAAyE;AAAA,IAC3F;AAAA,EACF,WAAW,CAAC,mBAAmB,CAAC,OAAO,OAAO,YAAY,eAAe,GAAG;AAC1E,UAAM,IAAI,MAAM,+FAA+F;AAAA,EACjH;AACA,aAAW,SAAS,OAAO,OAAO,UAAU,GAAG;AAC7C,UAAM,SAAU,MAA4B;AAC5C,QAAI,OAAO,gBAAgB,UAAa,MAAM,YAAY,KAAK,MAAM,YAAY;AAC/E,YAAM,IAAI,MAAM,8FAA8F;AAAA,EAClH;AACA,aAAW,SAAS,OAAO,OAAO,UAAU,GAAG;AAC7C,UAAM,SAAU,MAA4B;AAC5C,SAAK,OAAO,gBAAgB,OAAO,gBAAgB,MAAM,YAAY;AACnE,YAAM,IAAI,MAAM,4DAA4D;AAAA,EAChF;AACA,iCAA+B,YAAY,kBAAkB;AAC7D,SAAO;AACT;AAEA,SAAS,0BAA0B,MAAc,OAAgC;AAC/E;AAAA,IACE;AAAA,IACA,CAAC,QAAQ,eAAe,mBAAmB,gBAAgB,YAAY,UAAU;AAAA,IACjF,4BAAuB,IAAI;AAAA,EAC7B;AACA,QAAM,OAAO,eAAe,MAAM,MAAM,4BAAuB,IAAI,aAAQ;AAC3E,MAAI,SAAS,QAAQ,SAAS,SAAS,SAAS,UAAU;AACxD,UAAM,IAAI,MAAM,sDAAiD,IAAI,8BAAyB;AAAA,EAChG;AACA,QAAM,QAAQ,CAAC,YAAY,UAAU,UAAU,OAAO,aAAa;AACnE,QAAM,UACJ,SAAS,OACL,oBAAI,IAAI,CAAC,YAAY,UAAU,aAAa,CAAC,IAC7C,SAAS,QACP,oBAAI,IAAI,CAAC,QAAQ,CAAC,IAClB,oBAAI,IAAI,CAAC,OAAO,aAAa,CAAC;AACtC,MAAI,MAAM,KAAK,CAAC,UAAU,OAAO,OAAO,OAAO,KAAK,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,GAAG;AAC7E,UAAM,IAAI;AAAA,MACR,6BAA6B,KAAK,YAAY,CAAC,6BAAwB,IAAI;AAAA,IAC7E;AAAA,EACF;AACA,MAAI,SAAS,OAAO;AAClB,QAAI,CAAC,mBAAmB,eAAe,MAAM,QAAQ,wBAAmB,IAAI,QAAG,CAAC,GAAG;AACjF,YAAM,IAAI,MAAM,kDAA6C,IAAI,qBAAgB;AAAA,IACnF;AACA;AAAA,EACF;AACA,QAAM,cAAc,cAAc,MAAM,aAAa,4CAAuC,IAAI,QAAG;AACnG,MAAI,SAAS,UAAU;AACrB,uBAAmB,eAAe,MAAM,KAAK,wBAAmB,IAAI,QAAG,CAAC;AACxE,UAAM,WAAW,eAAe,YAAY,UAAU,6BAAwB,IAAI,QAAG;AACrF,UAAM,WAAW,eAAe,YAAY,UAAU,6BAAwB,IAAI,QAAG;AACrF,QAAI,CAAC,eAAe,mBAAmB,cAAc,EAAE,KAAK,CAAC,UAAU,OAAO,OAAO,aAAa,KAAK,CAAC,GAAG;AACzG,YAAM,IAAI,MAAM,0DAAqD,IAAI,oBAAe;AAAA,IAC1F;AACA,8BAA0B,UAAU,QAAQ;AAC5C;AAAA,EACF;AACA,sBAAoB,eAAe,MAAM,UAAU,yBAAoB,IAAI,QAAG,CAAC;AAC/E,iBAAe,MAAM,QAAQ,uBAAkB,IAAI,QAAG;AACtD,iBAAe,YAAY,aAAa,8BAAyB,IAAI,QAAG;AACxE,iBAAe,YAAY,iBAAiB,kCAA6B,IAAI,QAAG;AAChF,iBAAe,YAAY,cAAc,8BAAyB,IAAI,QAAG;AACzE,MAAI,CAAC,YAAY,UAAU,EAAE,KAAK,CAAC,UAAU,OAAO,OAAO,aAAa,KAAK,CAAC,GAAG;AAC/E,UAAM,IAAI,MAAM,sDAAiD,IAAI,oBAAe;AAAA,EACtF;AACF;AAEA,SAAS,kBAAkB,MAAc,OAAgC,aAAsC;AAC7G;AAAA,IACE;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,oBAAe,IAAI;AAAA,EACrB;AACA,QAAM,UAAU,cAAc,MAAM,SAAS,gCAA2B,IAAI,QAAG;AAC/E,QAAM,OAAO,cAAc,MAAM,MAAM,oCAA+B,IAAI,QAAG;AAC7E,qBAAmB,SAAS,CAAC,WAAW,UAAU,aAAa,WAAW,GAAG,gCAA2B,IAAI,QAAG;AAC/G;AAAA,IACE;AAAA,IACA,CAAC,YAAY,aAAa,gBAAgB,YAAY;AAAA,IACtD,oCAA+B,IAAI;AAAA,EACrC;AACA,QAAM,iBAAiB;AAAA,IACrB,QAAQ;AAAA,IACR,qDAAgD,IAAI;AAAA,EACtD;AACA,qBAAmB,gBAAgB,8BAA8B;AACjE,QAAM,aAAa,UAAmC,aAAa,cAAc;AACjF,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR,8CAAyC,IAAI,sDAA4C,cAAc;AAAA,IACzG;AAAA,EACF;AACA,QAAM,OAAO,WAAW;AACxB,uBAAqB,eAAe,QAAQ,MAAM,qCAAgC,IAAI,QAAG,CAAC;AAC1F,MAAI,SAAS,MAAM;AACjB,sBAAkB,eAAe,QAAQ,QAAQ,kCAA6B,IAAI,QAAG,CAAC;AACtF,QAAI,OAAO,OAAO,SAAS,QAAQ,EAAG,iBAAgB,MAAM,IAAI;AAAA,EAClE,WAAW,SAAS,OAAO;AACzB,uBAAmB,eAAe,QAAQ,QAAQ,mCAA8B,IAAI,QAAG,CAAC;AACxF,QAAI,OAAO,OAAO,SAAS,QAAQ,EAAG,iBAAgB,OAAO,IAAI;AAAA,EACnE,WAAW,SAAS,UAAU;AAC5B,QAAI,OAAO,OAAO,SAAS,QAAQ,KAAK,OAAO,OAAO,SAAS,QAAQ,GAAG;AACxE,sBAAgB,UAAU,IAAI;AAAA,IAChC;AAAA,EACF;AACA,MAAI,CAAC,OAAO,OAAO,MAAM,SAAS,GAAG;AACnC,UAAM,IAAI,MAAM,8CAAyC,IAAI,iCAA4B;AAAA,EAC3F;AACA,QAAM,UAAU,qBAAqB,KAAK,OAAO;AACjD,QAAM,cAAc,qBAAqB,KAAK,WAAW;AACzD,MAAI,YAAY,SAAS,KAAK,CAAC,QAAQ,SAAS,eAAe;AAC7D,UAAM,IAAI,MAAM,8DAA8D;AAChF,aAAW,OAAO,CAAC,gBAAgB,aAAa;AAC9C,QAAI,KAAK,GAAG,MAAM,UAAa,OAAO,KAAK,GAAG,MAAM,UAAW,OAAM,IAAI,MAAM,GAAG,GAAG,mBAAmB;AAC1G,MAAI,KAAK,kBAAkB,UAAa,OAAO,KAAK,kBAAkB;AACpE,UAAM,IAAI,MAAM,gCAAgC;AAClD,MAAI,KAAK,sBAAsB,UAAa,OAAO,KAAK,sBAAsB,WAAW;AACvF,UAAM,IAAI,MAAM,8CAAyC,IAAI,gDAA2C;AAAA,EAC1G;AACA,MAAI,OAAO,KAAK,cAAc,WAAW;AACvC,UAAM,IAAI,MAAM,8CAAyC,IAAI,wCAAmC;AAAA,EAClG;AACF;AAEA,SAAS,gBAAgB,MAAc,MAAqB;AAC1D,QAAM,IAAI,MAAM,6BAA6B,IAAI,qBAAgB,IAAI,8BAAyB;AAChG;AAEO,SAAS,+BAA+B,QAAiC,aAAsC;AACpH,QAAM,aAAa,oBAAI,IAAoB;AAC3C,aAAW,QAAQ,OAAO,KAAK,MAAM,GAAG;AACtC,UAAM,QAAQ,iBAAiB,QAAQ,MAAM,YAAY;AACzD,UAAM,aAAa;AAAA,MACjB;AAAA,MACA,MAAM,QAAQ;AAAA,MACd;AAAA,IACF;AACA,UAAM,WAAW,iCAAiC,OAAO,UAAU;AACnE,UAAM,WAAW,WAAW,IAAI,QAAQ;AACxC,QAAI,UAAU;AACZ,YAAM,IAAI;AAAA,QACR,+CAA0C,QAAQ,oBAAU,IAAI;AAAA,MAClE;AAAA,IACF;AACA,eAAW,IAAI,UAAU,IAAI;AAAA,EAC/B;AACF;AAEO,SAAS,iCAAiC,OAA0B,YAAuC;AAChH,QAAM,cAAc,qBAAqB,MAAM,QAAQ,IAAI;AAC3D,MAAI,WAAW,SAAS,OAAO;AAC7B,WAAO,KAAK,UAAU;AAAA,MACpB;AAAA,MACA,2BAA2B,WAAW,MAAM;AAAA,MAC5C,mBAAmB,MAAM,QAAQ,MAAM;AAAA,MACvC,sBAAsB,WAAW;AAAA,IACnC,CAAC;AAAA,EACH;AACA,MAAI,WAAW,SAAS,UAAU;AAChC,WAAO,KAAK,UAAU;AAAA,MACpB;AAAA,MACA,2BAA2B,WAAW,GAAG;AAAA,MACzC,WAAW,YAAY,SAAS,KAAK;AAAA,MACrC,oBAAoB,WAAW;AAAA,IACjC,CAAC;AAAA,EACH;AACA,SAAO,KAAK,UAAU;AAAA,IACpB;AAAA,IACA,0BAA0B,WAAW,QAAQ;AAAA,IAC7C,kBAAkB,MAAM,QAAQ,MAAM;AAAA,IACtC;AAAA,EACF,CAAC;AACH;AAEA,SAAS,mBAAmB,OAAgC,QAA2B,SAAiB;AACtG,QAAM,QAAQ,OAAO,KAAK,CAAC,cAAc,OAAO,OAAO,OAAO,SAAS,CAAC;AACxE,MAAI,OAAO;AACT,UAAM,IAAI,MAAM,6BAA6B,OAAO,iDAA4C,KAAK,SAAI;AAAA,EAC3G;AACF;AAEA,SAAS,sBAAsB,OAAgB,OAAe,WAAmB;AAC/E,QAAM,SAAS,cAAc,OAAO,KAAK;AACzC,aAAW,QAAQ,OAAO,KAAK,MAAM,EAAG,oBAAmB,MAAM,SAAS;AAC1E,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAgC,KAAa,OAAe;AACpF,QAAM,OAAO,UAAU,OAAO,GAAG;AACjC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,6BAA6B,KAAK,UAAK,GAAG,2BAAsB;AAC3F,SAAO;AACT;AAEO,SAAS,UAA4B,OAAgC,KAA4B;AACtG,MAAI,CAAC,OAAO,OAAO,OAAO,GAAG,EAAG,QAAO;AACvC,QAAM,OAAO,MAAM,GAAG;AACtB,SAAO,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,IAAK,OAAa;AAClF;AAEA,SAAS,cAAc,OAAgB,OAAwC;AAC7E,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AAC/D,UAAM,IAAI,MAAM,6BAA6B,KAAK,qBAAqB;AAAA,EACzE;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB,OAAe,OAAe;AAC/D,MACE,CAAC,MAAM,KAAK,KACZ,UAAU,MAAM,KAAK,KACrB,MAAM,SAAS,OACf,UAAU,eACV,UAAU,eACV,UAAU,iBACV,oBAAoB,KAAK,GACzB;AACA,UAAM,IAAI,MAAM,qCAAqC,KAAK,QAAQ;AAAA,EACpE;AACF;AAEO,SAAS,eAAe,OAAgB,OAAe;AAC5D,MAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,KAAK,oBAAoB,KAAK,GAAG;AAC5E,UAAM,IAAI,MAAM,6BAA6B,KAAK,8BAA8B;AAAA,EAClF;AACA,SAAO,MAAM,KAAK;AACpB;AAEA,SAAS,2BAA2B,OAAgB,OAAe;AACjE,QAAM,aAAa,eAAe,OAAO,KAAK;AAC9C,MAAI,UAAU,YAAY;AACxB,UAAM,IAAI,MAAM,6BAA6B,KAAK,wCAAwC;AAAA,EAC5F;AACA,SAAO;AACT;AAEA,SAAS,2BAA2B,OAAgB,OAAe;AACjE,QAAM,aAAa,eAAe,OAAO,KAAK;AAC9C,MAAI,eAAe,UAAa,UAAU,YAAY;AACpD,UAAM,IAAI,MAAM,6BAA6B,KAAK,wCAAwC;AAAA,EAC5F;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAgB,OAAe;AACrD,MAAI,OAAO,UAAU,YAAY,CAAC,SAAS,oBAAoB,KAAK,GAAG;AACrE,UAAM,IAAI,MAAM,6BAA6B,KAAK,sBAAsB;AAAA,EAC1E;AACA,SAAO;AACT;AAEO,SAAS,eAAe,OAAgB,OAAe;AAC5D,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,YAAY,oBAAoB,KAAK,GAAG;AAC3D,UAAM,IAAI,MAAM,6BAA6B,KAAK,oBAAoB;AAAA,EACxE;AACA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEO,SAAS,qBAAqB,OAAe;AAClD,MAAI,MAAM,KAAK,MAAM,OAAO,MAAM,KAAK,MAAM,KAAM,QAAO;AAC1D,QAAM,aAAa,MAAM,KAAK,EAAE,QAAQ,eAAe,EAAE;AACzD,MACE,CAAC,cACD,WAAW,SAAS,QACpB,WAAW,WAAW,GAAG,KACzB,WAAW,SAAS,IAAI,KACxB,oBAAoB,UAAU,KAC9B,WAAW,MAAM,GAAG,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,YAAY,OAAO,YAAY,IAAI,GACvF;AACA,UAAM,IAAI,MAAM,sEAAsE;AAAA,EACxF;AACA,SAAO;AACT;AAEO,SAAS,oBAAoB,OAAe;AACjD,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,MAAM,KAAK,CAAC;AAAA,EAC5B,QAAQ;AACN,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AACA,QAAM,WAAW,IAAI,aAAa,eAAe,IAAI,aAAa,eAAe,IAAI,aAAa;AAClG,MACG,IAAI,aAAa,YAAY,EAAE,IAAI,aAAa,WAAW,aAC5D,IAAI,YACJ,IAAI,YACJ,IAAI,UACJ,IAAI,MACJ;AACA,UAAM,IAAI,MAAM,qEAAqE;AAAA,EACvF;AACA,MAAI,WAAW,IAAI,SAAS,QAAQ,UAAU,EAAE;AAChD,SAAO,IAAI,SAAS,EAAE,QAAQ,QAAQ,EAAE;AAC1C;AAEO,SAAS,kBAAkB,OAA2B;AAC3D,QAAM,SAAS,eAAe,OAAO,WAAW;AAChD,MAAI,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,IAAI,KAAK,OAAO,WAAW,GAAG,GAAG;AAC3E,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAe;AAE1C,SAAO,gCAAgC,KAAK,KAAK;AACnD;AAEO,SAAS,uBAAuB,UAA8B;AACnE,QAAM,QAAQ,UAAU,KAAK;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,UAAM,WAAW,IAAI,IAAI,KAAK,EAAE,SAAS,YAAY;AACrD,WAAO,aAAa,8BAA8B,SAAS,SAAS,2BAA2B;AAAA,EACjG,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,UAAU,OAAqD;AAC7E,SAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAK,QAAoC;AAC5G;;;ACpaA,IAAI,oBAAmC,QAAQ,QAAQ;AAEvD,eAAsB,kBAAkB;AACtC,QAAM,WAAW,MAAM,sBAAsB;AAC7C,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,0EAA0E,gBAAgB,CAAC,GAAG;AAAA,EAChH;AACA,SAAO;AACT;AAEA,eAAsB,2BAA2B;AAC/C,QAAM,WAAW,MAAM,sBAAsB;AAC7C,SAAO,WAAW,OAAO,KAAK,SAAS,UAAU,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC,IAAI,CAAC;AACzG;AAEA,eAAsB,eAAwC;AAC5D,UAAQ,MAAM,gBAAgB,GAAG;AACnC;AAEO,SAAS,sBAAwC;AACtD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,oBAAoB,CAAC;AAAA,IACrB,YAAY,CAAC;AAAA,EACf;AACF;AAEA,eAAsB,0BAA0B;AAC9C,QAAM,WAAW,MAAM,iCAAiC,CAAC,aAAa;AACpE,6BAAyB,QAAQ;AAAA,EACnC,CAAC;AACD,MAAI,SAAU,0BAAyB,SAAS,MAAM;AACtD,SAAO;AACT;AAEA,eAAsB,wBAA+D;AACnF,UAAQ,MAAM,wBAAwB,IAAI;AAC5C;AAEO,SAAS,kBAAkB,QAAyD,QAAsB;AAC/G,QAAM,YAAY,kBAAkB,KAAK,MAAM;AAC7C,YAAQ,eAAe;AACvB,WAAO,yBAAyB,QAAQ,MAAM;AAAA,EAChD,CAAC;AACD,sBAAoB,UAAU;AAAA,IAC5B,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACA,SAAO;AACT;AAEA,eAAe,yBAAyB,QAAyD,QAAsB;AACrH,SAAO,0BAA0B,oBAAoB,GAAG,QAAQ,0BAA0B,MAAM;AAClG;;;AC/DA,SAAS,kBAAkB;AA6B3B,eAAsB,WAAW,WAA4C;AAC3E,SAAO,mBAAmB,MAAM,gBAAgB,GAAG,SAAS;AAC9D;AAGA,eAAsB,qBAA6C;AACjE,QAAM,WAAW,MAAM,uCAAuC;AAC9D,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,6BAA6B,gBAAgB,CAAC,EAAE;AAC/E,2BAAyB,SAAS,MAAM;AACxC,SAAO,mBAAmB,SAAS,MAA0B;AAC/D;AAEA,SAAS,mBAAmB,UAA4B,WAAmC;AACzF,QAAM,eAAe,aAAa,SAAS;AAC3C,MAAI,CAAC,aAAc,OAAM,IAAI,MAAM,gCAAgC;AACnE,qBAAmB,cAAc,YAAY;AAC7C,QAAM,QAAQ,UAA6B,SAAS,YAAY,YAAY;AAC5E,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,8CAAyC,YAAY,uBAAkB;AACnG,QAAM,iBAAiB,MAAM,QAAQ;AACrC,QAAM,aAAa,UAAqC,SAAS,oBAAoB,cAAc;AACnG,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR,8CAAyC,YAAY,sDAA4C,cAAc;AAAA,IACjH;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,SAAS,kBAAkB;AAAA,IAC3B,SAAS,cAAc;AAAA,EACzB;AACF;AAQA,eAAsB,kBAAkB,WAA4C;AAClF,QAAM,SAAS,MAAM,WAAW,SAAS;AACzC,SAAO;AAAA,IACL,WAAW,OAAO;AAAA,IAClB,GAAG,wBAAwB,MAAM;AAAA,IACjC,SAAS,CAAC,GAAG,OAAO,OAAO;AAAA,IAC3B,WAAW,OAAO;AAAA,IAClB,mBAAmB,OAAO,qBAAqB;AAAA,IAC/C,UAAU,OAAO;AAAA,IACjB,YAAY,OAAO;AAAA,EACrB;AACF;AAEO,SAAS,uBACd,WACA,OACA,gBACA,YACwB;AACxB,SAAO;AAAA,IACL,kBAAkB,WAAW,OAAO,gBAAgB,YAAY,mBAAmB,OAAO,IAAI;AAAA,EAChG;AACF;AAEO,SAAS,wBACd,WACA,OACA,gBACA,YACA;AACA,SAAO;AAAA,IACL,kBAAkB,WAAW,OAAO,gBAAgB,YAAY,mBAAmB,OAAO,IAAI;AAAA,EAChG;AACF;AAEO,SAAS,yBAAyB,QAAuB;AAC9D,SAAO,KAAK,UAAU;AAAA,IACpB,OAAO;AAAA,IACP,OAAO;AAAA,IACP,2BAA2B,MAAM;AAAA,IACjC,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO,qBAAqB;AAAA,IAC5B,OAAO;AAAA,IACP,OAAO,iBAAiB;AAAA,IACxB,OAAO,gBAAgB;AAAA,IACvB,OAAO,eAAe;AAAA,EACxB,CAAC;AACH;AAEO,SAAS,4BAA4B,QAAuB;AACjE,SAAO,WAAW,QAAQ,EAAE,OAAO,yBAAyB,MAAM,CAAC,EAAE,OAAO,KAAK;AACnF;AAGO,SAAS,2BAA2B,QAAuB;AAChE,SAAO,WAAW,QAAQ,EACvB,OAAO,KAAK,UAAU,EAAE,GAAG,QAAQ,YAAY,OAAU,CAAC,CAAC,EAC3D,OAAO,KAAK;AACjB;AAEA,SAAS,wBAAwB,QAA+C;AAC9E,SAAO;AAAA,IACL,gBAAgB,OAAO;AAAA,IACvB,aAAa,OAAO,QAAQ;AAAA,IAC5B,aAAa,OAAO;AAAA,IACpB,GAAI,OAAO,QAAQ,SAAS,OACxB,EAAE,QAAQ,OAAO,QAAQ,YAAY,OAAO,IAC5C,OAAO,QAAQ,SAAS,QACtB,EAAE,QAAQ,OAAO,QAAQ,YAAY,OAAO,IAC5C,CAAC;AAAA,EACT;AACF;AAEA,SAAS,kBACP,WACA,OACA,gBACA,YACA,UACA,gBACA,YACe;AACf,QAAM,cAAc,qBAAqB,MAAM,QAAQ,IAAI;AAC3D,QAAM,YAAY,gBAAgB,OAAO,SAAS,YAAY,MAAM,YAAY,YAAY,GAAG,IAAI,CAAC;AACpG,QAAM,UAAU,qBAAqB,MAAM,KAAK,OAAO;AACvD,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,IAClB;AAAA,IACA,WAAW,MAAM,KAAK;AAAA,IACtB,mBAAmB,MAAM,KAAK,qBAAqB;AAAA,IACnD,aAAa,MAAM,KAAK,gBAAgB,SAAY,SAAY,qBAAqB,MAAM,KAAK,WAAW;AAAA,IAC3G,eAAe,MAAM,KAAK,iBAAiB;AAAA,IAC3C,cAAc,MAAM,KAAK,gBAAgB;AAAA,IACzC,aAAa,MAAM,KAAK,eAAe;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,WAAW,SAAS,OAAO;AAC7B,WAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS,EAAE,MAAM,OAAO,QAAQ,mBAAmB,WAAW,MAAM,EAAY;AAAA,QAChF,aAAa;AAAA,UACX,QAAQ,mBAAmB,MAAM,QAAQ,MAAM;AAAA,UAC/C,WAAW,sBAAsB,WAAW;AAAA,UAC5C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,WAAW,SAAS,UAAU;AAChC,WAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS;AAAA,UACP,MAAM;AAAA,UACN,KAAK,mBAAmB,WAAW,GAAG;AAAA,UACtC,UAAU,WAAW,YAAY;AAAA,UACjC,UAAU,WAAW,YAAY;AAAA,QACnC;AAAA,QACA,aAAa,EAAE,MAAM,oBAAoB,WAAW,GAAG,UAAU;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS;AAAA,MACP,MAAM;AAAA,MACN,SAAS;AAAA,QACP,MAAM,uBAAuB,WAAW,QAAQ,IAAI,OAAO;AAAA,QAC3D,UAAU,oBAAoB,WAAW,QAAQ;AAAA,QACjD,QAAQ,eAAe,WAAW,QAAQ,WAAW;AAAA,QACrD,aAAa,WAAW,YAAY;AAAA,QACpC,iBAAiB,WAAW,YAAY;AAAA,QACxC,cAAc,eAAe,WAAW,YAAY,cAAc,kBAAkB;AAAA,MACtF;AAAA,MACA,aAAa;AAAA,QACX,QAAQ,kBAAkB,MAAM,QAAQ,MAAM;AAAA,QAC9C,QAAQ;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,qBAAqB,QAAsD;AACzF,MAAI,CAAC,uBAAuB,OAAO,QAAQ,KAAK,CAAC,OAAO,aAAc,QAAO,CAAC;AAC9E,SAAO;AAAA,IACL;AAAA,EACF;AACF;AAEO,SAAS,qBAAqB,QAAwC;AAC3E,MAAI,CAAC,OAAO,QAAQ,SAAS,UAAU,EAAG,QAAO,CAAC;AAClD,SAAO;AAAA,IACL;AAAA,EACF;AACF;",
|
|
6
|
+
"names": ["installed", "path", "path"]
|
|
7
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
2
|
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
|
-
import {
|
|
4
|
-
backendIdentityCoordinates
|
|
5
|
-
} from "./chunk-2JZFHNLM.ts";
|
|
6
3
|
import {
|
|
7
4
|
stateDir
|
|
8
5
|
} from "./chunk-BVVP33U5.ts";
|
|
6
|
+
import {
|
|
7
|
+
backendIdentityCoordinates
|
|
8
|
+
} from "./chunk-2JZFHNLM.ts";
|
|
9
9
|
import {
|
|
10
10
|
readJsonIfExists,
|
|
11
11
|
writeJson
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HUZ5CR4W.ts";
|
|
13
13
|
|
|
14
14
|
// src/state/sync-state-store.ts
|
|
15
15
|
import { createHash } from "node:crypto";
|
|
@@ -26,8 +26,17 @@ async function readStateForConfig(config) {
|
|
|
26
26
|
lastFileHashes: {}
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
async function writeStateForConfig(config, state) {
|
|
29
|
+
async function writeStateForConfig(config, state, snapshot, validate = () => {
|
|
30
|
+
}) {
|
|
31
|
+
validate();
|
|
32
|
+
if (snapshot) {
|
|
33
|
+
const { stageMergeBaseline } = await import("./merge-baseline-store-EVQAAML6.ts");
|
|
34
|
+
validate();
|
|
35
|
+
await stageMergeBaseline(config, snapshot, state);
|
|
36
|
+
validate();
|
|
37
|
+
}
|
|
30
38
|
await writeJson(statePathForConfig(config), state);
|
|
39
|
+
validate();
|
|
31
40
|
}
|
|
32
41
|
function statePathForConfig(config) {
|
|
33
42
|
const identity = backendIdentityCoordinates(config);
|
|
@@ -45,7 +54,8 @@ export {
|
|
|
45
54
|
syncStateFingerprint,
|
|
46
55
|
readStateForConfig,
|
|
47
56
|
writeStateForConfig,
|
|
57
|
+
statePathForConfig,
|
|
48
58
|
lockPath,
|
|
49
59
|
ensureStateDir
|
|
50
60
|
};
|
|
51
|
-
//# sourceMappingURL=chunk-
|
|
61
|
+
//# sourceMappingURL=chunk-4QVPTIZB.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/state/sync-state-store.ts"],
|
|
4
|
-
"sourcesContent": ["import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { backendIdentityCoordinates } from \"../backends/backend-identity.js\";\nimport { safeName } from \"../paths.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { readJsonIfExists, writeJson } from \"./json-file.js\";\nimport { stateDir } from \"./state-directory.js\";\nimport type { SyncState } from \"./state-types.js\";\n\nconst STATE_VERSION = 2;\n\n/** Bounded observation token; do not retain the full baseline in passive UI state. */\nexport function syncStateFingerprint(state: SyncState) {\n return createHash(\"sha256\").update(JSON.stringify(state)).digest(\"hex\");\n}\n\nexport async function readState(profile: string): Promise<SyncState> {\n return (\n (await readJsonIfExists<SyncState>(statePath(profile))) ?? {\n version: STATE_VERSION,\n profile,\n lastFileHashes: {},\n }\n );\n}\n\nexport async function writeState(profile: string, state: SyncState) {\n await writeJson(statePath(profile), state);\n}\n\nexport async function readStateForConfig(config: AnySyncConfig): Promise<SyncState> {\n return (\n (await readJsonIfExists<SyncState>(statePathForConfig(config))) ?? {\n version: STATE_VERSION,\n profile: config.snapshotIdentity,\n lastFileHashes: {},\n }\n );\n}\n\nexport async function writeStateForConfig(config: AnySyncConfig
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,OAAO,QAAQ;AACf,OAAO,UAAU;AAQjB,IAAM,gBAAgB;AAGf,SAAS,qBAAqB,OAAkB;AACrD,SAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,UAAU,KAAK,CAAC,EAAE,OAAO,KAAK;AACxE;AAgBA,eAAsB,mBAAmB,QAA2C;AAClF,SACG,MAAM,iBAA4B,mBAAmB,MAAM,CAAC,KAAM;AAAA,IACjE,SAAS;AAAA,IACT,SAAS,OAAO;AAAA,IAChB,gBAAgB,CAAC;AAAA,EACnB;AAEJ;AAEA,eAAsB,
|
|
4
|
+
"sourcesContent": ["import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { backendIdentityCoordinates } from \"../backends/backend-identity.js\";\nimport { safeName } from \"../paths.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { readJsonIfExists, writeJson } from \"./json-file.js\";\nimport { stateDir } from \"./state-directory.js\";\nimport type { SyncState } from \"./state-types.js\";\n\nconst STATE_VERSION = 2;\n\n/** Bounded observation token; do not retain the full baseline in passive UI state. */\nexport function syncStateFingerprint(state: SyncState) {\n return createHash(\"sha256\").update(JSON.stringify(state)).digest(\"hex\");\n}\n\nexport async function readState(profile: string): Promise<SyncState> {\n return (\n (await readJsonIfExists<SyncState>(statePath(profile))) ?? {\n version: STATE_VERSION,\n profile,\n lastFileHashes: {},\n }\n );\n}\n\nexport async function writeState(profile: string, state: SyncState) {\n await writeJson(statePath(profile), state);\n}\n\nexport async function readStateForConfig(config: AnySyncConfig): Promise<SyncState> {\n return (\n (await readJsonIfExists<SyncState>(statePathForConfig(config))) ?? {\n version: STATE_VERSION,\n profile: config.snapshotIdentity,\n lastFileHashes: {},\n }\n );\n}\n\nexport async function writeStateForConfig(\n config: AnySyncConfig,\n state: SyncState,\n snapshot?: import(\"../snapshot/snapshot-types.js\").Snapshot,\n validate: () => void = () => {},\n) {\n validate();\n if (snapshot) {\n const { stageMergeBaseline } = await import(\"./merge-baseline-store.js\");\n validate();\n await stageMergeBaseline(config, snapshot, state);\n validate();\n }\n await writeJson(statePathForConfig(config), state);\n validate();\n}\n\nexport function statePathForConfig(config: AnySyncConfig) {\n const identity = backendIdentityCoordinates(config);\n const hash = createHash(\"sha256\").update(identity).digest(\"hex\").slice(0, 16);\n return path.join(stateDir(), \"setups\", `${config.backend.type}-${hash}.state.json`);\n}\n\nfunction statePath(profile: string) {\n return path.join(stateDir(), `${safeName(profile)}.state.json`);\n}\n\nexport function lockPath() {\n return path.join(stateDir(), \"lock\");\n}\n\nexport async function ensureStateDir() {\n await fs.mkdir(stateDir(), { recursive: true });\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,OAAO,QAAQ;AACf,OAAO,UAAU;AAQjB,IAAM,gBAAgB;AAGf,SAAS,qBAAqB,OAAkB;AACrD,SAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,UAAU,KAAK,CAAC,EAAE,OAAO,KAAK;AACxE;AAgBA,eAAsB,mBAAmB,QAA2C;AAClF,SACG,MAAM,iBAA4B,mBAAmB,MAAM,CAAC,KAAM;AAAA,IACjE,SAAS;AAAA,IACT,SAAS,OAAO;AAAA,IAChB,gBAAgB,CAAC;AAAA,EACnB;AAEJ;AAEA,eAAsB,oBACpB,QACA,OACA,UACA,WAAuB,MAAM;AAAC,GAC9B;AACA,WAAS;AACT,MAAI,UAAU;AACZ,UAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,oCAA2B;AACvE,aAAS;AACT,UAAM,mBAAmB,QAAQ,UAAU,KAAK;AAChD,aAAS;AAAA,EACX;AACA,QAAM,UAAU,mBAAmB,MAAM,GAAG,KAAK;AACjD,WAAS;AACX;AAEO,SAAS,mBAAmB,QAAuB;AACxD,QAAM,WAAW,2BAA2B,MAAM;AAClD,QAAM,OAAO,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC5E,SAAO,KAAK,KAAK,SAAS,GAAG,UAAU,GAAG,OAAO,QAAQ,IAAI,IAAI,IAAI,aAAa;AACpF;AAMO,SAAS,WAAW;AACzB,SAAO,KAAK,KAAK,SAAS,GAAG,MAAM;AACrC;AAEA,eAAsB,iBAAiB;AACrC,QAAM,GAAG,MAAM,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,17 +5,17 @@ import {
|
|
|
5
5
|
} from "./chunk-ECYCNZOE.ts";
|
|
6
6
|
import {
|
|
7
7
|
setSyncSetupCompletions
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2DI6PDZV.ts";
|
|
9
9
|
import {
|
|
10
10
|
safeTerminalText
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-AMJRUXFT.ts";
|
|
12
12
|
import {
|
|
13
13
|
addStorageConnection,
|
|
14
14
|
addSyncSetup,
|
|
15
15
|
saveNewV3Settings,
|
|
16
16
|
updateStorageConnection,
|
|
17
17
|
updateSyncSetup
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-CHSIAC4N.ts";
|
|
19
19
|
import {
|
|
20
20
|
configuredSyncSetupNames,
|
|
21
21
|
effectiveSyncSetupRemoteIdentity,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
readLocalConfigObject,
|
|
27
27
|
requiredString,
|
|
28
28
|
validateConfigName
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-3B4Z2SKV.ts";
|
|
30
30
|
import {
|
|
31
31
|
normalizeWebDavPath,
|
|
32
32
|
normalizeWebDavUrl,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
} from "./chunk-7D5SF7P3.ts";
|
|
46
46
|
import {
|
|
47
47
|
DEFAULT_SYNC_INCLUDE
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-QOFMXPM5.ts";
|
|
49
49
|
|
|
50
50
|
// src/commands/setup-completions.ts
|
|
51
51
|
async function refreshTargetCompletions() {
|
|
@@ -1200,4 +1200,4 @@ export {
|
|
|
1200
1200
|
showAddWebDavStorageProfile,
|
|
1201
1201
|
showEditWebDavStorageProfile
|
|
1202
1202
|
};
|
|
1203
|
-
//# sourceMappingURL=chunk-
|
|
1203
|
+
//# sourceMappingURL=chunk-4SBTAVFL.ts.map
|
|
@@ -5,15 +5,15 @@ import {
|
|
|
5
5
|
} from "./chunk-K3RIEL6J.ts";
|
|
6
6
|
import {
|
|
7
7
|
safeTerminalText
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AMJRUXFT.ts";
|
|
9
9
|
import {
|
|
10
10
|
syncCheckConfigFingerprint,
|
|
11
11
|
syncConfigReviewFingerprint
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-3B4Z2SKV.ts";
|
|
13
13
|
import {
|
|
14
14
|
compareSyncInclude,
|
|
15
15
|
sameSyncInclude
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-QOFMXPM5.ts";
|
|
17
17
|
|
|
18
18
|
// src/ui/sync-attention.ts
|
|
19
19
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
@@ -124,6 +124,8 @@ function observationSummary(observation) {
|
|
|
124
124
|
if (result.firstSync)
|
|
125
125
|
return result.head ? "No sync baseline; review remote and local content" : "Remote empty; no sync baseline";
|
|
126
126
|
if (!result.head) return "Remote snapshot missing; review needed";
|
|
127
|
+
if (observation.automaticTransfer && result.selectionState?.kind === "legacy")
|
|
128
|
+
return "Legacy remote has no authoritative content list; review /sync and choose an explicit direction to adopt policy";
|
|
127
129
|
if (result.localChanged && result.remoteChanged) return "Local and remote changed since last sync; review needed";
|
|
128
130
|
if (result.remoteChanged) return "Remote changed since last sync";
|
|
129
131
|
if (result.localChanged) return "Local content changed since last sync";
|
|
@@ -136,6 +138,7 @@ function classifyObservation(observation) {
|
|
|
136
138
|
if (result.selectionState?.kind === "different") return "review";
|
|
137
139
|
if (result.firstSync) return result.head ? "review" : "guidance";
|
|
138
140
|
if (!result.head || result.localChanged && result.remoteChanged) return "review";
|
|
141
|
+
if (observation.automaticTransfer && result.selectionState?.kind === "legacy") return "review";
|
|
139
142
|
if (result.localChanged || result.remoteChanged) return "status";
|
|
140
143
|
return "none";
|
|
141
144
|
}
|
|
@@ -161,4 +164,4 @@ export {
|
|
|
161
164
|
observationMatchesConfig,
|
|
162
165
|
observationSummary
|
|
163
166
|
};
|
|
164
|
-
//# sourceMappingURL=chunk-
|
|
167
|
+
//# sourceMappingURL=chunk-5RZPINUQ.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/ui/sync-attention.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { syncCheckConfigFingerprint, syncConfigReviewFingerprint } from \"../settings/config.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport type { StartupObservation } from \"../sync/sync-inspection.js\";\nimport { compareSyncInclude, type RemoteSelectionDecision, sameSyncInclude } from \"../sync/sync-policy.js\";\nimport { setSyncStatus } from \"./sync-status.js\";\nimport { safeTerminalText } from \"./terminal-text.js\";\n\nconst WIDGET_KEY = \"sync:attention\";\n\nexport type SyncAttentionOrigin = \"sync\" | \"pull\" | \"push\";\n\nexport interface SyncAttentionState {\n decision: RemoteSelectionDecision;\n origin: SyncAttentionOrigin;\n offered: boolean;\n}\n\nexport function syncAttentionMatchesConfig(attention: SyncAttentionState, config: AnySyncConfig) {\n return (\n attention.decision.setupName === config.setupName &&\n attention.decision.configIdentity === syncConfigReviewFingerprint(config) &&\n sameSyncInclude(attention.decision.localInclude, config.include)\n );\n}\n\nexport interface SyncAttentionController {\n observe(observation: StartupObservation): void;\n observation(): StartupObservation | undefined;\n clearObservation(): void;\n notifyObservation(ctx: ExtensionContext): void;\n set(decision: RemoteSelectionDecision, origin: SyncAttentionOrigin): void;\n current(): SyncAttentionState | undefined;\n markOffered(): boolean;\n clear(ctx: ExtensionContext): void;\n reset(ctx: ExtensionContext): void;\n publish(ctx: ExtensionContext, signal?: AbortSignal): Promise<void>;\n}\n\nexport function createSyncAttentionController(): SyncAttentionController {\n let state: SyncAttentionState | undefined;\n let observation: StartupObservation | undefined;\n let generation = 0;\n\n return {\n observe(value) {\n generation++;\n observation = value;\n },\n observation() {\n return observation;\n },\n clearObservation() {\n generation++;\n observation = undefined;\n },\n notifyObservation(ctx) {\n if (ctx.hasUI && observation && observationNeedsAttention(observation)) {\n ctx.ui.notify(observationLines(observation).join(\"\\n\"), \"warning\");\n }\n },\n set(decision, origin) {\n generation++;\n state = { decision, origin, offered: false };\n },\n current() {\n return state;\n },\n markOffered() {\n if (!state || state.offered) return false;\n state = { ...state, offered: true };\n return true;\n },\n clear(ctx) {\n generation++;\n state = undefined;\n clearAttentionPresentation(ctx);\n },\n reset(ctx) {\n generation++;\n state = undefined;\n observation = undefined;\n clearAttentionPresentation(ctx);\n },\n async publish(ctx, signal) {\n const currentGeneration = ++generation;\n if (signal?.aborted || !ctx.hasUI) return;\n const classification = observation ? classifyObservation(observation) : \"none\";\n if (!state && (classification === \"none\" || classification === \"guidance\")) {\n clearAttentionPresentation(ctx);\n return;\n }\n if (!state && classification === \"status\") {\n ctx.ui.setWidget(WIDGET_KEY, undefined);\n setSyncStatus(ctx, observation?.inspection.remoteChanged ? \"sync \u21E3\" : \"sync \u21E1\");\n return;\n }\n const presentation = state\n ? attentionPresentation(state.decision)\n : {\n status: \"sync \u21D5\",\n lines: observationLines(observation as StartupObservation),\n };\n if (ctx.mode !== \"tui\") {\n ctx.ui.setWidget(WIDGET_KEY, undefined);\n setSyncStatus(ctx, presentation.status);\n return;\n }\n // Keep Kit outside the eager startup graph; module loading owns no cancellable resources.\n const { EditorStatusWidget } = await import(\"./attention-widget.js\").catch((error) => {\n if (generation === currentGeneration && !signal?.aborted) clearAttentionPresentation(ctx);\n throw error;\n });\n if (generation !== currentGeneration || signal?.aborted) return;\n setSyncStatus(ctx, presentation.status);\n ctx.ui.setWidget(\n WIDGET_KEY,\n (_tui, theme) =>\n new EditorStatusWidget({\n theme,\n renderBody: (width) =>\n presentation.lines.map((line, index) =>\n width === 0 ? \"\" : truncateToWidth(theme.fg(index === 0 ? \"warning\" : \"muted\", line), width, \"\u2026\"),\n ),\n }),\n );\n },\n };\n}\n\nfunction attentionPresentation(decision: RemoteSelectionDecision) {\n const setupName = safeTerminalText(decision.setupName);\n const comparison = compareSyncInclude(decision.localInclude, decision.remoteInclude);\n const difference =\n comparison.remoteOnly.length === 0 && comparison.localOnly.length === 0\n ? \"Only list order differs\"\n : `Remote ${comparison.remoteOnly.length} \u00B7 Device ${comparison.localOnly.length}`;\n return {\n status: \"sync \u21D5\",\n lines: [`Pi Sync needs review \u00B7 ${setupName}`, difference, \"No changes \u00B7 Run /sync to review\"],\n };\n}\n\nexport function observationMatchesConfig(observation: StartupObservation, config: AnySyncConfig) {\n return observation.configIdentity === syncCheckConfigFingerprint(config);\n}\n\nexport function observationSummary(observation: StartupObservation) {\n const result = observation.inspection;\n if (result.emptyInclude) return \"No included content selected\";\n if (result.selectionState?.kind === \"different\") return \"Synced-content list differs; review needed\";\n if (result.firstSync)\n return result.head ? \"No sync baseline; review remote and local content\" : \"Remote empty; no sync baseline\";\n if (!result.head) return \"Remote snapshot missing; review needed\";\n if (result.localChanged && result.remoteChanged) return \"Local and remote changed since last sync; review needed\";\n if (result.remoteChanged) return \"Remote changed since last sync\";\n if (result.localChanged) return \"Local content changed since last sync\";\n if (result.selectionState?.kind === \"legacy\") return \"Legacy remote has no authoritative content list\";\n return \"No changes detected since last sync\";\n}\n\nexport type ObservationClassification = \"none\" | \"guidance\" | \"status\" | \"review\";\n\n/** Presentation only: never discard an observation or authorize a transfer here. */\nexport function classifyObservation(observation: StartupObservation): ObservationClassification {\n const result = observation.inspection;\n // Sync now exits before reading remote content when nothing is selected.\n if (result.emptyInclude) return \"guidance\";\n // Ordering is part of the existing transfer policy, not merely display order.\n if (result.selectionState?.kind === \"different\") return \"review\";\n if (result.firstSync) return result.head ? \"review\" : \"guidance\";\n if (!result.head || (result.localChanged && result.remoteChanged)) return \"review\";\n if (result.localChanged || result.remoteChanged) return \"status\";\n // Legacy metadata alone requires no scope confirmation in transfer operations.\n return \"none\";\n}\n\nexport function observationNeedsAttention(observation: StartupObservation) {\n return classifyObservation(observation) === \"review\";\n}\n\nfunction observationLines(observation: StartupObservation) {\n return [\n `Pi Sync check \u00B7 ${safeTerminalText(observation.setupName)}`,\n observationSummary(observation),\n \"No startup transfer \u00B7 Run /sync to review\",\n ];\n}\n\nfunction clearAttentionPresentation(ctx: ExtensionContext) {\n if (!ctx.hasUI) return;\n setSyncStatus(ctx, undefined);\n ctx.ui.setWidget(WIDGET_KEY, undefined);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { syncCheckConfigFingerprint, syncConfigReviewFingerprint } from \"../settings/config.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport type { StartupObservation } from \"../sync/sync-inspection.js\";\nimport { compareSyncInclude, type RemoteSelectionDecision, sameSyncInclude } from \"../sync/sync-policy.js\";\nimport { setSyncStatus } from \"./sync-status.js\";\nimport { safeTerminalText } from \"./terminal-text.js\";\n\nconst WIDGET_KEY = \"sync:attention\";\n\nexport type SyncAttentionOrigin = \"sync\" | \"pull\" | \"push\";\n\nexport interface SyncAttentionState {\n decision: RemoteSelectionDecision;\n origin: SyncAttentionOrigin;\n offered: boolean;\n}\n\nexport function syncAttentionMatchesConfig(attention: SyncAttentionState, config: AnySyncConfig) {\n return (\n attention.decision.setupName === config.setupName &&\n attention.decision.configIdentity === syncConfigReviewFingerprint(config) &&\n sameSyncInclude(attention.decision.localInclude, config.include)\n );\n}\n\nexport interface SyncAttentionController {\n observe(observation: StartupObservation): void;\n observation(): StartupObservation | undefined;\n clearObservation(): void;\n notifyObservation(ctx: ExtensionContext): void;\n set(decision: RemoteSelectionDecision, origin: SyncAttentionOrigin): void;\n current(): SyncAttentionState | undefined;\n markOffered(): boolean;\n clear(ctx: ExtensionContext): void;\n reset(ctx: ExtensionContext): void;\n publish(ctx: ExtensionContext, signal?: AbortSignal): Promise<void>;\n}\n\nexport function createSyncAttentionController(): SyncAttentionController {\n let state: SyncAttentionState | undefined;\n let observation: StartupObservation | undefined;\n let generation = 0;\n\n return {\n observe(value) {\n generation++;\n observation = value;\n },\n observation() {\n return observation;\n },\n clearObservation() {\n generation++;\n observation = undefined;\n },\n notifyObservation(ctx) {\n if (ctx.hasUI && observation && observationNeedsAttention(observation)) {\n ctx.ui.notify(observationLines(observation).join(\"\\n\"), \"warning\");\n }\n },\n set(decision, origin) {\n generation++;\n state = { decision, origin, offered: false };\n },\n current() {\n return state;\n },\n markOffered() {\n if (!state || state.offered) return false;\n state = { ...state, offered: true };\n return true;\n },\n clear(ctx) {\n generation++;\n state = undefined;\n clearAttentionPresentation(ctx);\n },\n reset(ctx) {\n generation++;\n state = undefined;\n observation = undefined;\n clearAttentionPresentation(ctx);\n },\n async publish(ctx, signal) {\n const currentGeneration = ++generation;\n if (signal?.aborted || !ctx.hasUI) return;\n const classification = observation ? classifyObservation(observation) : \"none\";\n if (!state && (classification === \"none\" || classification === \"guidance\")) {\n clearAttentionPresentation(ctx);\n return;\n }\n if (!state && classification === \"status\") {\n ctx.ui.setWidget(WIDGET_KEY, undefined);\n setSyncStatus(ctx, observation?.inspection.remoteChanged ? \"sync \u21E3\" : \"sync \u21E1\");\n return;\n }\n const presentation = state\n ? attentionPresentation(state.decision)\n : {\n status: \"sync \u21D5\",\n lines: observationLines(observation as StartupObservation),\n };\n if (ctx.mode !== \"tui\") {\n ctx.ui.setWidget(WIDGET_KEY, undefined);\n setSyncStatus(ctx, presentation.status);\n return;\n }\n // Keep Kit outside the eager startup graph; module loading owns no cancellable resources.\n const { EditorStatusWidget } = await import(\"./attention-widget.js\").catch((error) => {\n if (generation === currentGeneration && !signal?.aborted) clearAttentionPresentation(ctx);\n throw error;\n });\n if (generation !== currentGeneration || signal?.aborted) return;\n setSyncStatus(ctx, presentation.status);\n ctx.ui.setWidget(\n WIDGET_KEY,\n (_tui, theme) =>\n new EditorStatusWidget({\n theme,\n renderBody: (width) =>\n presentation.lines.map((line, index) =>\n width === 0 ? \"\" : truncateToWidth(theme.fg(index === 0 ? \"warning\" : \"muted\", line), width, \"\u2026\"),\n ),\n }),\n );\n },\n };\n}\n\nfunction attentionPresentation(decision: RemoteSelectionDecision) {\n const setupName = safeTerminalText(decision.setupName);\n const comparison = compareSyncInclude(decision.localInclude, decision.remoteInclude);\n const difference =\n comparison.remoteOnly.length === 0 && comparison.localOnly.length === 0\n ? \"Only list order differs\"\n : `Remote ${comparison.remoteOnly.length} \u00B7 Device ${comparison.localOnly.length}`;\n return {\n status: \"sync \u21D5\",\n lines: [`Pi Sync needs review \u00B7 ${setupName}`, difference, \"No changes \u00B7 Run /sync to review\"],\n };\n}\n\nexport function observationMatchesConfig(observation: StartupObservation, config: AnySyncConfig) {\n return observation.configIdentity === syncCheckConfigFingerprint(config);\n}\n\nexport function observationSummary(observation: StartupObservation) {\n const result = observation.inspection;\n if (result.emptyInclude) return \"No included content selected\";\n if (result.selectionState?.kind === \"different\") return \"Synced-content list differs; review needed\";\n if (result.firstSync)\n return result.head ? \"No sync baseline; review remote and local content\" : \"Remote empty; no sync baseline\";\n if (!result.head) return \"Remote snapshot missing; review needed\";\n if (observation.automaticTransfer && result.selectionState?.kind === \"legacy\")\n return \"Legacy remote has no authoritative content list; review /sync and choose an explicit direction to adopt policy\";\n if (result.localChanged && result.remoteChanged) return \"Local and remote changed since last sync; review needed\";\n if (result.remoteChanged) return \"Remote changed since last sync\";\n if (result.localChanged) return \"Local content changed since last sync\";\n if (result.selectionState?.kind === \"legacy\") return \"Legacy remote has no authoritative content list\";\n return \"No changes detected since last sync\";\n}\n\nexport type ObservationClassification = \"none\" | \"guidance\" | \"status\" | \"review\";\n\n/** Presentation only: never discard an observation or authorize a transfer here. */\nexport function classifyObservation(observation: StartupObservation): ObservationClassification {\n const result = observation.inspection;\n // Sync now exits before reading remote content when nothing is selected.\n if (result.emptyInclude) return \"guidance\";\n // Ordering is part of the existing transfer policy, not merely display order.\n if (result.selectionState?.kind === \"different\") return \"review\";\n if (result.firstSync) return result.head ? \"review\" : \"guidance\";\n if (!result.head || (result.localChanged && result.remoteChanged)) return \"review\";\n if (observation.automaticTransfer && result.selectionState?.kind === \"legacy\") return \"review\";\n if (result.localChanged || result.remoteChanged) return \"status\";\n // Legacy metadata alone requires no scope confirmation in transfer operations.\n return \"none\";\n}\n\nexport function observationNeedsAttention(observation: StartupObservation) {\n return classifyObservation(observation) === \"review\";\n}\n\nfunction observationLines(observation: StartupObservation) {\n return [\n `Pi Sync check \u00B7 ${safeTerminalText(observation.setupName)}`,\n observationSummary(observation),\n \"No startup transfer \u00B7 Run /sync to review\",\n ];\n}\n\nfunction clearAttentionPresentation(ctx: ExtensionContext) {\n if (!ctx.hasUI) return;\n setSyncStatus(ctx, undefined);\n ctx.ui.setWidget(WIDGET_KEY, undefined);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
isDeniedPath
|
|
5
|
+
} from "./chunk-HUZ5CR4W.ts";
|
|
6
|
+
|
|
7
|
+
// src/sync/file-merge-planner.ts
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
function planFileMerge(input) {
|
|
11
|
+
if (!input.selectionCompatible) return { kind: "review", reason: "selection-changed" };
|
|
12
|
+
if (input.baseline === void 0) return { kind: "review", reason: "missing-baseline" };
|
|
13
|
+
const local = indexFiles(input.local);
|
|
14
|
+
const remote = indexFiles(input.remote);
|
|
15
|
+
const baseline = input.baseline;
|
|
16
|
+
for (const [filePath, hash] of Object.entries(baseline)) {
|
|
17
|
+
validatePath(filePath);
|
|
18
|
+
if (!/^[a-f0-9]{64}$/u.test(hash)) throw new Error("Invalid merge baseline hash.");
|
|
19
|
+
}
|
|
20
|
+
const paths = [.../* @__PURE__ */ new Set([...Object.keys(baseline), ...local.keys(), ...remote.keys()])].sort();
|
|
21
|
+
const collisions = collidingPaths(paths);
|
|
22
|
+
const protectedKeys = new Set([...input.protectedPaths ?? []].map(mergePathIdentity));
|
|
23
|
+
const decisions = paths.map((filePath) => {
|
|
24
|
+
if (collisions.has(filePath)) return { kind: "conflict", path: filePath, reason: "path-collision" };
|
|
25
|
+
const left = local.get(filePath);
|
|
26
|
+
const right = remote.get(filePath);
|
|
27
|
+
const ancestor = Object.hasOwn(baseline, filePath) ? baseline[filePath] : void 0;
|
|
28
|
+
let source;
|
|
29
|
+
let file;
|
|
30
|
+
if (left?.sha256 === right?.sha256) {
|
|
31
|
+
source = "equal";
|
|
32
|
+
file = left;
|
|
33
|
+
} else if (right?.sha256 === ancestor) {
|
|
34
|
+
source = "local";
|
|
35
|
+
file = left;
|
|
36
|
+
} else if (left?.sha256 === ancestor) {
|
|
37
|
+
source = "remote";
|
|
38
|
+
file = right;
|
|
39
|
+
} else {
|
|
40
|
+
return { kind: "conflict", path: filePath, reason: "both-changed" };
|
|
41
|
+
}
|
|
42
|
+
if (source === "remote" && protectedKeys.has(mergePathIdentity(filePath))) {
|
|
43
|
+
return { kind: "conflict", path: filePath, reason: "protected-session" };
|
|
44
|
+
}
|
|
45
|
+
return { kind: "accepted", path: filePath, source, file };
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
kind: "planned",
|
|
49
|
+
decisions,
|
|
50
|
+
conflicts: decisions.filter((decision) => decision.kind === "conflict")
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function indexFiles(files) {
|
|
54
|
+
const result = /* @__PURE__ */ new Map();
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
validatePath(file.path);
|
|
57
|
+
if (result.has(file.path)) throw new Error("Duplicate merge path.");
|
|
58
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/u.test(file.contentBase64) || file.contentBase64.length % 4 !== 0 || Buffer.from(file.contentBase64, "base64").toString("base64") !== file.contentBase64 || createHash("sha256").update(Buffer.from(file.contentBase64, "base64")).digest("hex") !== file.sha256) {
|
|
59
|
+
throw new Error("Invalid merge file content or checksum.");
|
|
60
|
+
}
|
|
61
|
+
result.set(file.path, file);
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
function validatePath(filePath) {
|
|
66
|
+
if (!filePath || filePath === "." || filePath === ".." || filePath.startsWith("../") || path.posix.isAbsolute(filePath) || filePath.includes("\\") || process.platform === "win32" && // biome-ignore lint/suspicious/noControlCharactersInRegex: Native Windows filename restrictions.
|
|
67
|
+
(/[<>:"|?*\u0001-\u001f]/u.test(filePath) || filePath.split("/").some(
|
|
68
|
+
(segment) => /[ .]$/u.test(segment) || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(segment)
|
|
69
|
+
)) || path.posix.normalize(filePath) !== filePath || // biome-ignore lint/suspicious/noControlCharactersInRegex: Reject unsafe snapshot paths.
|
|
70
|
+
/[\u0000\ud800-\udfff]/u.test(filePath) || isDeniedPath(filePath) || filePath.startsWith("sessions/") && !filePath.endsWith(".jsonl")) {
|
|
71
|
+
throw new Error("Unsafe merge path.");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function mergePathIdentity(filePath) {
|
|
75
|
+
return Buffer.from(filePath, "utf8").toString("utf8").normalize("NFC").toLowerCase();
|
|
76
|
+
}
|
|
77
|
+
function collidingPaths(paths) {
|
|
78
|
+
const byLower = /* @__PURE__ */ new Map();
|
|
79
|
+
for (const filePath of paths) {
|
|
80
|
+
const lower = mergePathIdentity(filePath);
|
|
81
|
+
const variants = byLower.get(lower) ?? [];
|
|
82
|
+
variants.push(filePath);
|
|
83
|
+
byLower.set(lower, variants);
|
|
84
|
+
}
|
|
85
|
+
const result = /* @__PURE__ */ new Set();
|
|
86
|
+
for (const variants of byLower.values()) {
|
|
87
|
+
if (variants.length > 1) for (const variant of variants) result.add(variant);
|
|
88
|
+
}
|
|
89
|
+
for (const filePath of paths) {
|
|
90
|
+
const segments = mergePathIdentity(filePath).split("/");
|
|
91
|
+
for (let length = 1; length < segments.length; length += 1) {
|
|
92
|
+
const ancestors = byLower.get(segments.slice(0, length).join("/"));
|
|
93
|
+
if (ancestors) {
|
|
94
|
+
result.add(filePath);
|
|
95
|
+
for (const ancestor of ancestors) result.add(ancestor);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
planFileMerge,
|
|
104
|
+
mergePathIdentity,
|
|
105
|
+
collidingPaths
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=chunk-76NB4R7U.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/sync/file-merge-planner.ts"],
|
|
4
|
+
"sourcesContent": ["import { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport { isDeniedPath } from \"../paths.js\";\nimport type { SnapshotFile } from \"../snapshot/snapshot-types.js\";\n\n/** Absence and an empty file are different states. A missing ancestor is not absence. */\nexport type FileVersion = SnapshotFile | undefined;\nexport type FileMergeDecision =\n | { kind: \"accepted\"; path: string; source: \"equal\" | \"local\" | \"remote\" | \"merged\"; file: FileVersion }\n | { kind: \"conflict\"; path: string; reason: \"both-changed\" | \"protected-session\" | \"path-collision\" };\n\nexport type FileMergePlan =\n | { kind: \"review\"; reason: \"missing-baseline\" | \"selection-changed\" }\n | { kind: \"planned\"; decisions: FileMergeDecision[]; conflicts: Extract<FileMergeDecision, { kind: \"conflict\" }>[] };\n\nexport interface FileMergeInput {\n /** Undefined means unavailable; an empty map is an established empty baseline. */\n baseline: Readonly<Record<string, string>> | undefined;\n local: readonly SnapshotFile[];\n remote: readonly SnapshotFile[];\n selectionCompatible: boolean;\n protectedPaths?: ReadonlySet<string>;\n}\n\n/** Pure, deterministic decisions only. This does not authorize publication or mutation. */\nexport function planFileMerge(input: FileMergeInput): FileMergePlan {\n if (!input.selectionCompatible) return { kind: \"review\", reason: \"selection-changed\" };\n if (input.baseline === undefined) return { kind: \"review\", reason: \"missing-baseline\" };\n const local = indexFiles(input.local);\n const remote = indexFiles(input.remote);\n const baseline = input.baseline;\n for (const [filePath, hash] of Object.entries(baseline)) {\n validatePath(filePath);\n if (!/^[a-f0-9]{64}$/u.test(hash)) throw new Error(\"Invalid merge baseline hash.\");\n }\n const paths = [...new Set([...Object.keys(baseline), ...local.keys(), ...remote.keys()])].sort();\n const collisions = collidingPaths(paths);\n const protectedKeys = new Set([...(input.protectedPaths ?? [])].map(mergePathIdentity));\n const decisions: FileMergeDecision[] = paths.map((filePath) => {\n if (collisions.has(filePath)) return { kind: \"conflict\", path: filePath, reason: \"path-collision\" };\n const left = local.get(filePath);\n const right = remote.get(filePath);\n const ancestor = Object.hasOwn(baseline, filePath) ? baseline[filePath] : undefined;\n let source: \"equal\" | \"local\" | \"remote\";\n let file: FileVersion;\n if (left?.sha256 === right?.sha256) {\n source = \"equal\";\n file = left;\n } else if (right?.sha256 === ancestor) {\n source = \"local\";\n file = left;\n } else if (left?.sha256 === ancestor) {\n source = \"remote\";\n file = right;\n } else {\n return { kind: \"conflict\", path: filePath, reason: \"both-changed\" };\n }\n if (source === \"remote\" && protectedKeys.has(mergePathIdentity(filePath))) {\n return { kind: \"conflict\", path: filePath, reason: \"protected-session\" };\n }\n return { kind: \"accepted\", path: filePath, source, file };\n });\n return {\n kind: \"planned\",\n decisions,\n conflicts: decisions.filter((decision) => decision.kind === \"conflict\"),\n };\n}\n\nfunction indexFiles(files: readonly SnapshotFile[]) {\n const result = new Map<string, SnapshotFile>();\n for (const file of files) {\n validatePath(file.path);\n if (result.has(file.path)) throw new Error(\"Duplicate merge path.\");\n if (\n !/^[A-Za-z0-9+/]*={0,2}$/u.test(file.contentBase64) ||\n file.contentBase64.length % 4 !== 0 ||\n Buffer.from(file.contentBase64, \"base64\").toString(\"base64\") !== file.contentBase64 ||\n createHash(\"sha256\").update(Buffer.from(file.contentBase64, \"base64\")).digest(\"hex\") !== file.sha256\n ) {\n throw new Error(\"Invalid merge file content or checksum.\");\n }\n result.set(file.path, file);\n }\n return result;\n}\n\nfunction validatePath(filePath: string) {\n if (\n !filePath ||\n filePath === \".\" ||\n filePath === \"..\" ||\n filePath.startsWith(\"../\") ||\n path.posix.isAbsolute(filePath) ||\n filePath.includes(\"\\\\\") ||\n (process.platform === \"win32\" &&\n // biome-ignore lint/suspicious/noControlCharactersInRegex: Native Windows filename restrictions.\n (/[<>:\"|?*\\u0001-\\u001f]/u.test(filePath) ||\n filePath\n .split(\"/\")\n .some(\n (segment) => /[ .]$/u.test(segment) || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\\.|$)/iu.test(segment),\n ))) ||\n path.posix.normalize(filePath) !== filePath ||\n // biome-ignore lint/suspicious/noControlCharactersInRegex: Reject unsafe snapshot paths.\n /[\\u0000\\ud800-\\udfff]/u.test(filePath) ||\n isDeniedPath(filePath) ||\n (filePath.startsWith(\"sessions/\") && !filePath.endsWith(\".jsonl\"))\n ) {\n throw new Error(\"Unsafe merge path.\");\n }\n}\n\nexport function mergePathIdentity(filePath: string) {\n // Node path strings encode invalid UTF-16 as U+FFFD; context paths can still contain it.\n return Buffer.from(filePath, \"utf8\").toString(\"utf8\").normalize(\"NFC\").toLowerCase();\n}\n\n/** Conservative dependency groups: never accept a case or file/directory transition independently. */\nexport function collidingPaths(paths: readonly string[]) {\n const byLower = new Map<string, string[]>();\n for (const filePath of paths) {\n const lower = mergePathIdentity(filePath);\n const variants = byLower.get(lower) ?? [];\n variants.push(filePath);\n byLower.set(lower, variants);\n }\n const result = new Set<string>();\n for (const variants of byLower.values()) {\n if (variants.length > 1) for (const variant of variants) result.add(variant);\n }\n for (const filePath of paths) {\n const segments = mergePathIdentity(filePath).split(\"/\");\n for (let length = 1; length < segments.length; length += 1) {\n const ancestors = byLower.get(segments.slice(0, length).join(\"/\"));\n if (ancestors) {\n result.add(filePath);\n for (const ancestor of ancestors) result.add(ancestor);\n }\n }\n }\n return result;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
2
|
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
|
-
import {
|
|
4
|
-
createSyncBackend
|
|
5
|
-
} from "./chunk-HGJH56KR.ts";
|
|
6
3
|
import {
|
|
7
4
|
hasLocalChanges,
|
|
8
5
|
remoteChangedSinceState
|
|
9
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BDB4IDM3.ts";
|
|
7
|
+
import {
|
|
8
|
+
createSyncBackend
|
|
9
|
+
} from "./chunk-QHSQCK7L.ts";
|
|
10
10
|
import {
|
|
11
11
|
throwIfAborted
|
|
12
12
|
} from "./chunk-PWMWOJP4.ts";
|
|
13
13
|
import {
|
|
14
14
|
readStateForConfig,
|
|
15
15
|
syncStateFingerprint
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import {
|
|
18
|
-
createSnapshot
|
|
19
|
-
} from "./chunk-EEJPYTWE.ts";
|
|
16
|
+
} from "./chunk-4QVPTIZB.ts";
|
|
20
17
|
import {
|
|
18
|
+
createSnapshot,
|
|
21
19
|
inspectRemoteSelection
|
|
22
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-QOFMXPM5.ts";
|
|
23
21
|
|
|
24
22
|
// src/sync/sync-inspection.ts
|
|
25
23
|
async function inspectSync(config, options, signal, factory = createSyncBackend) {
|
|
@@ -49,4 +47,4 @@ async function inspectSync(config, options, signal, factory = createSyncBackend)
|
|
|
49
47
|
export {
|
|
50
48
|
inspectSync
|
|
51
49
|
};
|
|
52
|
-
//# sourceMappingURL=chunk-
|
|
50
|
+
//# sourceMappingURL=chunk-7D6RDCVC.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/sync/sync-inspection.ts"],
|
|
4
|
-
"sourcesContent": ["import { createSyncBackend, type SyncBackendFactory } from \"../backends/backend-factory.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { createSnapshot } from \"../snapshot/snapshot.js\";\nimport type { SnapshotOptions } from \"../snapshot/snapshot-types.js\";\nimport { readStateForConfig, syncStateFingerprint } from \"../state/sync-state-store.js\";\nimport { throwIfAborted } from \"./signals.js\";\nimport { inspectRemoteSelection } from \"./sync-policy.js\";\nimport { hasLocalChanges, remoteChangedSinceState } from \"./sync-state.js\";\n\n/** Advisory baseline-relative observation, never an authorization to transfer files. */\nexport async function inspectSync(\n config: AnySyncConfig,\n options: SnapshotOptions,\n signal?: AbortSignal,\n factory: SyncBackendFactory = createSyncBackend,\n) {\n throwIfAborted(signal);\n const backend = await factory(config);\n throwIfAborted(signal);\n const local = await createSnapshot(config.snapshotIdentity, { ...options, signal });\n throwIfAborted(signal);\n const state = await readStateForConfig(config);\n throwIfAborted(signal);\n const head = await backend.readHead(signal);\n throwIfAborted(signal);\n return {\n head,\n selectionState: head ? inspectRemoteSelection(config.include, { selection: head.selection, files: [] }) : undefined,\n localFiles: local.files.length,\n localChanged: hasLocalChanges(local, state, config),\n remoteChanged: remoteChangedSinceState(head, state, config, (left, right) => backend.sameRevision(left, right)),\n firstSync: !state.lastAppliedSnapshot,\n emptyInclude: config.include.length === 0,\n stateIdentity: syncStateFingerprint(state),\n destination: backend.destination,\n capability: backend.capability,\n };\n}\n\nexport type SyncInspection = Awaited<ReturnType<typeof inspectSync>>;\n\nexport interface StartupObservation {\n setupName: string;\n configIdentity: string;\n checkedAt: string;\n inspection: SyncInspection;\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { createSyncBackend, type SyncBackendFactory } from \"../backends/backend-factory.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { createSnapshot } from \"../snapshot/snapshot.js\";\nimport type { SnapshotOptions } from \"../snapshot/snapshot-types.js\";\nimport { readStateForConfig, syncStateFingerprint } from \"../state/sync-state-store.js\";\nimport { throwIfAborted } from \"./signals.js\";\nimport { inspectRemoteSelection } from \"./sync-policy.js\";\nimport { hasLocalChanges, remoteChangedSinceState } from \"./sync-state.js\";\n\n/** Advisory baseline-relative observation, never an authorization to transfer files. */\nexport async function inspectSync(\n config: AnySyncConfig,\n options: SnapshotOptions,\n signal?: AbortSignal,\n factory: SyncBackendFactory = createSyncBackend,\n) {\n throwIfAborted(signal);\n const backend = await factory(config);\n throwIfAborted(signal);\n const local = await createSnapshot(config.snapshotIdentity, { ...options, signal });\n throwIfAborted(signal);\n const state = await readStateForConfig(config);\n throwIfAborted(signal);\n const head = await backend.readHead(signal);\n throwIfAborted(signal);\n return {\n head,\n selectionState: head ? inspectRemoteSelection(config.include, { selection: head.selection, files: [] }) : undefined,\n localFiles: local.files.length,\n localChanged: hasLocalChanges(local, state, config),\n remoteChanged: remoteChangedSinceState(head, state, config, (left, right) => backend.sameRevision(left, right)),\n firstSync: !state.lastAppliedSnapshot,\n emptyInclude: config.include.length === 0,\n stateIdentity: syncStateFingerprint(state),\n destination: backend.destination,\n capability: backend.capability,\n };\n}\n\nexport type SyncInspection = Awaited<ReturnType<typeof inspectSync>>;\n\nexport interface StartupObservation {\n setupName: string;\n configIdentity: string;\n checkedAt: string;\n automaticTransfer?: boolean;\n inspection: SyncInspection;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;AAUA,eAAsB,YACpB,QACA,SACA,QACA,UAA8B,mBAC9B;AACA,iBAAe,MAAM;AACrB,QAAM,UAAU,MAAM,QAAQ,MAAM;AACpC,iBAAe,MAAM;AACrB,QAAM,QAAQ,MAAM,eAAe,OAAO,kBAAkB,EAAE,GAAG,SAAS,OAAO,CAAC;AAClF,iBAAe,MAAM;AACrB,QAAM,QAAQ,MAAM,mBAAmB,MAAM;AAC7C,iBAAe,MAAM;AACrB,QAAM,OAAO,MAAM,QAAQ,SAAS,MAAM;AAC1C,iBAAe,MAAM;AACrB,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,OAAO,uBAAuB,OAAO,SAAS,EAAE,WAAW,KAAK,WAAW,OAAO,CAAC,EAAE,CAAC,IAAI;AAAA,IAC1G,YAAY,MAAM,MAAM;AAAA,IACxB,cAAc,gBAAgB,OAAO,OAAO,MAAM;AAAA,IAClD,eAAe,wBAAwB,MAAM,OAAO,QAAQ,CAAC,MAAM,UAAU,QAAQ,aAAa,MAAM,KAAK,CAAC;AAAA,IAC9G,WAAW,CAAC,MAAM;AAAA,IAClB,cAAc,OAAO,QAAQ,WAAW;AAAA,IACxC,eAAe,qBAAqB,KAAK;AAAA,IACzC,aAAa,QAAQ;AAAA,IACrB,YAAY,QAAQ;AAAA,EACtB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|