@superblocksteam/vite-plugin-file-sync 2.0.162 → 2.0.163-next.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/dist/ai-service/agent/middleware.d.ts.map +1 -1
- package/dist/ai-service/agent/middleware.js +18 -7
- package/dist/ai-service/agent/middleware.js.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js +43 -29
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.js +25 -0
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.js.map +1 -1
- package/dist/ai-service/agent/subagents/coding/run-coding-subagent.d.ts.map +1 -1
- package/dist/ai-service/agent/subagents/coding/run-coding-subagent.js +15 -0
- package/dist/ai-service/agent/subagents/coding/run-coding-subagent.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/test-api.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/apis/test-api.js +4 -2
- package/dist/ai-service/agent/tools/apis/test-api.js.map +1 -1
- package/dist/ai-service/agent/tools/app-environment/switch-selected-environment.d.ts +18 -0
- package/dist/ai-service/agent/tools/app-environment/switch-selected-environment.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/app-environment/switch-selected-environment.js +61 -0
- package/dist/ai-service/agent/tools/app-environment/switch-selected-environment.js.map +1 -0
- package/dist/ai-service/agent/tools/build-capture-screenshot.js +1 -1
- package/dist/ai-service/agent/tools/build-install-packages.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-install-packages.js +21 -8
- package/dist/ai-service/agent/tools/build-install-packages.js.map +1 -1
- package/dist/ai-service/agent/tools/build-read-file.d.ts +87 -1
- package/dist/ai-service/agent/tools/build-read-file.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-read-file.js +453 -82
- package/dist/ai-service/agent/tools/build-read-file.js.map +1 -1
- package/dist/ai-service/agent/tools/build-write-file.d.ts +1 -0
- package/dist/ai-service/agent/tools/build-write-file.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-write-file.js +18 -2
- package/dist/ai-service/agent/tools/build-write-file.js.map +1 -1
- package/dist/ai-service/agent/tools/debug-cache.d.ts +11 -0
- package/dist/ai-service/agent/tools/debug-cache.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/debug-cache.js +62 -14
- package/dist/ai-service/agent/tools/debug-cache.js.map +1 -1
- package/dist/ai-service/agent/tools/index.d.ts +2 -1
- package/dist/ai-service/agent/tools/index.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/index.js +2 -1
- package/dist/ai-service/agent/tools/index.js.map +1 -1
- package/dist/ai-service/agent/tools/integrations/metadata.d.ts +1 -0
- package/dist/ai-service/agent/tools/integrations/metadata.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/integrations/metadata.js +7 -2
- package/dist/ai-service/agent/tools/integrations/metadata.js.map +1 -1
- package/dist/ai-service/agent/tools/read-file-metrics.d.ts +30 -0
- package/dist/ai-service/agent/tools/read-file-metrics.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/read-file-metrics.js +158 -0
- package/dist/ai-service/agent/tools/read-file-metrics.js.map +1 -0
- package/dist/ai-service/agent/tools/subagent-isolation.d.ts +27 -0
- package/dist/ai-service/agent/tools/subagent-isolation.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/subagent-isolation.js +57 -0
- package/dist/ai-service/agent/tools/subagent-isolation.js.map +1 -0
- package/dist/ai-service/agent/tools.d.ts +4 -1
- package/dist/ai-service/agent/tools.d.ts.map +1 -1
- package/dist/ai-service/agent/tools.js +51 -5
- package/dist/ai-service/agent/tools.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/download-attachments.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/download-attachments.js +1 -0
- package/dist/ai-service/agent/tools2/tools/download-attachments.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/start-test-run.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/start-test-run.js +14 -0
- package/dist/ai-service/agent/tools2/tools/start-test-run.js.map +1 -1
- package/dist/ai-service/agent/utils.d.ts.map +1 -1
- package/dist/ai-service/agent/utils.js +2 -2
- package/dist/ai-service/agent/utils.js.map +1 -1
- package/dist/ai-service/app-interface/filesystem/draft-manager.d.ts +2 -1
- package/dist/ai-service/app-interface/filesystem/draft-manager.d.ts.map +1 -1
- package/dist/ai-service/app-interface/filesystem/draft-manager.js +3 -0
- package/dist/ai-service/app-interface/filesystem/draft-manager.js.map +1 -1
- package/dist/ai-service/app-interface/filesystem/handlers/skill-handler.d.ts +2 -1
- package/dist/ai-service/app-interface/filesystem/handlers/skill-handler.d.ts.map +1 -1
- package/dist/ai-service/app-interface/filesystem/handlers/skill-handler.js +13 -11
- package/dist/ai-service/app-interface/filesystem/handlers/skill-handler.js.map +1 -1
- package/dist/ai-service/app-interface/filesystem/virtual-file-system.d.ts.map +1 -1
- package/dist/ai-service/app-interface/filesystem/virtual-file-system.js +1 -1
- package/dist/ai-service/app-interface/filesystem/virtual-file-system.js.map +1 -1
- package/dist/ai-service/app-interface/shell.d.ts +18 -9
- package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
- package/dist/ai-service/app-interface/shell.js +92 -112
- package/dist/ai-service/app-interface/shell.js.map +1 -1
- package/dist/ai-service/app-interface/typecheck-output-parser.d.ts +9 -0
- package/dist/ai-service/app-interface/typecheck-output-parser.d.ts.map +1 -0
- package/dist/ai-service/app-interface/typecheck-output-parser.js +21 -0
- package/dist/ai-service/app-interface/typecheck-output-parser.js.map +1 -0
- package/dist/ai-service/app-interface/vulnerability-gate.d.ts +5 -0
- package/dist/ai-service/app-interface/vulnerability-gate.d.ts.map +1 -1
- package/dist/ai-service/app-interface/vulnerability-gate.js +9 -2
- package/dist/ai-service/app-interface/vulnerability-gate.js.map +1 -1
- package/dist/ai-service/app-layout.d.ts +13 -0
- package/dist/ai-service/app-layout.d.ts.map +1 -0
- package/dist/ai-service/app-layout.js +25 -0
- package/dist/ai-service/app-layout.js.map +1 -0
- package/dist/ai-service/app-skills/manager.d.ts +2 -0
- package/dist/ai-service/app-skills/manager.d.ts.map +1 -1
- package/dist/ai-service/app-skills/manager.js +13 -6
- package/dist/ai-service/app-skills/manager.js.map +1 -1
- package/dist/ai-service/attachment-label-metrics.d.ts +5 -0
- package/dist/ai-service/attachment-label-metrics.d.ts.map +1 -0
- package/dist/ai-service/attachment-label-metrics.js +26 -0
- package/dist/ai-service/attachment-label-metrics.js.map +1 -0
- package/dist/ai-service/attachment-upload.d.ts.map +1 -1
- package/dist/ai-service/attachment-upload.js +6 -1
- package/dist/ai-service/attachment-upload.js.map +1 -1
- package/dist/ai-service/attachments/validate-prompt-attachments.d.ts +7 -0
- package/dist/ai-service/attachments/validate-prompt-attachments.d.ts.map +1 -0
- package/dist/ai-service/attachments/validate-prompt-attachments.js +96 -0
- package/dist/ai-service/attachments/validate-prompt-attachments.js.map +1 -0
- package/dist/ai-service/chat/chat-push-metrics.d.ts +17 -0
- package/dist/ai-service/chat/chat-push-metrics.d.ts.map +1 -1
- package/dist/ai-service/chat/chat-push-metrics.js +45 -0
- package/dist/ai-service/chat/chat-push-metrics.js.map +1 -1
- package/dist/ai-service/chat/chat-session-store.d.ts +51 -0
- package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
- package/dist/ai-service/chat/chat-session-store.js +181 -4
- package/dist/ai-service/chat/chat-session-store.js.map +1 -1
- package/dist/ai-service/features.d.ts +32 -0
- package/dist/ai-service/features.d.ts.map +1 -1
- package/dist/ai-service/features.js +32 -0
- package/dist/ai-service/features.js.map +1 -1
- package/dist/ai-service/helper-system-ai-attributes.d.ts +1 -1
- package/dist/ai-service/helper-system-ai-attributes.d.ts.map +1 -1
- package/dist/ai-service/helper-system-ai-attributes.js.map +1 -1
- package/dist/ai-service/index.d.ts +19 -4
- package/dist/ai-service/index.d.ts.map +1 -1
- package/dist/ai-service/index.js +176 -23
- package/dist/ai-service/index.js.map +1 -1
- package/dist/ai-service/llm/client.d.ts +9 -0
- package/dist/ai-service/llm/client.d.ts.map +1 -1
- package/dist/ai-service/llm/client.js +13 -43
- package/dist/ai-service/llm/client.js.map +1 -1
- package/dist/ai-service/llm/context-v2/adapter.d.ts +2 -0
- package/dist/ai-service/llm/context-v2/adapter.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/adapter.js +6 -0
- package/dist/ai-service/llm/context-v2/adapter.js.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts +8 -0
- package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/server-side.js +8 -0
- package/dist/ai-service/llm/context-v2/compaction/server-side.js.map +1 -1
- package/dist/ai-service/llm/context-v2/context-component-partition.d.ts +24 -0
- package/dist/ai-service/llm/context-v2/context-component-partition.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/context-component-partition.js +222 -0
- package/dist/ai-service/llm/context-v2/context-component-partition.js.map +1 -0
- package/dist/ai-service/llm/context-v2/context-metrics.d.ts +9 -0
- package/dist/ai-service/llm/context-v2/context-metrics.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/context-metrics.js +40 -2
- package/dist/ai-service/llm/context-v2/context-metrics.js.map +1 -1
- package/dist/ai-service/llm/context-v2/context.d.ts +116 -4
- package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/context.js +276 -64
- package/dist/ai-service/llm/context-v2/context.js.map +1 -1
- package/dist/ai-service/llm/context-v2/conversation-context.d.ts +18 -0
- package/dist/ai-service/llm/context-v2/conversation-context.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/file-read-ledger.d.ts +218 -0
- package/dist/ai-service/llm/context-v2/file-read-ledger.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/file-read-ledger.js +275 -0
- package/dist/ai-service/llm/context-v2/file-read-ledger.js.map +1 -0
- package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.js +5 -2
- package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.js.map +1 -1
- package/dist/ai-service/llm/context-v2/token-tracker.d.ts +2 -0
- package/dist/ai-service/llm/context-v2/token-tracker.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/token-tracker.js +4 -0
- package/dist/ai-service/llm/context-v2/token-tracker.js.map +1 -1
- package/dist/ai-service/llm/context-v2/tool-schema-tokens.d.ts +22 -0
- package/dist/ai-service/llm/context-v2/tool-schema-tokens.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/tool-schema-tokens.js +36 -0
- package/dist/ai-service/llm/context-v2/tool-schema-tokens.js.map +1 -0
- package/dist/ai-service/llm/context-v2/types.d.ts +8 -0
- package/dist/ai-service/llm/context-v2/types.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/types.js +48 -0
- package/dist/ai-service/llm/context-v2/types.js.map +1 -1
- package/dist/ai-service/llm/error.d.ts +2 -0
- package/dist/ai-service/llm/error.d.ts.map +1 -1
- package/dist/ai-service/llm/error.js +1 -0
- package/dist/ai-service/llm/error.js.map +1 -1
- package/dist/ai-service/llm/retry-exhaustion-reason.d.ts +2 -0
- package/dist/ai-service/llm/retry-exhaustion-reason.d.ts.map +1 -0
- package/dist/ai-service/llm/retry-exhaustion-reason.js +2 -0
- package/dist/ai-service/llm/retry-exhaustion-reason.js.map +1 -0
- package/dist/ai-service/llm/stream/errors.d.ts +6 -7
- package/dist/ai-service/llm/stream/errors.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/errors.js +7 -1
- package/dist/ai-service/llm/stream/errors.js.map +1 -1
- package/dist/ai-service/llm/stream/event-bus.d.ts +9 -1
- package/dist/ai-service/llm/stream/event-bus.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/event-bus.js.map +1 -1
- package/dist/ai-service/llm/stream/idle-monitor.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/idle-monitor.js.map +1 -1
- package/dist/ai-service/llm/stream/observers/context.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/observers/context.js +13 -0
- package/dist/ai-service/llm/stream/observers/context.js.map +1 -1
- package/dist/ai-service/llm/stream/observers/llmobs.d.ts +14 -1
- package/dist/ai-service/llm/stream/observers/llmobs.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/observers/llmobs.js +91 -7
- package/dist/ai-service/llm/stream/observers/llmobs.js.map +1 -1
- package/dist/ai-service/llm/stream/observers/logging.d.ts +3 -1
- package/dist/ai-service/llm/stream/observers/logging.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/observers/logging.js +73 -52
- package/dist/ai-service/llm/stream/observers/logging.js.map +1 -1
- package/dist/ai-service/llm/stream/observers/retry-notification.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/observers/retry-notification.js +16 -9
- package/dist/ai-service/llm/stream/observers/retry-notification.js.map +1 -1
- package/dist/ai-service/llm/stream/orchestrator.d.ts +5 -1
- package/dist/ai-service/llm/stream/orchestrator.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/orchestrator.js +103 -12
- package/dist/ai-service/llm/stream/orchestrator.js.map +1 -1
- package/dist/ai-service/llm/stream/retry-engine.d.ts +3 -1
- package/dist/ai-service/llm/stream/retry-engine.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/retry-engine.js +9 -0
- package/dist/ai-service/llm/stream/retry-engine.js.map +1 -1
- package/dist/ai-service/llm/stream/session.d.ts +8 -1
- package/dist/ai-service/llm/stream/session.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/session.js +19 -0
- package/dist/ai-service/llm/stream/session.js.map +1 -1
- package/dist/ai-service/llm/stream/types.d.ts +13 -0
- package/dist/ai-service/llm/stream/types.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/wait-state-tracker.d.ts +5 -14
- package/dist/ai-service/llm/stream/wait-state-tracker.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/wait-state-tracker.js +25 -0
- package/dist/ai-service/llm/stream/wait-state-tracker.js.map +1 -1
- package/dist/ai-service/mcp/embedded-playwright-mcp-server.js +8 -8
- package/dist/ai-service/mcp/embedded-playwright-mcp-server.js.map +1 -1
- package/dist/ai-service/policy-gate-remediation-usage.d.ts +2 -0
- package/dist/ai-service/policy-gate-remediation-usage.d.ts.map +1 -0
- package/dist/ai-service/policy-gate-remediation-usage.js +56 -0
- package/dist/ai-service/policy-gate-remediation-usage.js.map +1 -0
- package/dist/ai-service/prompts/summarize-destructive-migration.d.ts +12 -0
- package/dist/ai-service/prompts/summarize-destructive-migration.d.ts.map +1 -0
- package/dist/ai-service/prompts/summarize-destructive-migration.js +37 -0
- package/dist/ai-service/prompts/summarize-destructive-migration.js.map +1 -0
- package/dist/ai-service/recording/storage/session-recording-storage.js +2 -2
- package/dist/ai-service/recording/storage/session-recording-storage.js.map +1 -1
- package/dist/ai-service/security/secret-scanner-service.d.ts.map +1 -1
- package/dist/ai-service/security/secret-scanner-service.js +88 -23
- package/dist/ai-service/security/secret-scanner-service.js.map +1 -1
- package/dist/ai-service/skills/index.d.ts +0 -7
- package/dist/ai-service/skills/index.d.ts.map +1 -1
- package/dist/ai-service/skills/index.js +0 -9
- package/dist/ai-service/skills/index.js.map +1 -1
- package/dist/ai-service/skills/system/_registry.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/_registry.generated.js +4 -0
- package/dist/ai-service/skills/system/_registry.generated.js.map +1 -1
- package/dist/ai-service/skills/system/resolve.d.ts +2 -0
- package/dist/ai-service/skills/system/resolve.d.ts.map +1 -0
- package/dist/ai-service/skills/system/resolve.js +32 -0
- package/dist/ai-service/skills/system/resolve.js.map +1 -0
- package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-imports-legacy.generated.d.ts +2 -0
- package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-imports-legacy.generated.d.ts.map +1 -0
- package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-imports-legacy.generated.js +9 -0
- package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-imports-legacy.generated.js.map +1 -0
- package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-imports-sdk.generated.d.ts +2 -0
- package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-imports-sdk.generated.d.ts.map +1 -0
- package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-imports-sdk.generated.js +11 -0
- package/dist/ai-service/skills/system/superblocks-frontend/_variants/api-imports-sdk.generated.js.map +1 -0
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js +16 -24
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js.map +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.js +1 -1
- package/dist/ai-service/state-machine/clark-fsm.d.ts +11 -0
- package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.js +7 -1
- package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.js +21 -0
- package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.js +32 -9
- package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.js +21 -2
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/stable-peer.d.ts +11 -2
- package/dist/ai-service/state-machine/helpers/stable-peer.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/stable-peer.js +105 -18
- package/dist/ai-service/state-machine/helpers/stable-peer.js.map +1 -1
- package/dist/ai-service/state-machine/mocks.d.ts +3 -1
- package/dist/ai-service/state-machine/mocks.d.ts.map +1 -1
- package/dist/ai-service/state-machine/mocks.js +52 -13
- package/dist/ai-service/state-machine/mocks.js.map +1 -1
- package/dist/ai-service/tasks/peer-push.d.ts +3 -0
- package/dist/ai-service/tasks/peer-push.d.ts.map +1 -1
- package/dist/ai-service/tasks/peer-push.js +33 -0
- package/dist/ai-service/tasks/peer-push.js.map +1 -1
- package/dist/ai-service/util/call-peer.d.ts +2 -0
- package/dist/ai-service/util/call-peer.d.ts.map +1 -1
- package/dist/ai-service/util/call-peer.js +17 -4
- package/dist/ai-service/util/call-peer.js.map +1 -1
- package/dist/ai-service/util/line-ranges.d.ts +21 -0
- package/dist/ai-service/util/line-ranges.d.ts.map +1 -0
- package/dist/ai-service/util/line-ranges.js +36 -0
- package/dist/ai-service/util/line-ranges.js.map +1 -0
- package/dist/ai-service/util/peer-liveness.d.ts +1 -0
- package/dist/ai-service/util/peer-liveness.d.ts.map +1 -1
- package/dist/ai-service/util/peer-liveness.js +2 -0
- package/dist/ai-service/util/peer-liveness.js.map +1 -1
- package/dist/dev-server-ws-smoke-client.d.ts.map +1 -1
- package/dist/dev-server-ws-smoke-client.js +7 -0
- package/dist/dev-server-ws-smoke-client.js.map +1 -1
- package/dist/draft-interface.d.ts +8 -0
- package/dist/draft-interface.d.ts.map +1 -1
- package/dist/early-error-relay.d.ts +2 -0
- package/dist/early-error-relay.d.ts.map +1 -1
- package/dist/early-error-relay.js +7 -3
- package/dist/early-error-relay.js.map +1 -1
- package/dist/file-sync-vite-plugin.d.ts.map +1 -1
- package/dist/file-sync-vite-plugin.js +43 -15
- package/dist/file-sync-vite-plugin.js.map +1 -1
- package/dist/file-system-helpers.d.ts +2 -2
- package/dist/file-system-helpers.d.ts.map +1 -1
- package/dist/file-system-helpers.js +3 -3
- package/dist/file-system-helpers.js.map +1 -1
- package/dist/git-service/errors.d.ts +9 -0
- package/dist/git-service/errors.d.ts.map +1 -1
- package/dist/git-service/errors.js +15 -0
- package/dist/git-service/errors.js.map +1 -1
- package/dist/git-service/index.d.ts +2 -2
- package/dist/git-service/index.d.ts.map +1 -1
- package/dist/git-service/index.js +2 -2
- package/dist/git-service/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lock-service/activity-tracker.d.ts +5 -0
- package/dist/lock-service/activity-tracker.d.ts.map +1 -1
- package/dist/lock-service/activity-tracker.js +10 -0
- package/dist/lock-service/activity-tracker.js.map +1 -1
- package/dist/lock-service/index.d.ts +69 -4
- package/dist/lock-service/index.d.ts.map +1 -1
- package/dist/lock-service/index.js +139 -29
- package/dist/lock-service/index.js.map +1 -1
- package/dist/migration-templates/app-fullstack/client/components/ui/accordion.tsx +9 -65
- package/dist/migration-templates/app-fullstack/client/components/ui/area-chart.tsx +36 -156
- package/dist/migration-templates/app-fullstack/client/components/ui/aspect-ratio.tsx +1 -5
- package/dist/migration-templates/app-fullstack/client/components/ui/avatar.tsx +4 -18
- package/dist/migration-templates/app-fullstack/client/components/ui/badge.tsx +6 -29
- package/dist/migration-templates/app-fullstack/client/components/ui/bar-chart.tsx +41 -167
- package/dist/migration-templates/app-fullstack/client/components/ui/breadcrumb.tsx +1 -4
- package/dist/migration-templates/app-fullstack/client/components/ui/button.tsx +5 -64
- package/dist/migration-templates/app-fullstack/client/components/ui/calendar.tsx +6 -42
- package/dist/migration-templates/app-fullstack/client/components/ui/card.tsx +1 -4
- package/dist/migration-templates/app-fullstack/client/components/ui/checkbox.tsx +4 -29
- package/dist/migration-templates/app-fullstack/client/components/ui/date-range-picker.tsx +13 -154
- package/dist/migration-templates/app-fullstack/client/components/ui/dialog.tsx +7 -34
- package/dist/migration-templates/app-fullstack/client/components/ui/file-dropzone.tsx +12 -95
- package/dist/migration-templates/app-fullstack/client/components/ui/file-input.tsx +12 -73
- package/dist/migration-templates/app-fullstack/client/components/ui/hover-card.tsx +10 -66
- package/dist/migration-templates/app-fullstack/client/components/ui/icon.tsx +3 -16
- package/dist/migration-templates/app-fullstack/client/components/ui/image.tsx +4 -16
- package/dist/migration-templates/app-fullstack/client/components/ui/input.tsx +18 -159
- package/dist/migration-templates/app-fullstack/client/components/ui/label.tsx +1 -4
- package/dist/migration-templates/app-fullstack/client/components/ui/line-chart.tsx +32 -148
- package/dist/migration-templates/app-fullstack/client/components/ui/link.tsx +4 -24
- package/dist/migration-templates/app-fullstack/client/components/ui/navigation-menu.tsx +3 -24
- package/dist/migration-templates/app-fullstack/client/components/ui/pagination.tsx +4 -24
- package/dist/migration-templates/app-fullstack/client/components/ui/pie-chart.tsx +28 -142
- package/dist/migration-templates/app-fullstack/client/components/ui/popover.tsx +9 -61
- package/dist/migration-templates/app-fullstack/client/components/ui/progress.tsx +2 -11
- package/dist/migration-templates/app-fullstack/client/components/ui/radar-chart.tsx +37 -171
- package/dist/migration-templates/app-fullstack/client/components/ui/radial-chart.tsx +50 -202
- package/dist/migration-templates/app-fullstack/client/components/ui/select.tsx +5 -35
- package/dist/migration-templates/app-fullstack/client/components/ui/separator.tsx +1 -8
- package/dist/migration-templates/app-fullstack/client/components/ui/sheet.tsx +7 -40
- package/dist/migration-templates/app-fullstack/client/components/ui/sidebar.tsx +3 -35
- package/dist/migration-templates/app-fullstack/client/components/ui/slider.tsx +11 -95
- package/dist/migration-templates/app-fullstack/client/components/ui/switch.tsx +5 -32
- package/dist/migration-templates/app-fullstack/client/components/ui/textarea.tsx +13 -90
- package/dist/migration-templates/app-fullstack/client/components/ui/toggle-group.tsx +11 -75
- package/dist/migration-templates/app-fullstack/client/components/ui/toggle.tsx +8 -70
- package/dist/migration-templates/app-fullstack/client/components/ui/tooltip.tsx +9 -62
- package/dist/parsing/imports.d.ts +22 -0
- package/dist/parsing/imports.d.ts.map +1 -1
- package/dist/parsing/imports.js +71 -0
- package/dist/parsing/imports.js.map +1 -1
- package/dist/reconnect-guard-plugin.d.ts +53 -0
- package/dist/reconnect-guard-plugin.d.ts.map +1 -0
- package/dist/reconnect-guard-plugin.js +203 -0
- package/dist/reconnect-guard-plugin.js.map +1 -0
- package/dist/resolver.d.ts +2 -1
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +4 -6
- package/dist/resolver.js.map +1 -1
- package/dist/router-parser-metrics.d.ts +2 -1
- package/dist/router-parser-metrics.d.ts.map +1 -1
- package/dist/router-parser-metrics.js.map +1 -1
- package/dist/router-parser.d.ts +10 -1
- package/dist/router-parser.d.ts.map +1 -1
- package/dist/router-parser.js +306 -69
- package/dist/router-parser.js.map +1 -1
- package/dist/scan-scope-merge-metrics.d.ts +2 -0
- package/dist/scan-scope-merge-metrics.d.ts.map +1 -0
- package/dist/scan-scope-merge-metrics.js +18 -0
- package/dist/scan-scope-merge-metrics.js.map +1 -0
- package/dist/scan-scope-merge.d.ts +2 -2
- package/dist/scan-scope-merge.d.ts.map +1 -1
- package/dist/scan-scope-merge.js +64 -14
- package/dist/scan-scope-merge.js.map +1 -1
- package/dist/server-rpc/index.d.ts.map +1 -1
- package/dist/server-rpc/index.js +12 -2
- package/dist/server-rpc/index.js.map +1 -1
- package/dist/socket-manager.d.ts +1 -1
- package/dist/socket-manager.d.ts.map +1 -1
- package/dist/socket-manager.js +276 -15
- package/dist/socket-manager.js.map +1 -1
- package/dist/sync-service/download.js +2 -2
- package/dist/sync-service/download.js.map +1 -1
- package/dist/sync-service/index.d.ts +95 -22
- package/dist/sync-service/index.d.ts.map +1 -1
- package/dist/sync-service/index.js +402 -56
- package/dist/sync-service/index.js.map +1 -1
- package/dist/sync-service/page-reference-guard.d.ts +17 -0
- package/dist/sync-service/page-reference-guard.d.ts.map +1 -0
- package/dist/sync-service/page-reference-guard.js +170 -0
- package/dist/sync-service/page-reference-guard.js.map +1 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +16 -2
- package/dist/util.js.map +1 -1
- package/package.json +15 -14
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
2
4
|
import { normalizePath } from "vite";
|
|
3
5
|
import { SyncronizationStatus, } from "@superblocksteam/library-shared/types";
|
|
4
|
-
import { addTracingToMethods, TracedEventEmitter, } from "@superblocksteam/shared";
|
|
5
|
-
import { BadRequestError } from "@superblocksteam/shared";
|
|
6
|
-
import { unwrapResponseDto } from "@superblocksteam/shared";
|
|
6
|
+
import { addTracingToMethods, TracedEventEmitter, BadRequestError, unwrapResponseDto, } from "@superblocksteam/shared";
|
|
7
7
|
import { applyErrorHandling, } from "../errors/error-handler.js";
|
|
8
8
|
import { makeServerError } from "../errors/make-server-error.js";
|
|
9
9
|
import { POLICY_GATE_RUN_SYNC_BLOCKED_ERROR, policyGateRunRegistry, } from "../policy-gate-run-registry.js";
|
|
10
|
-
import { getErrorMeta } from "../util/logger.js";
|
|
10
|
+
import { getErrorMeta, } from "../util/logger.js";
|
|
11
11
|
import { OperationQueue } from "../util/operation-queue.js";
|
|
12
12
|
import { withResolvers } from "../util/with-resolvers.js";
|
|
13
13
|
import { classifyCheckpointResult } from "./checkpoint-result.js";
|
|
@@ -15,6 +15,7 @@ import { downloadApplicationDirectory } from "./download.js";
|
|
|
15
15
|
import { hasSwapFiles, discardAllDrafts, commitAllDrafts, getDraftInfo, createDraft, generateDraftDiff, createDraftMarkerOnly, createDraftDelete, cleanUpDraftsDirectory, } from "./draft-helpers.js";
|
|
16
16
|
import { recordDraftStateReport } from "./draft-state-report-metrics.js";
|
|
17
17
|
import { HashCache } from "./hash-cache.js";
|
|
18
|
+
import { validatePageReferences } from "./page-reference-guard.js";
|
|
18
19
|
import { snapshotLocalDirectory, snapshotLocalDirectoryWithDrafts, } from "./snapshot/take-snapshot.js";
|
|
19
20
|
import { uploadDirectorySnapshot } from "./snapshot/upload-snapshot.js";
|
|
20
21
|
const SYNC_INTERVAL_MS = 10_000;
|
|
@@ -115,6 +116,16 @@ export class SyncService extends TracedEventEmitter {
|
|
|
115
116
|
draftStateReports = new Map();
|
|
116
117
|
/** This will be defined if (and only if) a sync is currently pending */
|
|
117
118
|
pendingSyncUp;
|
|
119
|
+
/**
|
|
120
|
+
* The sync most recently handed to `syncOperationQueue`.
|
|
121
|
+
*
|
|
122
|
+
* A timer clears `pendingSyncUp` before it enqueues, so an empty pending
|
|
123
|
+
* slot does not mean nothing newer is outstanding: a second save can arm and
|
|
124
|
+
* fire its own timer while the first sync waits its turn in the queue. This
|
|
125
|
+
* is how a sync that finds the slot empty tells "I am the newest" from "a
|
|
126
|
+
* newer one is already queued ahead of me".
|
|
127
|
+
*/
|
|
128
|
+
lastEnqueuedSyncUp;
|
|
118
129
|
handledAutosyncUploads = new WeakSet();
|
|
119
130
|
latestAutosyncPreparation;
|
|
120
131
|
latestAutosyncUpload;
|
|
@@ -128,6 +139,7 @@ export class SyncService extends TracedEventEmitter {
|
|
|
128
139
|
_rpcClient;
|
|
129
140
|
_logger;
|
|
130
141
|
_moduleInvalidationCallback;
|
|
142
|
+
pageReferenceResolver;
|
|
131
143
|
removeManualCommitsEnabled;
|
|
132
144
|
constructor(config) {
|
|
133
145
|
super(config.tracer);
|
|
@@ -149,12 +161,16 @@ export class SyncService extends TracedEventEmitter {
|
|
|
149
161
|
operation: "syncing current directory to server",
|
|
150
162
|
},
|
|
151
163
|
hasLocalDraftChanges: { operation: "checking for local draft changes" },
|
|
152
|
-
commitLocalDraftChanges: {
|
|
164
|
+
commitLocalDraftChanges: {
|
|
165
|
+
operation: "committing local draft changes",
|
|
166
|
+
rethrow: true,
|
|
167
|
+
},
|
|
153
168
|
});
|
|
154
169
|
const methods = [
|
|
155
170
|
"downloadDirectory",
|
|
156
171
|
"uploadDirectory",
|
|
157
172
|
"syncCurrentDirectoryWithDraftsNow",
|
|
173
|
+
"syncCurrentDirectoryWithDraftsNowIfChanged",
|
|
158
174
|
"uploadDirectoryNowIfNeeded",
|
|
159
175
|
"scheduleUploadDirectory",
|
|
160
176
|
"performUploadDirectory",
|
|
@@ -238,6 +254,9 @@ export class SyncService extends TracedEventEmitter {
|
|
|
238
254
|
setModuleInvalidationCallback(callback) {
|
|
239
255
|
this._moduleInvalidationCallback = callback;
|
|
240
256
|
}
|
|
257
|
+
setPageReferenceResolver(resolver) {
|
|
258
|
+
this.pageReferenceResolver = resolver;
|
|
259
|
+
}
|
|
241
260
|
/**
|
|
242
261
|
* Pulls the application down from the server.
|
|
243
262
|
*/
|
|
@@ -297,15 +316,7 @@ export class SyncService extends TracedEventEmitter {
|
|
|
297
316
|
}
|
|
298
317
|
});
|
|
299
318
|
}
|
|
300
|
-
/**
|
|
301
|
-
* Request a sync of the local application directory to the server.
|
|
302
|
-
*
|
|
303
|
-
* This method is asynchronous, in the sense that it does not wait for the sync to complete. It simply schedules
|
|
304
|
-
* a sync to happen in the future. The returned promise resolves as soon as the method has collected enough information
|
|
305
|
-
* to schedule the sync. So the caller must wait for the returned promise to resolve before performing any filesystem
|
|
306
|
-
* operations.
|
|
307
|
-
* This method is also idempotent.
|
|
308
|
-
*/
|
|
319
|
+
/** Resolves after scheduling, not uploading. Await before filesystem changes. */
|
|
309
320
|
uploadDirectory(source) {
|
|
310
321
|
if (this._autoSyncPaused) {
|
|
311
322
|
this.getLogger().info("[sync-service] Auto-sync paused (migration in progress), skipping upload");
|
|
@@ -367,14 +378,18 @@ export class SyncService extends TracedEventEmitter {
|
|
|
367
378
|
* Requests an immediate sync of the local application directory to the server, if there are any pending changes.
|
|
368
379
|
* If there are no pending changes, the method returns immediately.
|
|
369
380
|
*
|
|
381
|
+
* @param options.reclaimAfterHandback - Whether this drain may take back an
|
|
382
|
+
* edit lock this session handed back. Pass it only from a call site a user
|
|
383
|
+
* action is behind; see {@link PendingSyncUpState.reclaimAfterHandback}.
|
|
370
384
|
* @returns the dbfs hash of the most recent sync
|
|
371
385
|
*/
|
|
372
|
-
uploadDirectoryNowIfNeeded(source) {
|
|
386
|
+
uploadDirectoryNowIfNeeded(source, options) {
|
|
373
387
|
if (!this.pendingSyncUp) {
|
|
374
388
|
this.getLogger().info("[sync-service] No pending sync, skipping");
|
|
375
389
|
return Promise.resolve({
|
|
376
390
|
directoryHash: this.lastSyncedDirectoryHash || "",
|
|
377
391
|
commitId: undefined,
|
|
392
|
+
localNoPending: true,
|
|
378
393
|
});
|
|
379
394
|
}
|
|
380
395
|
const directorySnapshot = this.pendingSyncUp.directorySnapshot;
|
|
@@ -383,7 +398,7 @@ export class SyncService extends TracedEventEmitter {
|
|
|
383
398
|
this.latestAutosyncPreparation = preparation;
|
|
384
399
|
this.latestAutosyncUpload = undefined;
|
|
385
400
|
return this.fsOperation(async () => {
|
|
386
|
-
const completedPromise = this.scheduleUploadDirectory(directorySnapshot, true, this.generationNumberSequence?.currentValue, source);
|
|
401
|
+
const completedPromise = this.scheduleUploadDirectory(directorySnapshot, true, this.generationNumberSequence?.currentValue, source, { reclaimAfterHandback: options?.reclaimAfterHandback ?? false });
|
|
387
402
|
return await this.trackAutosyncUpload(completedPromise, source, preparation);
|
|
388
403
|
});
|
|
389
404
|
}
|
|
@@ -405,7 +420,50 @@ export class SyncService extends TracedEventEmitter {
|
|
|
405
420
|
return result;
|
|
406
421
|
});
|
|
407
422
|
}
|
|
408
|
-
|
|
423
|
+
/**
|
|
424
|
+
* Flushes the current directory for teardown without creating a redundant
|
|
425
|
+
* setApplicationHash request when disk already matches the last successful
|
|
426
|
+
* sync. Termination can race application or branch deletion; avoiding the
|
|
427
|
+
* no-op request prevents that expected teardown from looking like lost work,
|
|
428
|
+
* while a genuinely changed directory still attempts its final upload.
|
|
429
|
+
*/
|
|
430
|
+
syncCurrentDirectoryWithDraftsNowIfChanged(source) {
|
|
431
|
+
if (this._autoSyncPaused) {
|
|
432
|
+
this.getLogger().info("[sync-service] Auto-sync paused (migration in progress), skipping final upload");
|
|
433
|
+
return Promise.resolve({
|
|
434
|
+
directoryHash: this.lastSyncedDirectoryHash || "",
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
return this.fsOperation(async () => {
|
|
438
|
+
const directorySnapshot = await snapshotLocalDirectoryWithDrafts(this.appRootDirPath);
|
|
439
|
+
// Debounce clears pendingSyncUp before performUploadDirectory runs, so
|
|
440
|
+
// an in-flight upload can still rewrite lastSyncedDirectoryHash after a
|
|
441
|
+
// skip. Drain that queue first; fs-outer/sync-inner is the same nesting
|
|
442
|
+
// as flipTemplate. Clear the timer before waiting so it cannot enqueue
|
|
443
|
+
// a stale snapshot behind this barrier; keep pendingSyncUp so skip can
|
|
444
|
+
// still settle its promise.
|
|
445
|
+
if (this.pendingSyncUp) {
|
|
446
|
+
clearTimeout(this.pendingSyncUp.timeoutId);
|
|
447
|
+
}
|
|
448
|
+
await this.syncOperationQueue.enqueue(async () => undefined);
|
|
449
|
+
if (directorySnapshot.hash === this.lastSyncedDirectoryHash) {
|
|
450
|
+
const result = { directoryHash: directorySnapshot.hash };
|
|
451
|
+
if (this.pendingSyncUp) {
|
|
452
|
+
this.pendingSyncUp.resolve(result);
|
|
453
|
+
this.pendingSyncUp = undefined;
|
|
454
|
+
}
|
|
455
|
+
this.status = SyncServiceStatus.IDLE;
|
|
456
|
+
this.setSynchronizationStatus(SyncronizationStatus.IN_SYNC, source);
|
|
457
|
+
this.getLogger().info(`[sync-service] Current directory already synchronized, skipping final upload (source=${source})`);
|
|
458
|
+
return result;
|
|
459
|
+
}
|
|
460
|
+
const completedPromise = this.scheduleUploadDirectory(directorySnapshot, true, this.generationNumberSequence?.currentValue, source);
|
|
461
|
+
const result = await completedPromise;
|
|
462
|
+
this.getLogger().info("[sync-service] Changed current directory synchronized during final flush");
|
|
463
|
+
return result;
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
scheduleUploadDirectory(directorySnapshot, performNow, generationNumber, source, options) {
|
|
409
467
|
if (performNow && this.pendingSyncUp) {
|
|
410
468
|
// There is already a sync pending, cancel it so that we can schedule a new one, to be performed immediately
|
|
411
469
|
clearTimeout(this.pendingSyncUp.timeoutId);
|
|
@@ -418,7 +476,13 @@ export class SyncService extends TracedEventEmitter {
|
|
|
418
476
|
// server classifies the commit by it.
|
|
419
477
|
this.pendingSyncUp.directorySnapshot = directorySnapshot;
|
|
420
478
|
this.pendingSyncUp.generationNumber = generationNumber;
|
|
421
|
-
|
|
479
|
+
// A sync kept pending after a skip has no timer (see
|
|
480
|
+
// `keepSkippedSyncPending`), so a save joining it arms one whichever
|
|
481
|
+
// debounce is in force; otherwise the snapshot only leaves on an
|
|
482
|
+
// explicit drain.
|
|
483
|
+
if (!performNow &&
|
|
484
|
+
(this.removeManualCommitsEnabled ||
|
|
485
|
+
this.pendingSyncUp.timeoutId === undefined)) {
|
|
422
486
|
this.armPendingSyncTimeout(this.nextAutosyncDelayMs(this.pendingSyncUp.burstStartedAt));
|
|
423
487
|
}
|
|
424
488
|
}
|
|
@@ -433,6 +497,8 @@ export class SyncService extends TracedEventEmitter {
|
|
|
433
497
|
completed: completedPromise,
|
|
434
498
|
directorySnapshot,
|
|
435
499
|
generationNumber,
|
|
500
|
+
performNow,
|
|
501
|
+
reclaimAfterHandback: options?.reclaimAfterHandback ?? false,
|
|
436
502
|
reject,
|
|
437
503
|
resolve,
|
|
438
504
|
source,
|
|
@@ -464,10 +530,44 @@ export class SyncService extends TracedEventEmitter {
|
|
|
464
530
|
pendingSyncUp.timeoutId = setTimeout(() => {
|
|
465
531
|
void (async () => {
|
|
466
532
|
this.pendingSyncUp = undefined;
|
|
533
|
+
this.lastEnqueuedSyncUp = pendingSyncUp;
|
|
467
534
|
await this.syncOperationQueue.enqueue(async () => {
|
|
468
535
|
try {
|
|
469
|
-
|
|
536
|
+
// Only a sync a user action is behind may take back a lock this
|
|
537
|
+
// session handed over, and the call site says so. A debounced
|
|
538
|
+
// save is not evidence its user came back to the keyboard — a tab
|
|
539
|
+
// left open reconnects its socket by itself — and after a handback
|
|
540
|
+
// the lock is free, so letting one reclaim would hand the app to
|
|
541
|
+
// somebody who walked away (APPS-4840).
|
|
542
|
+
const isLockValid = await this.lockService.ensureLockForWrite({
|
|
543
|
+
reclaimAfterHandback: pendingSyncUp.reclaimAfterHandback,
|
|
544
|
+
});
|
|
470
545
|
if (!isLockValid) {
|
|
546
|
+
if (!pendingSyncUp.performNow) {
|
|
547
|
+
// Nothing is waiting on this one, and not holding the lock is
|
|
548
|
+
// its ordinary answer: the session it belonged to handed the
|
|
549
|
+
// lock back, or another client holds it. Failing it took the
|
|
550
|
+
// sandbox down with it — nothing awaits this promise, so the
|
|
551
|
+
// rejection was unhandled, and the dev server turns an
|
|
552
|
+
// unhandled rejection into `process.exit` (ENG-6808).
|
|
553
|
+
//
|
|
554
|
+
// Skipped, not synced: the status stays LOCAL_AHEAD, the
|
|
555
|
+
// last-synced hash is left alone, and the snapshot stays
|
|
556
|
+
// pending so the next save or an explicit drain still sends
|
|
557
|
+
// it.
|
|
558
|
+
this.getLogger().info(`[sync-service] Skipping autosync, this session does not hold the edit lock (source=${pendingSyncUp.source})`);
|
|
559
|
+
this.keepSkippedSyncPending(pendingSyncUp);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
// Its caller hears about this one, but the local files are ahead
|
|
563
|
+
// either way, so the snapshot is retained on both branches. A
|
|
564
|
+
// drain that finds nothing pending hands back the last synced
|
|
565
|
+
// hash without asking the server, and a failed accept followed
|
|
566
|
+
// by a confident "DBFS is current" is how stale files reach an
|
|
567
|
+
// API run.
|
|
568
|
+
if (this.retentionFor(pendingSyncUp).kind === "retain") {
|
|
569
|
+
this.retainAsPendingSync(this.forkPendingSyncForRetry(pendingSyncUp));
|
|
570
|
+
}
|
|
471
571
|
throw new Error("Lock is not valid");
|
|
472
572
|
}
|
|
473
573
|
const result = await this.performUploadDirectory(
|
|
@@ -486,6 +586,112 @@ export class SyncService extends TracedEventEmitter {
|
|
|
486
586
|
})();
|
|
487
587
|
}, timeoutDelayMs);
|
|
488
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* Where the snapshot of a sync that could not have the lock belongs.
|
|
591
|
+
*
|
|
592
|
+
* Keeping it matters because the timer cleared `pendingSyncUp` before the
|
|
593
|
+
* sync ran: `uploadDirectoryNowIfNeeded` drains only what is pending and
|
|
594
|
+
* otherwise hands back the last synced hash, so an editor calling
|
|
595
|
+
* `ensureFilesSynced` before running an API would be told DBFS is current
|
|
596
|
+
* while the status says LOCAL_AHEAD, and run against stale files.
|
|
597
|
+
*
|
|
598
|
+
* Only the newest outstanding snapshot may be kept. Snapshots are
|
|
599
|
+
* whole-directory and `performUploadDirectory` sets the application hash to
|
|
600
|
+
* whatever it uploads, so parking an older one behind a newer upload reverts
|
|
601
|
+
* the server files to it once something drains. An empty pending slot does
|
|
602
|
+
* not establish newest: a save that landed while this one waited its turn
|
|
603
|
+
* may already have fired its own timer and queued behind it, emptying the
|
|
604
|
+
* slot on the way. That is what `lastEnqueuedSyncUp` answers.
|
|
605
|
+
*/
|
|
606
|
+
retentionFor(enqueued) {
|
|
607
|
+
if (this._autoSyncPaused) {
|
|
608
|
+
// A migration paused autosync while this waited its turn; it wants the
|
|
609
|
+
// queues empty, and the pod restarts with a fresh service on success.
|
|
610
|
+
return { kind: "discarded" };
|
|
611
|
+
}
|
|
612
|
+
if (this.pendingSyncUp) {
|
|
613
|
+
// A save landed while this waited its turn and opened a new pending
|
|
614
|
+
// sync, which carries everything this one had.
|
|
615
|
+
return { kind: "superseded", by: this.pendingSyncUp };
|
|
616
|
+
}
|
|
617
|
+
if (this.lastEnqueuedSyncUp && this.lastEnqueuedSyncUp !== enqueued) {
|
|
618
|
+
// A save landed, armed its timer, and fired it, so the newer snapshot is
|
|
619
|
+
// queued behind this one rather than sitting in the pending slot. It
|
|
620
|
+
// still carries everything this one had, and it uploads after this
|
|
621
|
+
// returns, so retaining this one would leave an older snapshot drainable
|
|
622
|
+
// over the top of it.
|
|
623
|
+
return { kind: "superseded", by: this.lastEnqueuedSyncUp };
|
|
624
|
+
}
|
|
625
|
+
return { kind: "retain" };
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Park a snapshot in the pending slot, without a timer.
|
|
629
|
+
*
|
|
630
|
+
* Re-arming one would retry every interval for as long as the lock stays
|
|
631
|
+
* handed back, which for a pod nobody came back to is until it is reaped.
|
|
632
|
+
* Instead the snapshot leaves on the next save, which arms a timer as it
|
|
633
|
+
* joins (see `scheduleUploadDirectory`), or on an explicit drain. The burst
|
|
634
|
+
* clock restarts so that save gets an ordinary debounce rather than firing
|
|
635
|
+
* at once because the original burst's max wait has long passed.
|
|
636
|
+
*/
|
|
637
|
+
retainAsPendingSync(toRetain) {
|
|
638
|
+
toRetain.timeoutId = undefined;
|
|
639
|
+
toRetain.burstStartedAt = performance.now();
|
|
640
|
+
this.pendingSyncUp = toRetain;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Retain a skipped sync's snapshot and settle its promise, which for a
|
|
644
|
+
* superseded one is the newer sync's outcome.
|
|
645
|
+
*
|
|
646
|
+
* Only for the skip path: a sync that fails settles its own promise through
|
|
647
|
+
* the caller's `catch`, and must not also adopt another sync's outcome.
|
|
648
|
+
*/
|
|
649
|
+
keepSkippedSyncPending(skipped) {
|
|
650
|
+
const retention = this.retentionFor(skipped);
|
|
651
|
+
if (retention.kind === "superseded") {
|
|
652
|
+
retention.by.completed.then(skipped.resolve, skipped.reject);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (retention.kind === "retain") {
|
|
656
|
+
this.retainAsPendingSync(skipped);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Copy a sync whose promise is already spoken for, so its snapshot can be
|
|
661
|
+
* retained without that promise coming along.
|
|
662
|
+
*
|
|
663
|
+
* A failed sync's promise is rejected for its caller, and a retained sync's
|
|
664
|
+
* is handed to whatever picks the snapshot up next: a save coalescing into
|
|
665
|
+
* it is returned this promise, and the final flush resolves it. Both would
|
|
666
|
+
* be no-ops against the settled one.
|
|
667
|
+
*
|
|
668
|
+
* The copy is a plain trailing snapshot. Nothing awaits it, and the claim to
|
|
669
|
+
* reclaim a handed-back lock belonged to the user action that failed, not to
|
|
670
|
+
* whoever happens to save next.
|
|
671
|
+
*/
|
|
672
|
+
forkPendingSyncForRetry(settled) {
|
|
673
|
+
const { promise: completed, resolve, reject, } = withResolvers();
|
|
674
|
+
// Whoever picks this up brings its own reporting, through
|
|
675
|
+
// `trackAutosyncUpload`. Until then the promise needs a handler of its
|
|
676
|
+
// own: an unhandled rejection is what the dev server turns into
|
|
677
|
+
// `process.exit`, which is the failure this whole path exists to avoid.
|
|
678
|
+
void completed.catch((error) => {
|
|
679
|
+
logSyncError(this.getLogger(), error, "[sync-service] Retained snapshot failed to synchronize");
|
|
680
|
+
});
|
|
681
|
+
return {
|
|
682
|
+
burstStartedAt: settled.burstStartedAt,
|
|
683
|
+
completed,
|
|
684
|
+
directorySnapshot: settled.directorySnapshot,
|
|
685
|
+
generationNumber: settled.generationNumber,
|
|
686
|
+
performNow: false,
|
|
687
|
+
reclaimAfterHandback: false,
|
|
688
|
+
reject,
|
|
689
|
+
resolve,
|
|
690
|
+
source: settled.source,
|
|
691
|
+
syncTime: settled.syncTime,
|
|
692
|
+
timeoutId: undefined,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
489
695
|
cancelPendingSync() {
|
|
490
696
|
if (this.pendingSyncUp) {
|
|
491
697
|
clearTimeout(this.pendingSyncUp.timeoutId);
|
|
@@ -519,11 +725,7 @@ export class SyncService extends TracedEventEmitter {
|
|
|
519
725
|
this._autoSyncPaused = false;
|
|
520
726
|
this.status = SyncServiceStatus.IDLE;
|
|
521
727
|
}
|
|
522
|
-
/**
|
|
523
|
-
* Takes a snapshot of an arbitrary local directory and uploads it to DBFS.
|
|
524
|
-
* Used by the git deploy flow to upload a worktree snapshot for a main branch commit.
|
|
525
|
-
* Does NOT create any internal commits — the caller is responsible for that.
|
|
526
|
-
*/
|
|
728
|
+
/** Uploads a directory snapshot to DBFS without creating an internal commit. */
|
|
527
729
|
async uploadSnapshotToDbfs(localDirectoryPath) {
|
|
528
730
|
const snapshot = await snapshotLocalDirectory(localDirectoryPath);
|
|
529
731
|
await this._rpcClient.call(async (rpcClient) => {
|
|
@@ -645,6 +847,7 @@ export class SyncService extends TracedEventEmitter {
|
|
|
645
847
|
return {
|
|
646
848
|
checkpointSkipped,
|
|
647
849
|
commitId,
|
|
850
|
+
degradedMode,
|
|
648
851
|
directoryHash: directorySnapshot.hash,
|
|
649
852
|
};
|
|
650
853
|
}
|
|
@@ -678,7 +881,14 @@ export class SyncService extends TracedEventEmitter {
|
|
|
678
881
|
return this.fsOperation(async () => {
|
|
679
882
|
const directorySnapshot = await snapshotLocalDirectoryWithDrafts(this.appRootDirPath);
|
|
680
883
|
return this.syncOperationQueue.enqueue(async () => {
|
|
681
|
-
|
|
884
|
+
// A migration runs off the editor's migrate banner (`/sb-migrate-to-
|
|
885
|
+
// fullstack`), so a user is behind this write and it may take back a
|
|
886
|
+
// lock this session handed over. It fails loudly when it cannot: a
|
|
887
|
+
// migration half applied in silence is worse than one that reports it
|
|
888
|
+
// could not run.
|
|
889
|
+
const isLockValid = await this.lockService.ensureLockForWrite({
|
|
890
|
+
reclaimAfterHandback: true,
|
|
891
|
+
});
|
|
682
892
|
if (!isLockValid) {
|
|
683
893
|
throw new Error("Lock is not valid");
|
|
684
894
|
}
|
|
@@ -716,7 +926,14 @@ export class SyncService extends TracedEventEmitter {
|
|
|
716
926
|
return this.fsOperation(async () => {
|
|
717
927
|
const directorySnapshot = await snapshotLocalDirectoryWithDrafts(this.appRootDirPath);
|
|
718
928
|
return this.syncOperationQueue.enqueue(async () => {
|
|
719
|
-
|
|
929
|
+
// A migration runs off the editor's migrate banner (`/sb-migrate-to-
|
|
930
|
+
// fullstack`), so a user is behind this write and it may take back a
|
|
931
|
+
// lock this session handed over. It fails loudly when it cannot: a
|
|
932
|
+
// migration half applied in silence is worse than one that reports it
|
|
933
|
+
// could not run.
|
|
934
|
+
const isLockValid = await this.lockService.ensureLockForWrite({
|
|
935
|
+
reclaimAfterHandback: true,
|
|
936
|
+
});
|
|
720
937
|
if (!isLockValid) {
|
|
721
938
|
throw new Error("Lock is not valid");
|
|
722
939
|
}
|
|
@@ -730,11 +947,6 @@ export class SyncService extends TracedEventEmitter {
|
|
|
730
947
|
});
|
|
731
948
|
});
|
|
732
949
|
}
|
|
733
|
-
/**
|
|
734
|
-
* Resolve datasource integration IDs (UUIDs) to plugin IDs.
|
|
735
|
-
* Used by migration preflight to classify whether UUID-backed steps map to
|
|
736
|
-
* supported SDK integration types.
|
|
737
|
-
*/
|
|
738
950
|
async getIntegrationPluginIds(integrationIds) {
|
|
739
951
|
const uniqueIntegrationIds = [...new Set(integrationIds)];
|
|
740
952
|
if (uniqueIntegrationIds.length === 0) {
|
|
@@ -802,29 +1014,146 @@ export class SyncService extends TracedEventEmitter {
|
|
|
802
1014
|
* @returns A promise that resolves when the draft edit or addition is created.
|
|
803
1015
|
*/
|
|
804
1016
|
async createDraftFile(filePath, draftContent) {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1017
|
+
await this.applyDraftChanges([{ filePath, content: draftContent }]);
|
|
1018
|
+
}
|
|
1019
|
+
async applyDraftChanges(changes) {
|
|
1020
|
+
const normalized = changes.map((change) => {
|
|
1021
|
+
const filePath = normalizePath(change.filePath);
|
|
1022
|
+
const fullPath = path.resolve(this.appRootDirPath, filePath);
|
|
1023
|
+
const relativePath = path.relative(this.appRootDirPath, fullPath);
|
|
1024
|
+
if (relativePath === ".." ||
|
|
1025
|
+
relativePath.startsWith(`..${path.sep}`) ||
|
|
1026
|
+
path.isAbsolute(relativePath)) {
|
|
1027
|
+
throw new Error(`Cannot change a path outside the app root: ${filePath}`);
|
|
1028
|
+
}
|
|
1029
|
+
if (change.content === undefined && relativePath === "") {
|
|
1030
|
+
throw new Error("Cannot delete the app root");
|
|
1031
|
+
}
|
|
1032
|
+
return { ...change, filePath: normalizePath(relativePath) };
|
|
1033
|
+
});
|
|
808
1034
|
await this.fsOperation(async () => {
|
|
809
|
-
const
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1035
|
+
const requestedPaths = new Set();
|
|
1036
|
+
for (const change of normalized) {
|
|
1037
|
+
if (requestedPaths.has(change.filePath)) {
|
|
1038
|
+
throw new Error(`Cannot change ${change.filePath} more than once in the same batch`);
|
|
1039
|
+
}
|
|
1040
|
+
requestedPaths.add(change.filePath);
|
|
1041
|
+
}
|
|
1042
|
+
const expanded = [];
|
|
1043
|
+
const directories = new Set();
|
|
1044
|
+
const realAppRootDirPath = await fs.realpath(this.appRootDirPath);
|
|
1045
|
+
for (const change of normalized) {
|
|
1046
|
+
if (change.content !== undefined) {
|
|
1047
|
+
expanded.push(change);
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
const fullPath = path.resolve(this.appRootDirPath, change.filePath);
|
|
1051
|
+
let stats;
|
|
1052
|
+
try {
|
|
1053
|
+
stats = await fs.lstat(fullPath);
|
|
1054
|
+
}
|
|
1055
|
+
catch (error) {
|
|
1056
|
+
if (error instanceof Error &&
|
|
1057
|
+
"code" in error &&
|
|
1058
|
+
error.code === "ENOENT") {
|
|
1059
|
+
expanded.push(change);
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
throw error;
|
|
1063
|
+
}
|
|
1064
|
+
if (stats.isSymbolicLink()) {
|
|
1065
|
+
throw new Error(`Cannot delete symbolic link: ${change.filePath}`);
|
|
1066
|
+
}
|
|
1067
|
+
if (!stats.isFile() && !stats.isDirectory()) {
|
|
1068
|
+
throw new Error(`Cannot delete unsupported file type: ${change.filePath}`);
|
|
1069
|
+
}
|
|
1070
|
+
// `lstat` only describes the last segment of the path. A parent
|
|
1071
|
+
// directory can still be a symbolic link pointing somewhere else, and
|
|
1072
|
+
// deleting through it would remove a file outside the app root.
|
|
1073
|
+
const realPath = await fs.realpath(fullPath);
|
|
1074
|
+
const realRelativePath = path.relative(realAppRootDirPath, realPath);
|
|
1075
|
+
if (realRelativePath === ".." ||
|
|
1076
|
+
realRelativePath.startsWith(`..${path.sep}`) ||
|
|
1077
|
+
path.isAbsolute(realRelativePath)) {
|
|
1078
|
+
throw new Error(`Cannot delete a path outside the app root: ${change.filePath}`);
|
|
1079
|
+
}
|
|
1080
|
+
if (!stats.isDirectory()) {
|
|
1081
|
+
expanded.push(change);
|
|
1082
|
+
continue;
|
|
1083
|
+
}
|
|
1084
|
+
const entries = await fs.readdir(fullPath, {
|
|
1085
|
+
recursive: true,
|
|
1086
|
+
withFileTypes: true,
|
|
1087
|
+
});
|
|
1088
|
+
const offender = entries.find((entry) => !entry.isFile() && !entry.isDirectory());
|
|
1089
|
+
if (offender) {
|
|
1090
|
+
const offenderPath = normalizePath(path.relative(this.appRootDirPath, path.resolve(offender.parentPath, offender.name)));
|
|
1091
|
+
const kind = offender.isSymbolicLink()
|
|
1092
|
+
? "a symbolic link"
|
|
1093
|
+
: "an unsupported file type";
|
|
1094
|
+
throw new Error(`Cannot delete ${change.filePath}: it contains ${kind} at ${offenderPath}`);
|
|
1095
|
+
}
|
|
1096
|
+
directories.add(fullPath);
|
|
1097
|
+
for (const entry of entries) {
|
|
1098
|
+
const entryPath = path.resolve(entry.parentPath, entry.name);
|
|
1099
|
+
if (entry.isDirectory())
|
|
1100
|
+
directories.add(entryPath);
|
|
1101
|
+
if (entry.isFile()) {
|
|
1102
|
+
expanded.push({
|
|
1103
|
+
filePath: normalizePath(path.relative(this.appRootDirPath, entryPath)),
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
814
1107
|
}
|
|
815
|
-
|
|
816
|
-
|
|
1108
|
+
const seen = new Set();
|
|
1109
|
+
for (const change of expanded) {
|
|
1110
|
+
if (seen.has(change.filePath)) {
|
|
1111
|
+
throw new Error(`Cannot change ${change.filePath} more than once in the same batch`);
|
|
1112
|
+
}
|
|
1113
|
+
seen.add(change.filePath);
|
|
817
1114
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1115
|
+
for (const change of expanded) {
|
|
1116
|
+
if (change.content === undefined)
|
|
1117
|
+
continue;
|
|
1118
|
+
const fullPath = path.resolve(this.appRootDirPath, change.filePath);
|
|
1119
|
+
if ([...directories].some((directory) => fullPath === directory ||
|
|
1120
|
+
fullPath.startsWith(`${directory}${path.sep}`))) {
|
|
1121
|
+
throw new Error(`Cannot write ${change.filePath} because its directory is being deleted`);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (this.pageReferenceResolver) {
|
|
1125
|
+
await validatePageReferences({
|
|
1126
|
+
appRoot: this.appRootDirPath,
|
|
1127
|
+
changes: expanded,
|
|
1128
|
+
resolver: this.pageReferenceResolver,
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
let mutationStarted = false;
|
|
1132
|
+
try {
|
|
1133
|
+
for (const change of expanded) {
|
|
1134
|
+
const fullPath = path.join(this.appRootDirPath, change.filePath);
|
|
1135
|
+
mutationStarted = true;
|
|
1136
|
+
if (change.content === undefined) {
|
|
1137
|
+
await createDraftDelete(fullPath, this.appRootDirPath);
|
|
1138
|
+
this.getLogger().info(`[sync-service] Draft deletion created for: ${change.filePath}`);
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
const type = await createDraft(fullPath, change.content, this.appRootDirPath);
|
|
1142
|
+
this.getLogger().info(`[sync-service] Draft ${type === "edit" ? "edit" : "addition"} created for: ${change.filePath}`);
|
|
1143
|
+
this._moduleInvalidationCallback?.(change.filePath);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
for (const directory of [...directories].sort((left, right) => right.length - left.length)) {
|
|
1147
|
+
mutationStarted = true;
|
|
1148
|
+
await fs.rmdir(directory);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
finally {
|
|
1152
|
+
if (mutationStarted && (await hasSwapFiles(this.appRootDirPath))) {
|
|
1153
|
+
this.notifyDraftStateChanged(true);
|
|
1154
|
+
}
|
|
822
1155
|
}
|
|
823
1156
|
});
|
|
824
|
-
this.getLogger().info("[sync-service] Draft file created", {
|
|
825
|
-
filePath: normalizedFilePath,
|
|
826
|
-
});
|
|
827
|
-
this.notifyDraftStateChanged(true);
|
|
828
1157
|
}
|
|
829
1158
|
/**
|
|
830
1159
|
* Creates the marker file only for a draft edit or addition.
|
|
@@ -847,11 +1176,7 @@ export class SyncService extends TracedEventEmitter {
|
|
|
847
1176
|
* @returns A promise that resolves when the draft deletion is created.
|
|
848
1177
|
*/
|
|
849
1178
|
async createDraftDelete(filePath) {
|
|
850
|
-
await this.
|
|
851
|
-
const fullPath = `${this.appRootDirPath}/${filePath}`;
|
|
852
|
-
await createDraftDelete(fullPath, this.appRootDirPath);
|
|
853
|
-
});
|
|
854
|
-
this.notifyDraftStateChanged(true);
|
|
1179
|
+
await this.applyDraftChanges([{ filePath }]);
|
|
855
1180
|
}
|
|
856
1181
|
/**
|
|
857
1182
|
* Discards all local draft changes by restoring original files and removing draft additions.
|
|
@@ -866,7 +1191,11 @@ export class SyncService extends TracedEventEmitter {
|
|
|
866
1191
|
this.getLogger().info("[sync-service] Local draft changes discarded");
|
|
867
1192
|
});
|
|
868
1193
|
await this.uploadDirectory(source);
|
|
869
|
-
|
|
1194
|
+
// A reject is a click in the editor, so it may take back a lock this
|
|
1195
|
+
// session handed back (ENG-6808).
|
|
1196
|
+
await this.uploadDirectoryNowIfNeeded(source, {
|
|
1197
|
+
reclaimAfterHandback: true,
|
|
1198
|
+
});
|
|
870
1199
|
this.notifyDraftStateChanged(false);
|
|
871
1200
|
}
|
|
872
1201
|
/**
|
|
@@ -889,6 +1218,11 @@ export class SyncService extends TracedEventEmitter {
|
|
|
889
1218
|
});
|
|
890
1219
|
const uploadResult = await this.uploadCommittedDraftForSource(source, checkpointAttemptId);
|
|
891
1220
|
const result = uploadResult.result;
|
|
1221
|
+
// commitAllDrafts above already removed the local markers, so there is no
|
|
1222
|
+
// draft on disk regardless of how the checkpoint turned out. Report that
|
|
1223
|
+
// before classifying the result: a server left on isDraft=true blocks git
|
|
1224
|
+
// sync until the next reconnect. The rejection below still happens before
|
|
1225
|
+
// the RPC caller advances the chat or waits on this report.
|
|
892
1226
|
this.notifyDraftStateChanged(false);
|
|
893
1227
|
this.getLogger().info(`[sync-service] commitLocalDraftChanges upload result commitId=${result.commitId ?? "none"}`);
|
|
894
1228
|
if (source === "ai:accept") {
|
|
@@ -900,6 +1234,13 @@ export class SyncService extends TracedEventEmitter {
|
|
|
900
1234
|
commitId: result.commitId,
|
|
901
1235
|
outcome: this.aiAcceptCheckpointOutcomeFromLog(checkpointLog),
|
|
902
1236
|
});
|
|
1237
|
+
// A missing commitId is a failed checkpoint unless this drain never
|
|
1238
|
+
// reached the server (localNoPending). Hash-only server responses,
|
|
1239
|
+
// including blocked Superblocks users, have neither commitId nor a
|
|
1240
|
+
// by-design skip reason and must reject.
|
|
1241
|
+
if (checkpointLog.level === "warn" && !result.localNoPending) {
|
|
1242
|
+
throw new Error("AI accept checkpoint upload completed without a commit ID");
|
|
1243
|
+
}
|
|
903
1244
|
}
|
|
904
1245
|
if (result.commitId) {
|
|
905
1246
|
return { commitId: result.commitId };
|
|
@@ -913,7 +1254,12 @@ export class SyncService extends TracedEventEmitter {
|
|
|
913
1254
|
}
|
|
914
1255
|
async uploadCommittedDraft(source) {
|
|
915
1256
|
await this.uploadDirectory(source);
|
|
916
|
-
|
|
1257
|
+
// An accept is a click in the editor, so it may take back a lock this
|
|
1258
|
+
// session handed back (ENG-6808). This is the write the reported session
|
|
1259
|
+
// lost: the user came back and hit Accept against a lock nobody held.
|
|
1260
|
+
return await this.uploadDirectoryNowIfNeeded(source, {
|
|
1261
|
+
reclaimAfterHandback: true,
|
|
1262
|
+
});
|
|
917
1263
|
}
|
|
918
1264
|
async uploadCommittedDraftWithRetry(source, checkpointAttemptId) {
|
|
919
1265
|
for (let attempt = 1;; attempt++) {
|