@opendatalabs/personal-server-ts-core 0.2.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.d.ts +160 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +774 -0
- package/dist/api/index.js.map +1 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/request.d.ts +28 -0
- package/dist/auth/request.d.ts.map +1 -0
- package/dist/auth/request.js +106 -0
- package/dist/auth/request.js.map +1 -0
- package/dist/client.d.ts +233 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +243 -0
- package/dist/client.js.map +1 -0
- package/dist/config/defaults.d.ts +5 -5
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +3 -5
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +0 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +1 -9
- package/dist/config/paths.js.map +1 -1
- package/dist/contracts/access-logs.d.ts +8 -0
- package/dist/contracts/access-logs.d.ts.map +1 -0
- package/dist/contracts/access-logs.js +7 -0
- package/dist/contracts/access-logs.js.map +1 -0
- package/dist/contracts/auth-device.d.ts +70 -0
- package/dist/contracts/auth-device.d.ts.map +1 -0
- package/dist/contracts/auth-device.js +163 -0
- package/dist/contracts/auth-device.js.map +1 -0
- package/dist/contracts/binary.d.ts +54 -0
- package/dist/contracts/binary.d.ts.map +1 -0
- package/dist/contracts/binary.js +92 -0
- package/dist/contracts/binary.js.map +1 -0
- package/dist/contracts/config.d.ts +5 -0
- package/dist/contracts/config.d.ts.map +1 -0
- package/dist/contracts/config.js +30 -0
- package/dist/contracts/config.js.map +1 -0
- package/dist/contracts/data.d.ts +115 -0
- package/dist/contracts/data.d.ts.map +1 -0
- package/dist/contracts/data.js +237 -0
- package/dist/contracts/data.js.map +1 -0
- package/dist/contracts/grants.d.ts +45 -0
- package/dist/contracts/grants.d.ts.map +1 -0
- package/dist/contracts/grants.js +177 -0
- package/dist/contracts/grants.js.map +1 -0
- package/dist/contracts/http.d.ts +21 -0
- package/dist/contracts/http.d.ts.map +1 -0
- package/dist/contracts/http.js +49 -0
- package/dist/contracts/http.js.map +1 -0
- package/dist/contracts/index.d.ts +16 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +10 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/oauth.d.ts +38 -0
- package/dist/contracts/oauth.d.ts.map +1 -0
- package/dist/contracts/oauth.js +130 -0
- package/dist/contracts/oauth.js.map +1 -0
- package/dist/contracts/sync.d.ts +10 -0
- package/dist/contracts/sync.d.ts.map +1 -0
- package/dist/contracts/sync.js +34 -0
- package/dist/contracts/sync.js.map +1 -0
- package/dist/errors/catalog.d.ts +9 -0
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/catalog.js +15 -0
- package/dist/errors/catalog.js.map +1 -1
- package/dist/grants/index.d.ts +2 -4
- package/dist/grants/index.d.ts.map +1 -1
- package/dist/grants/index.js +1 -2
- package/dist/grants/index.js.map +1 -1
- package/dist/keys/index.d.ts +1 -2
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +1 -2
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/server-account.d.ts +5 -9
- package/dist/keys/server-account.d.ts.map +1 -1
- package/dist/keys/server-account.js +1 -47
- package/dist/keys/server-account.js.map +1 -1
- package/dist/logger/index.d.ts +6 -2
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +2 -6
- package/dist/logger/index.js.map +1 -1
- package/dist/logging/access-log.d.ts +0 -1
- package/dist/logging/access-log.d.ts.map +1 -1
- package/dist/logging/access-log.js +1 -24
- package/dist/logging/access-log.js.map +1 -1
- package/dist/logging/access-reader.d.ts +0 -5
- package/dist/logging/access-reader.d.ts.map +1 -1
- package/dist/logging/access-reader.js +1 -49
- package/dist/logging/access-reader.js.map +1 -1
- package/dist/mcp/activity.d.ts +68 -0
- package/dist/mcp/activity.d.ts.map +1 -0
- package/dist/mcp/activity.js +111 -0
- package/dist/mcp/activity.js.map +1 -0
- package/dist/mcp/builder-registration.d.ts +24 -0
- package/dist/mcp/builder-registration.d.ts.map +1 -0
- package/dist/mcp/builder-registration.js +89 -0
- package/dist/mcp/builder-registration.js.map +1 -0
- package/dist/mcp/connection-api.d.ts +170 -0
- package/dist/mcp/connection-api.d.ts.map +1 -0
- package/dist/mcp/connection-api.js +379 -0
- package/dist/mcp/connection-api.js.map +1 -0
- package/dist/mcp/grantee.d.ts +52 -0
- package/dist/mcp/grantee.d.ts.map +1 -0
- package/dist/mcp/grantee.js +91 -0
- package/dist/mcp/grantee.js.map +1 -0
- package/dist/mcp/index.d.ts +10 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +10 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/read-client.d.ts +136 -0
- package/dist/mcp/read-client.d.ts.map +1 -0
- package/dist/mcp/read-client.js +306 -0
- package/dist/mcp/read-client.js.map +1 -0
- package/dist/mcp/resources.d.ts +37 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +114 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/search/index.d.ts +34 -0
- package/dist/mcp/search/index.d.ts.map +1 -0
- package/dist/mcp/search/index.js +69 -0
- package/dist/mcp/search/index.js.map +1 -0
- package/dist/mcp/server.d.ts +53 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +308 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/store.d.ts +13 -0
- package/dist/mcp/store.d.ts.map +1 -0
- package/dist/mcp/store.js +98 -0
- package/dist/mcp/store.js.map +1 -0
- package/dist/mcp/tools.d.ts +60 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +1058 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/types.d.ts +97 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +24 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/payment/index.d.ts +2 -0
- package/dist/payment/index.d.ts.map +1 -0
- package/dist/payment/index.js +2 -0
- package/dist/payment/index.js.map +1 -0
- package/dist/payment/x402.d.ts +155 -0
- package/dist/payment/x402.d.ts.map +1 -0
- package/dist/payment/x402.js +402 -0
- package/dist/payment/x402.js.map +1 -0
- package/dist/policy/data-read.d.ts +15 -0
- package/dist/policy/data-read.d.ts.map +1 -0
- package/dist/policy/data-read.js +84 -0
- package/dist/policy/data-read.js.map +1 -0
- package/dist/policy/index.d.ts +2 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +2 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/ports/index.d.ts +95 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +7 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/schemas/server-config.d.ts +12 -0
- package/dist/schemas/server-config.d.ts.map +1 -1
- package/dist/schemas/server-config.js +34 -4
- package/dist/schemas/server-config.js.map +1 -1
- package/dist/signing/index.d.ts +0 -1
- package/dist/signing/index.d.ts.map +1 -1
- package/dist/signing/index.js +0 -1
- package/dist/signing/index.js.map +1 -1
- package/dist/signing/request-signer.d.ts +8 -2
- package/dist/signing/request-signer.d.ts.map +1 -1
- package/dist/signing/request-signer.js +5 -31
- package/dist/signing/request-signer.js.map +1 -1
- package/dist/signing/signer.d.ts +20 -2
- package/dist/signing/signer.d.ts.map +1 -1
- package/dist/signing/signer.js +23 -13
- package/dist/signing/signer.js.map +1 -1
- package/dist/storage/adapters/index.d.ts +2 -1
- package/dist/storage/adapters/index.d.ts.map +1 -1
- package/dist/storage/adapters/index.js +2 -1
- package/dist/storage/adapters/index.js.map +1 -1
- package/dist/storage/adapters/interface.d.ts +10 -0
- package/dist/storage/adapters/interface.d.ts.map +1 -1
- package/dist/storage/adapters/sdk.d.ts +23 -0
- package/dist/storage/adapters/sdk.d.ts.map +1 -0
- package/dist/storage/adapters/sdk.js +73 -0
- package/dist/storage/adapters/sdk.js.map +1 -0
- package/dist/storage/adapters/vana.d.ts +7 -30
- package/dist/storage/adapters/vana.d.ts.map +1 -1
- package/dist/storage/adapters/vana.js +37 -92
- package/dist/storage/adapters/vana.js.map +1 -1
- package/dist/storage/blocks/build.d.ts +20 -0
- package/dist/storage/blocks/build.d.ts.map +1 -0
- package/dist/storage/blocks/build.js +378 -0
- package/dist/storage/blocks/build.js.map +1 -0
- package/dist/storage/blocks/cursor.d.ts +33 -0
- package/dist/storage/blocks/cursor.d.ts.map +1 -0
- package/dist/storage/blocks/cursor.js +142 -0
- package/dist/storage/blocks/cursor.js.map +1 -0
- package/dist/storage/blocks/errors.d.ts +6 -0
- package/dist/storage/blocks/errors.d.ts.map +1 -0
- package/dist/storage/blocks/errors.js +9 -0
- package/dist/storage/blocks/errors.js.map +1 -0
- package/dist/storage/blocks/index.d.ts +5 -0
- package/dist/storage/blocks/index.d.ts.map +1 -0
- package/dist/storage/blocks/index.js +4 -0
- package/dist/storage/blocks/index.js.map +1 -0
- package/dist/storage/blocks/types.d.ts +49 -0
- package/dist/storage/blocks/types.d.ts.map +1 -0
- package/dist/storage/blocks/types.js +2 -0
- package/dist/{grants → storage/blocks}/types.js.map +1 -1
- package/dist/storage/hierarchy/index.d.ts +0 -1
- package/dist/storage/hierarchy/index.d.ts.map +1 -1
- package/dist/storage/hierarchy/index.js +0 -1
- package/dist/storage/hierarchy/index.js.map +1 -1
- package/dist/storage/hierarchy/manager.d.ts +0 -14
- package/dist/storage/hierarchy/manager.d.ts.map +1 -1
- package/dist/storage/hierarchy/manager.js +1 -59
- package/dist/storage/hierarchy/manager.js.map +1 -1
- package/dist/storage/hierarchy/paths.d.ts.map +1 -1
- package/dist/storage/hierarchy/paths.js +11 -4
- package/dist/storage/hierarchy/paths.js.map +1 -1
- package/dist/storage/index/index.d.ts +2 -3
- package/dist/storage/index/index.d.ts.map +1 -1
- package/dist/storage/index/index.js +1 -2
- package/dist/storage/index/index.js.map +1 -1
- package/dist/storage/index/manager.d.ts +26 -6
- package/dist/storage/index/manager.d.ts.map +1 -1
- package/dist/storage/index/manager.js +1 -131
- package/dist/storage/index/manager.js.map +1 -1
- package/dist/storage/index/schema.d.ts +1 -3
- package/dist/storage/index/schema.d.ts.map +1 -1
- package/dist/storage/index/schema.js +1 -26
- package/dist/storage/index/schema.js.map +1 -1
- package/dist/storage/index/types.d.ts +16 -0
- package/dist/storage/index/types.d.ts.map +1 -1
- package/dist/storage/preview.d.ts +12 -0
- package/dist/storage/preview.d.ts.map +1 -0
- package/dist/storage/preview.js +239 -0
- package/dist/storage/preview.js.map +1 -0
- package/dist/sync/cursor.d.ts +5 -7
- package/dist/sync/cursor.d.ts.map +1 -1
- package/dist/sync/cursor.js +1 -18
- package/dist/sync/cursor.js.map +1 -1
- package/dist/sync/engine/sync-manager.d.ts +17 -0
- package/dist/sync/engine/sync-manager.d.ts.map +1 -1
- package/dist/sync/engine/sync-manager.js +104 -43
- package/dist/sync/engine/sync-manager.js.map +1 -1
- package/dist/sync/index.d.ts +4 -2
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/issues.d.ts +55 -0
- package/dist/sync/issues.d.ts.map +1 -0
- package/dist/sync/issues.js +168 -0
- package/dist/sync/issues.js.map +1 -0
- package/dist/sync/retry-memory.d.ts +33 -0
- package/dist/sync/retry-memory.d.ts.map +1 -0
- package/dist/sync/retry-memory.js +68 -0
- package/dist/sync/retry-memory.js.map +1 -0
- package/dist/sync/types.d.ts +9 -24
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/workers/delete.d.ts +42 -0
- package/dist/sync/workers/delete.d.ts.map +1 -0
- package/dist/sync/workers/delete.js +40 -0
- package/dist/sync/workers/delete.js.map +1 -0
- package/dist/sync/workers/download.d.ts +64 -18
- package/dist/sync/workers/download.d.ts.map +1 -1
- package/dist/sync/workers/download.js +396 -54
- package/dist/sync/workers/download.js.map +1 -1
- package/dist/sync/workers/upload.d.ts +29 -18
- package/dist/sync/workers/upload.d.ts.map +1 -1
- package/dist/sync/workers/upload.js +163 -46
- package/dist/sync/workers/upload.js.map +1 -1
- package/dist/test-utils/wallet.d.ts +1 -0
- package/dist/test-utils/wallet.d.ts.map +1 -1
- package/dist/test-utils/wallet.js +9 -31
- package/dist/test-utils/wallet.js.map +1 -1
- package/package.json +51 -27
- package/dist/auth/web3-signed.d.ts +0 -48
- package/dist/auth/web3-signed.d.ts.map +0 -1
- package/dist/auth/web3-signed.js +0 -110
- package/dist/auth/web3-signed.js.map +0 -1
- package/dist/config/loader.d.ts +0 -8
- package/dist/config/loader.d.ts.map +0 -1
- package/dist/config/loader.js +0 -38
- package/dist/config/loader.js.map +0 -1
- package/dist/gateway/client.d.ts +0 -98
- package/dist/gateway/client.d.ts.map +0 -1
- package/dist/gateway/client.js +0 -171
- package/dist/gateway/client.js.map +0 -1
- package/dist/grants/eip712.d.ts +0 -28
- package/dist/grants/eip712.d.ts.map +0 -1
- package/dist/grants/eip712.js +0 -30
- package/dist/grants/eip712.js.map +0 -1
- package/dist/grants/types.d.ts +0 -29
- package/dist/grants/types.d.ts.map +0 -1
- package/dist/grants/types.js +0 -6
- package/dist/grants/verify.d.ts +0 -24
- package/dist/grants/verify.d.ts.map +0 -1
- package/dist/grants/verify.js +0 -71
- package/dist/grants/verify.js.map +0 -1
- package/dist/keys/derive.d.ts +0 -18
- package/dist/keys/derive.d.ts.map +0 -1
- package/dist/keys/derive.js +0 -43
- package/dist/keys/derive.js.map +0 -1
- package/dist/schemas/data-file.d.ts +0 -20
- package/dist/schemas/data-file.d.ts.map +0 -1
- package/dist/schemas/data-file.js +0 -23
- package/dist/schemas/data-file.js.map +0 -1
- package/dist/scopes/index.d.ts +0 -3
- package/dist/scopes/index.d.ts.map +0 -1
- package/dist/scopes/index.js +0 -3
- package/dist/scopes/index.js.map +0 -1
- package/dist/scopes/match.d.ts +0 -13
- package/dist/scopes/match.d.ts.map +0 -1
- package/dist/scopes/match.js +0 -23
- package/dist/scopes/match.js.map +0 -1
- package/dist/scopes/parse.d.ts +0 -12
- package/dist/scopes/parse.d.ts.map +0 -1
- package/dist/scopes/parse.js +0 -29
- package/dist/scopes/parse.js.map +0 -1
- package/dist/signing/eip712.d.ts +0 -69
- package/dist/signing/eip712.d.ts.map +0 -1
- package/dist/signing/eip712.js +0 -68
- package/dist/signing/eip712.js.map +0 -1
- package/dist/storage/encryption/decrypt.d.ts +0 -10
- package/dist/storage/encryption/decrypt.d.ts.map +0 -1
- package/dist/storage/encryption/decrypt.js +0 -19
- package/dist/storage/encryption/decrypt.js.map +0 -1
- package/dist/storage/encryption/encrypt.d.ts +0 -10
- package/dist/storage/encryption/encrypt.d.ts.map +0 -1
- package/dist/storage/encryption/encrypt.js +0 -19
- package/dist/storage/encryption/encrypt.js.map +0 -1
- package/dist/storage/encryption/index.d.ts +0 -3
- package/dist/storage/encryption/index.d.ts.map +0 -1
- package/dist/storage/encryption/index.js +0 -3
- package/dist/storage/encryption/index.js.map +0 -1
|
@@ -1,61 +1,96 @@
|
|
|
1
1
|
import { uploadAll } from "../workers/upload.js";
|
|
2
2
|
import { downloadAll } from "../workers/download.js";
|
|
3
|
+
import { deleteScopeRemote } from "../workers/delete.js";
|
|
4
|
+
import { createDownloadRetryMemory } from "../retry-memory.js";
|
|
3
5
|
const MAX_ERRORS = 10;
|
|
4
6
|
export function createSyncManager(uploadDeps, downloadDeps, options) {
|
|
5
7
|
const pollInterval = options?.pollInterval ?? 60_000;
|
|
6
8
|
const uploadBatchSize = options?.uploadBatchSize ?? 50;
|
|
9
|
+
const notifyDebounceMs = options?.notifyDebounceMs ?? 500;
|
|
7
10
|
let intervalId = null;
|
|
11
|
+
let notifyTimerId = null;
|
|
8
12
|
let isRunning = false;
|
|
9
13
|
let lastSync = null;
|
|
10
14
|
let lastProcessedTimestamp = null;
|
|
15
|
+
let blocked = null;
|
|
11
16
|
let errors = [];
|
|
12
17
|
let cycleInFlight = null;
|
|
18
|
+
let rerunRequested = false;
|
|
19
|
+
let needsFullReconcile = true;
|
|
20
|
+
// One retry memory per manager (= per boot session): a blob that 404s is
|
|
21
|
+
// attempted once per session, not once per cycle; transient storage errors
|
|
22
|
+
// back off exponentially instead of re-firing on every cycle.
|
|
23
|
+
const downloadRetryMemory = createDownloadRetryMemory();
|
|
13
24
|
async function runCycle() {
|
|
14
25
|
// Prevent concurrent cycles
|
|
15
26
|
if (cycleInFlight) {
|
|
27
|
+
rerunRequested = true;
|
|
16
28
|
return cycleInFlight;
|
|
17
29
|
}
|
|
18
30
|
cycleInFlight = (async () => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
31
|
+
do {
|
|
32
|
+
rerunRequested = false;
|
|
33
|
+
const canRun = await (options?.canSync?.() ?? { ok: true });
|
|
34
|
+
if (!canRun.ok) {
|
|
35
|
+
blocked = { reason: canRun.reason, message: canRun.message };
|
|
36
|
+
uploadDeps.logger.warn(blocked, "Sync cycle blocked");
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
blocked = null;
|
|
40
|
+
try {
|
|
41
|
+
// Upload unsynced local files
|
|
42
|
+
const uploadResults = await uploadAll(uploadDeps, {
|
|
43
|
+
batchSize: uploadBatchSize,
|
|
44
|
+
onError(entry, error) {
|
|
45
|
+
pushError({
|
|
46
|
+
fileId: entry.fileId,
|
|
47
|
+
scope: entry.scope,
|
|
48
|
+
message: `Upload failed for ${entry.path}: ${error.message}`,
|
|
49
|
+
timestamp: new Date().toISOString(),
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
uploadDeps.logger.debug({ uploaded: uploadResults.length }, "Upload cycle complete");
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
const syncError = {
|
|
57
|
+
fileId: null,
|
|
58
|
+
scope: null,
|
|
59
|
+
message: `Upload cycle failed: ${err.message}`,
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
};
|
|
62
|
+
pushError(syncError);
|
|
63
|
+
uploadDeps.logger.error({ error: err.message }, "Upload cycle failed");
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
// Download new remote files
|
|
67
|
+
const fullReconcile = needsFullReconcile;
|
|
68
|
+
const downloadResults = await downloadAll(downloadDeps, {
|
|
69
|
+
fullReconcile,
|
|
70
|
+
retryMemory: downloadRetryMemory,
|
|
71
|
+
});
|
|
72
|
+
needsFullReconcile = false;
|
|
73
|
+
downloadDeps.logger.debug({ downloaded: downloadResults.length, fullReconcile }, "Download cycle complete");
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
const syncError = {
|
|
77
|
+
fileId: null,
|
|
78
|
+
scope: null,
|
|
79
|
+
message: `Download cycle failed: ${err.message}`,
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
};
|
|
82
|
+
pushError(syncError);
|
|
83
|
+
downloadDeps.logger.error({ error: err.message }, "Download cycle failed");
|
|
84
|
+
}
|
|
85
|
+
// Update cached cursor value
|
|
86
|
+
try {
|
|
87
|
+
lastProcessedTimestamp = await downloadDeps.cursor.read();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Non-critical: status will show stale value
|
|
91
|
+
}
|
|
92
|
+
lastSync = new Date().toISOString();
|
|
93
|
+
} while (rerunRequested && isRunning);
|
|
59
94
|
})();
|
|
60
95
|
try {
|
|
61
96
|
await cycleInFlight;
|
|
@@ -64,6 +99,18 @@ export function createSyncManager(uploadDeps, downloadDeps, options) {
|
|
|
64
99
|
cycleInFlight = null;
|
|
65
100
|
}
|
|
66
101
|
}
|
|
102
|
+
function scheduleNotifiedCycle() {
|
|
103
|
+
if (!isRunning || notifyTimerId !== null)
|
|
104
|
+
return;
|
|
105
|
+
notifyTimerId = setTimeout(() => {
|
|
106
|
+
notifyTimerId = null;
|
|
107
|
+
if (!isRunning)
|
|
108
|
+
return;
|
|
109
|
+
runCycle().catch(() => {
|
|
110
|
+
// Errors already captured in ring buffer
|
|
111
|
+
});
|
|
112
|
+
}, notifyDebounceMs);
|
|
113
|
+
}
|
|
67
114
|
function pushError(error) {
|
|
68
115
|
errors.push(error);
|
|
69
116
|
if (errors.length > MAX_ERRORS) {
|
|
@@ -85,6 +132,12 @@ export function createSyncManager(uploadDeps, downloadDeps, options) {
|
|
|
85
132
|
intervalId = null;
|
|
86
133
|
}
|
|
87
134
|
}
|
|
135
|
+
function clearNotifyTimer() {
|
|
136
|
+
if (notifyTimerId !== null) {
|
|
137
|
+
clearTimeout(notifyTimerId);
|
|
138
|
+
notifyTimerId = null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
88
141
|
const manager = {
|
|
89
142
|
get running() {
|
|
90
143
|
return isRunning;
|
|
@@ -101,6 +154,7 @@ export function createSyncManager(uploadDeps, downloadDeps, options) {
|
|
|
101
154
|
},
|
|
102
155
|
async stop() {
|
|
103
156
|
clearIntervalTimer();
|
|
157
|
+
clearNotifyTimer();
|
|
104
158
|
isRunning = false;
|
|
105
159
|
// Wait for any in-flight cycle to complete
|
|
106
160
|
if (cycleInFlight) {
|
|
@@ -108,18 +162,21 @@ export function createSyncManager(uploadDeps, downloadDeps, options) {
|
|
|
108
162
|
}
|
|
109
163
|
},
|
|
110
164
|
async trigger() {
|
|
111
|
-
// Clear existing
|
|
165
|
+
// Clear existing timers, run immediately, restart interval
|
|
112
166
|
clearIntervalTimer();
|
|
167
|
+
clearNotifyTimer();
|
|
113
168
|
await runCycle();
|
|
114
169
|
if (isRunning) {
|
|
115
170
|
startInterval();
|
|
116
171
|
}
|
|
117
172
|
},
|
|
118
173
|
getStatus() {
|
|
119
|
-
const pendingFiles = uploadDeps.
|
|
174
|
+
const pendingFiles = uploadDeps.storage.findUnsynced().length;
|
|
120
175
|
return {
|
|
121
176
|
enabled: true,
|
|
122
177
|
running: isRunning,
|
|
178
|
+
syncing: cycleInFlight !== null,
|
|
179
|
+
blocked,
|
|
123
180
|
lastSync,
|
|
124
181
|
lastProcessedTimestamp,
|
|
125
182
|
pendingFiles,
|
|
@@ -127,8 +184,12 @@ export function createSyncManager(uploadDeps, downloadDeps, options) {
|
|
|
127
184
|
};
|
|
128
185
|
},
|
|
129
186
|
notifyNewData() {
|
|
130
|
-
// No-op signal — the next cycle picks up unsynced entries automatically.
|
|
131
187
|
uploadDeps.logger.debug("New data notification received");
|
|
188
|
+
scheduleNotifiedCycle();
|
|
189
|
+
},
|
|
190
|
+
async deleteScopeRemote(scope) {
|
|
191
|
+
// Reuses the upload worker's remote deps (gateway, storage adapter, signer, owner).
|
|
192
|
+
await deleteScopeRemote(uploadDeps, scope);
|
|
132
193
|
},
|
|
133
194
|
};
|
|
134
195
|
return manager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-manager.js","sourceRoot":"","sources":["../../../src/sync/engine/sync-manager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"sync-manager.js","sourceRoot":"","sources":["../../../src/sync/engine/sync-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
export {
|
|
1
|
+
export type { SyncStatus, SyncError } from "./types.js";
|
|
2
|
+
export type { SyncCursor } from "./cursor.js";
|
|
3
|
+
export { classifySyncFailure, inferPayloadKind, type ClassifiedSyncFailure, type ClassifySyncFailureInput, type SyncDownloadFailureTelemetryEvent, type SyncFailureDisposition, type SyncFailureStage, type SyncFileIssue, type SyncPayloadKind, } from "./issues.js";
|
|
3
4
|
export { createSyncManager, type SyncManager, type SyncManagerOptions, } from "./engine/sync-manager.js";
|
|
5
|
+
export type { DownloadDiagnosticsHook, DownloadWorkerDeps, } from "./workers/download.js";
|
|
4
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/sync/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,iCAAiC,EACtC,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC"}
|
package/dist/sync/index.js
CHANGED
package/dist/sync/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,GAQjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,GAGlB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type SyncFailureStage = "download" | "openpgp_parse" | "decrypt" | "json_parse" | "envelope_validate" | "block_build" | "unknown";
|
|
2
|
+
export type SyncPayloadKind = "openpgp" | "json" | "html" | "empty" | "binary" | "unknown";
|
|
3
|
+
export type SyncFailureDisposition = "deterministic" | "transient";
|
|
4
|
+
export interface SyncFileIssue {
|
|
5
|
+
fileId: string;
|
|
6
|
+
schemaId?: string;
|
|
7
|
+
scope?: string;
|
|
8
|
+
stage: SyncFailureStage;
|
|
9
|
+
errorClass: string;
|
|
10
|
+
message: string;
|
|
11
|
+
payloadKind?: SyncPayloadKind;
|
|
12
|
+
encryptedSizeBytes?: number;
|
|
13
|
+
syncRunId: string;
|
|
14
|
+
firstSeenAt: string;
|
|
15
|
+
lastSeenAt: string;
|
|
16
|
+
disposition: SyncFailureDisposition;
|
|
17
|
+
retryable: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface SyncDownloadFailureTelemetryEvent {
|
|
20
|
+
event: "sync.download.failure";
|
|
21
|
+
syncRunId: string;
|
|
22
|
+
fileId: string;
|
|
23
|
+
schemaId?: string;
|
|
24
|
+
scope?: string;
|
|
25
|
+
stage: SyncFailureStage;
|
|
26
|
+
payloadKind?: SyncPayloadKind;
|
|
27
|
+
encryptedSizeBytes?: number;
|
|
28
|
+
errorClass: string;
|
|
29
|
+
disposition: SyncFailureDisposition;
|
|
30
|
+
retryable: boolean;
|
|
31
|
+
appVersion?: string;
|
|
32
|
+
personalServerVersion?: string;
|
|
33
|
+
storageBackend?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ClassifySyncFailureInput {
|
|
36
|
+
error: unknown;
|
|
37
|
+
fileId: string;
|
|
38
|
+
syncRunId: string;
|
|
39
|
+
stage?: SyncFailureStage;
|
|
40
|
+
schemaId?: string;
|
|
41
|
+
scope?: string;
|
|
42
|
+
payloadKind?: SyncPayloadKind;
|
|
43
|
+
encryptedSizeBytes?: number;
|
|
44
|
+
now?: Date;
|
|
45
|
+
appVersion?: string;
|
|
46
|
+
personalServerVersion?: string;
|
|
47
|
+
storageBackend?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface ClassifiedSyncFailure {
|
|
50
|
+
issue: SyncFileIssue;
|
|
51
|
+
telemetry: SyncDownloadFailureTelemetryEvent;
|
|
52
|
+
}
|
|
53
|
+
export declare function classifySyncFailure(input: ClassifySyncFailureInput): ClassifiedSyncFailure;
|
|
54
|
+
export declare function inferPayloadKind(bytes: Uint8Array | string): SyncPayloadKind;
|
|
55
|
+
//# sourceMappingURL=issues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../../src/sync/issues.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB,UAAU,GACV,eAAe,GACf,SAAS,GACT,YAAY,GACZ,mBAAmB,GACnB,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,eAAe,GACzB,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE/D,MAAM,MAAM,sBAAsB,GAAG,eAAe,GAAG,WAAW,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,sBAAsB,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iCAAiC;IAChD,KAAK,EAAE,uBAAuB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,sBAAsB,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,iCAAiC,CAAC;CAC9C;AAiBD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,wBAAwB,GAC9B,qBAAqB,CA0CvB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,eAAe,CAgB5E"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const DETERMINISTIC_STAGES = new Set([
|
|
2
|
+
"openpgp_parse",
|
|
3
|
+
"decrypt",
|
|
4
|
+
"json_parse",
|
|
5
|
+
"envelope_validate",
|
|
6
|
+
"block_build",
|
|
7
|
+
]);
|
|
8
|
+
// Only statuses that PROVE the blob cannot appear by retrying are
|
|
9
|
+
// deterministic. Everything else — 5xx, Cloudflare 52x, 401/403 auth blips
|
|
10
|
+
// (downloads are Web3Signed off the local clock), 429 — is treated as
|
|
11
|
+
// transient and bounded by the retry memory's attempt cap instead of being
|
|
12
|
+
// permanently quarantined on first sight.
|
|
13
|
+
const DETERMINISTIC_DOWNLOAD_STATUSES = new Set([404, 410]);
|
|
14
|
+
export function classifySyncFailure(input) {
|
|
15
|
+
const stage = input.stage ?? inferStage(input.error);
|
|
16
|
+
const disposition = classifyDisposition(input.error, stage);
|
|
17
|
+
const retryable = disposition === "transient";
|
|
18
|
+
const timestamp = (input.now ?? new Date()).toISOString();
|
|
19
|
+
const errorClass = getErrorClass(input.error);
|
|
20
|
+
const message = getSafeIssueMessage(stage, disposition);
|
|
21
|
+
const issue = {
|
|
22
|
+
fileId: input.fileId,
|
|
23
|
+
schemaId: input.schemaId,
|
|
24
|
+
scope: input.scope,
|
|
25
|
+
stage,
|
|
26
|
+
errorClass,
|
|
27
|
+
message,
|
|
28
|
+
payloadKind: input.payloadKind,
|
|
29
|
+
encryptedSizeBytes: input.encryptedSizeBytes,
|
|
30
|
+
syncRunId: input.syncRunId,
|
|
31
|
+
firstSeenAt: timestamp,
|
|
32
|
+
lastSeenAt: timestamp,
|
|
33
|
+
disposition,
|
|
34
|
+
retryable,
|
|
35
|
+
};
|
|
36
|
+
const telemetry = {
|
|
37
|
+
event: "sync.download.failure",
|
|
38
|
+
syncRunId: input.syncRunId,
|
|
39
|
+
fileId: input.fileId,
|
|
40
|
+
schemaId: input.schemaId,
|
|
41
|
+
scope: input.scope,
|
|
42
|
+
stage,
|
|
43
|
+
payloadKind: input.payloadKind,
|
|
44
|
+
encryptedSizeBytes: input.encryptedSizeBytes,
|
|
45
|
+
errorClass,
|
|
46
|
+
disposition,
|
|
47
|
+
retryable,
|
|
48
|
+
appVersion: input.appVersion,
|
|
49
|
+
personalServerVersion: input.personalServerVersion,
|
|
50
|
+
storageBackend: input.storageBackend,
|
|
51
|
+
};
|
|
52
|
+
return { issue, telemetry };
|
|
53
|
+
}
|
|
54
|
+
export function inferPayloadKind(bytes) {
|
|
55
|
+
const text = typeof bytes === "string"
|
|
56
|
+
? bytes
|
|
57
|
+
: new TextDecoder().decode(bytes.slice(0, 256));
|
|
58
|
+
const trimmed = text.trimStart();
|
|
59
|
+
if (trimmed.length === 0)
|
|
60
|
+
return "empty";
|
|
61
|
+
if (trimmed.startsWith("-----BEGIN PGP MESSAGE-----"))
|
|
62
|
+
return "openpgp";
|
|
63
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("["))
|
|
64
|
+
return "json";
|
|
65
|
+
if (/^<!doctype html\b/i.test(trimmed) || /^<html[\s>]/i.test(trimmed)) {
|
|
66
|
+
return "html";
|
|
67
|
+
}
|
|
68
|
+
if (containsBinaryByte(bytes))
|
|
69
|
+
return "binary";
|
|
70
|
+
return "unknown";
|
|
71
|
+
}
|
|
72
|
+
function classifyDisposition(error, stage) {
|
|
73
|
+
if (stage === "download") {
|
|
74
|
+
const status = getNumericProperty(error, "status") ??
|
|
75
|
+
getNumericProperty(error, "statusCode") ??
|
|
76
|
+
getDownloadStatusFromMessage(error);
|
|
77
|
+
if (status !== undefined) {
|
|
78
|
+
return DETERMINISTIC_DOWNLOAD_STATUSES.has(status)
|
|
79
|
+
? "deterministic"
|
|
80
|
+
: "transient";
|
|
81
|
+
}
|
|
82
|
+
return "transient";
|
|
83
|
+
}
|
|
84
|
+
if (DETERMINISTIC_STAGES.has(stage)) {
|
|
85
|
+
return "deterministic";
|
|
86
|
+
}
|
|
87
|
+
return "transient";
|
|
88
|
+
}
|
|
89
|
+
function inferStage(error) {
|
|
90
|
+
if (error instanceof SyntaxError)
|
|
91
|
+
return "json_parse";
|
|
92
|
+
const name = getStringProperty(error, "name").toLowerCase();
|
|
93
|
+
const message = getRawMessage(error).toLowerCase();
|
|
94
|
+
if (name === "zoderror" || message.includes("zod"))
|
|
95
|
+
return "envelope_validate";
|
|
96
|
+
if (message.includes("decrypt") || message.includes("session key"))
|
|
97
|
+
return "decrypt";
|
|
98
|
+
if (message.includes("openpgp") || message.includes("pgp message")) {
|
|
99
|
+
return "openpgp_parse";
|
|
100
|
+
}
|
|
101
|
+
return "unknown";
|
|
102
|
+
}
|
|
103
|
+
function getErrorClass(error) {
|
|
104
|
+
const name = getStringProperty(error, "name");
|
|
105
|
+
if (name.length > 0)
|
|
106
|
+
return name;
|
|
107
|
+
if (error instanceof Error && error.constructor.name.length > 0) {
|
|
108
|
+
return error.constructor.name;
|
|
109
|
+
}
|
|
110
|
+
return typeof error;
|
|
111
|
+
}
|
|
112
|
+
function getRawMessage(error) {
|
|
113
|
+
if (error instanceof Error)
|
|
114
|
+
return error.message;
|
|
115
|
+
if (typeof error === "string")
|
|
116
|
+
return error;
|
|
117
|
+
const message = getStringProperty(error, "message");
|
|
118
|
+
return message.length > 0 ? message : "Unknown sync failure";
|
|
119
|
+
}
|
|
120
|
+
function getStringProperty(value, key) {
|
|
121
|
+
if (typeof value !== "object" || value === null || !(key in value))
|
|
122
|
+
return "";
|
|
123
|
+
const property = value[key];
|
|
124
|
+
return typeof property === "string" ? property : "";
|
|
125
|
+
}
|
|
126
|
+
// The SDK's StorageError carries the HTTP status only in its message —
|
|
127
|
+
// "vana-storage download failed: 404 Not Found" — with no numeric property.
|
|
128
|
+
// Without this fallback a 404'd blob classifies transient and is retried
|
|
129
|
+
// every sync cycle forever. The anchored "download failed: " prefix mirrors
|
|
130
|
+
// the provider's stable message shape and avoids misreading digits in
|
|
131
|
+
// network-error causes (IPs, ports), which stay transient.
|
|
132
|
+
function getDownloadStatusFromMessage(error) {
|
|
133
|
+
const match = /download failed: ([1-5]\d{2})\b/.exec(getRawMessage(error));
|
|
134
|
+
return match ? Number(match[1]) : undefined;
|
|
135
|
+
}
|
|
136
|
+
function getNumericProperty(value, key) {
|
|
137
|
+
if (typeof value !== "object" || value === null || !(key in value)) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const property = value[key];
|
|
141
|
+
return typeof property === "number" ? property : undefined;
|
|
142
|
+
}
|
|
143
|
+
function containsBinaryByte(bytes) {
|
|
144
|
+
if (typeof bytes === "string")
|
|
145
|
+
return false;
|
|
146
|
+
return bytes.some((byte) => byte === 0 || (byte < 0x09 && byte !== 0x07));
|
|
147
|
+
}
|
|
148
|
+
function getSafeIssueMessage(stage, disposition) {
|
|
149
|
+
switch (stage) {
|
|
150
|
+
case "download":
|
|
151
|
+
return disposition === "transient"
|
|
152
|
+
? "Download failed with a retryable storage or network error"
|
|
153
|
+
: "Download failed with a non-retryable storage response";
|
|
154
|
+
case "openpgp_parse":
|
|
155
|
+
return "Encrypted payload could not be parsed as an OpenPGP message";
|
|
156
|
+
case "decrypt":
|
|
157
|
+
return "Encrypted payload could not be decrypted";
|
|
158
|
+
case "json_parse":
|
|
159
|
+
return "Decrypted payload could not be parsed as JSON";
|
|
160
|
+
case "envelope_validate":
|
|
161
|
+
return "Decrypted JSON did not match the expected envelope shape";
|
|
162
|
+
case "block_build":
|
|
163
|
+
return "Envelope was stored, but bounded block build failed";
|
|
164
|
+
case "unknown":
|
|
165
|
+
return "Sync file failed at an unknown stage";
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=issues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.js","sourceRoot":"","sources":["../../src/sync/issues.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type DownloadAttemptDecision = "attempt" | "backoff" | "give-up";
|
|
2
|
+
export interface DownloadRetryMemoryOptions {
|
|
3
|
+
/** Attempt cap for retryable failures (default 5). */
|
|
4
|
+
maxTransientAttempts?: number;
|
|
5
|
+
/** Base backoff, doubled per failed attempt (default 30s). */
|
|
6
|
+
backoffBaseMs?: number;
|
|
7
|
+
/** Clock override for tests. */
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
export declare const DEFAULT_MAX_TRANSIENT_ATTEMPTS = 5;
|
|
11
|
+
export declare const DEFAULT_BACKOFF_BASE_MS = 30000;
|
|
12
|
+
export interface DownloadRetryMemory {
|
|
13
|
+
/** Whether the next download of `key` should run, wait, or never happen. */
|
|
14
|
+
decide(key: string): DownloadAttemptDecision;
|
|
15
|
+
/** Record a failed attempt; `retryable` follows the issue classification. */
|
|
16
|
+
recordFailure(key: string, retryable: boolean): void;
|
|
17
|
+
/** Clear the failure history after a successful download. */
|
|
18
|
+
recordSuccess(key: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* The listing restarted from the beginning (repair pass / full
|
|
21
|
+
* reconcile) — give transient failures a fresh attempt budget so the
|
|
22
|
+
* reset can actually re-fetch what it re-lists. Deterministic (404)
|
|
23
|
+
* entries stay dead: re-listing doesn't make missing bytes appear.
|
|
24
|
+
*/
|
|
25
|
+
onListingReset(): void;
|
|
26
|
+
}
|
|
27
|
+
/** Retry-memory key for a data-point record: id + version. */
|
|
28
|
+
export declare function downloadRetryKey(record: {
|
|
29
|
+
id: string;
|
|
30
|
+
expectedVersion: string;
|
|
31
|
+
}): string;
|
|
32
|
+
export declare function createDownloadRetryMemory(options?: DownloadRetryMemoryOptions): DownloadRetryMemory;
|
|
33
|
+
//# sourceMappingURL=retry-memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-memory.d.ts","sourceRoot":"","sources":["../../src/sync/retry-memory.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,0BAA0B;IACzC,sDAAsD;IACtD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,uBAAuB,CAAC;IAC7C,6EAA6E;IAC7E,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IACrD,6DAA6D;IAC7D,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;;OAKG;IACH,cAAc,IAAI,IAAI,CAAC;CACxB;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,MAAM,CAET;AAQD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,0BAA+B,GACvC,mBAAmB,CA4CrB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Cross-cycle download retry memory.
|
|
2
|
+
//
|
|
3
|
+
// downloadAll re-lists the owner's data points every sync cycle (the cursor
|
|
4
|
+
// only advances past fully-successful pages, and a single-page listing has no
|
|
5
|
+
// nextCursor at all), so without memory a record whose blob fails to download
|
|
6
|
+
// is re-fetched on every cycle. With the DCR page triggering a sync per
|
|
7
|
+
// readiness poll, one missing blob produced a 700+ request 404 storm against
|
|
8
|
+
// storage (2026-07-08 prod incident).
|
|
9
|
+
//
|
|
10
|
+
// Policy:
|
|
11
|
+
// - non-retryable failures (deterministic — e.g. HTTP 404: the blob is not
|
|
12
|
+
// there and waiting cannot heal it): never attempt again for the lifetime
|
|
13
|
+
// of this memory (one attempt per boot session).
|
|
14
|
+
// - retryable failures (transient — 5xx, network): exponential backoff,
|
|
15
|
+
// capped at DEFAULT_MAX_TRANSIENT_ATTEMPTS attempts, then give up for the
|
|
16
|
+
// session.
|
|
17
|
+
//
|
|
18
|
+
// Keys pair the record id with its expectedVersion (`downloadRetryKey`), so a
|
|
19
|
+
// re-registered version — new bytes under a new blob key — always gets a
|
|
20
|
+
// fresh set of attempts.
|
|
21
|
+
export const DEFAULT_MAX_TRANSIENT_ATTEMPTS = 5;
|
|
22
|
+
export const DEFAULT_BACKOFF_BASE_MS = 30_000;
|
|
23
|
+
/** Retry-memory key for a data-point record: id + version. */
|
|
24
|
+
export function downloadRetryKey(record) {
|
|
25
|
+
return `${record.id}@${record.expectedVersion}`;
|
|
26
|
+
}
|
|
27
|
+
export function createDownloadRetryMemory(options = {}) {
|
|
28
|
+
const maxTransientAttempts = options.maxTransientAttempts ?? DEFAULT_MAX_TRANSIENT_ATTEMPTS;
|
|
29
|
+
const backoffBaseMs = options.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS;
|
|
30
|
+
const now = options.now ?? Date.now;
|
|
31
|
+
// DPv2 keys one record per (owner, scope), so the map stays small — no
|
|
32
|
+
// eviction needed.
|
|
33
|
+
const entries = new Map();
|
|
34
|
+
return {
|
|
35
|
+
decide(key) {
|
|
36
|
+
const entry = entries.get(key);
|
|
37
|
+
if (!entry)
|
|
38
|
+
return "attempt";
|
|
39
|
+
if (entry.permanent || entry.attempts >= maxTransientAttempts) {
|
|
40
|
+
return "give-up";
|
|
41
|
+
}
|
|
42
|
+
return now() < entry.nextRetryAtMs ? "backoff" : "attempt";
|
|
43
|
+
},
|
|
44
|
+
recordFailure(key, retryable) {
|
|
45
|
+
if (!retryable) {
|
|
46
|
+
entries.set(key, { attempts: 0, nextRetryAtMs: 0, permanent: true });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const attempts = (entries.get(key)?.attempts ?? 0) + 1;
|
|
50
|
+
entries.set(key, {
|
|
51
|
+
attempts,
|
|
52
|
+
nextRetryAtMs: now() + backoffBaseMs * 2 ** (attempts - 1),
|
|
53
|
+
permanent: false,
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
recordSuccess(key) {
|
|
57
|
+
entries.delete(key);
|
|
58
|
+
},
|
|
59
|
+
onListingReset() {
|
|
60
|
+
for (const [key, entry] of entries) {
|
|
61
|
+
if (!entry.permanent) {
|
|
62
|
+
entries.delete(key);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=retry-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-memory.js","sourceRoot":"","sources":["../../src/sync/retry-memory.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,8EAA8E;AAC9E,wEAAwE;AACxE,6EAA6E;AAC7E,sCAAsC;AACtC,EAAE;AACF,UAAU;AACV,6EAA6E;AAC7E,8EAA8E;AAC9E,qDAAqD;AACrD,0EAA0E;AAC1E,8EAA8E;AAC9E,eAAe;AACf,EAAE;AACF,8EAA8E;AAC9E,yEAAyE;AACzE,yBAAyB;AAazB,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAkB9C,8DAA8D;AAC9D,MAAM,UAAU,gBAAgB,CAAC,MAGhC;IACC,OAAO,GAAG,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;AAClD,CAAC;AAQD,MAAM,UAAU,yBAAyB,CACvC,UAAsC,EAAE;IAExC,MAAM,oBAAoB,GACxB,OAAO,CAAC,oBAAoB,IAAI,8BAA8B,CAAC;IACjE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,uBAAuB,CAAC;IACvE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,uEAAuE;IACvE,mBAAmB;IACnB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAE9C,OAAO;QACL,MAAM,CAAC,GAAG;YACR,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAC;YAC7B,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,QAAQ,IAAI,oBAAoB,EAAE,CAAC;gBAC9D,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,OAAO,GAAG,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7D,CAAC;QAED,aAAa,CAAC,GAAG,EAAE,SAAS;YAC1B,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrE,OAAO;YACT,CAAC;YACD,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE;gBACf,QAAQ;gBACR,aAAa,EAAE,GAAG,EAAE,GAAG,aAAa,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;gBAC1D,SAAS,EAAE,KAAK;aACjB,CAAC,CAAC;QACL,CAAC;QAED,aAAa,CAAC,GAAG;YACf,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;QAED,cAAc;YACZ,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;gBACnC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;oBACrB,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|