@opendatalabs/personal-server-ts-core 0.0.1-canary.01ca694 → 0.0.1-canary.052d47e
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 +282 -9
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +867 -163
- package/dist/api/index.js.map +1 -1
- package/dist/auth/request.d.ts.map +1 -1
- package/dist/auth/request.js.map +1 -1
- package/dist/client.d.ts +8 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +21 -3
- package/dist/client.js.map +1 -1
- package/dist/contracts/binary.d.ts +12 -0
- package/dist/contracts/binary.d.ts.map +1 -1
- package/dist/contracts/binary.js +31 -0
- package/dist/contracts/binary.js.map +1 -1
- package/dist/contracts/data.d.ts +51 -5
- package/dist/contracts/data.d.ts.map +1 -1
- package/dist/contracts/data.js +166 -30
- package/dist/contracts/data.js.map +1 -1
- package/dist/contracts/grants.d.ts +7 -5
- package/dist/contracts/grants.d.ts.map +1 -1
- package/dist/contracts/grants.js +60 -37
- package/dist/contracts/grants.js.map +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/derivatives/api.d.ts +48 -0
- package/dist/derivatives/api.d.ts.map +1 -0
- package/dist/derivatives/api.js +366 -0
- package/dist/derivatives/api.js.map +1 -0
- package/dist/derivatives/compute.d.ts +106 -0
- package/dist/derivatives/compute.d.ts.map +1 -0
- package/dist/derivatives/compute.js +414 -0
- package/dist/derivatives/compute.js.map +1 -0
- package/dist/derivatives/e2ee/aad.d.ts +22 -0
- package/dist/derivatives/e2ee/aad.d.ts.map +1 -0
- package/dist/derivatives/e2ee/aad.js +32 -0
- package/dist/derivatives/e2ee/aad.js.map +1 -0
- package/dist/derivatives/e2ee/attestation.d.ts +134 -0
- package/dist/derivatives/e2ee/attestation.d.ts.map +1 -0
- package/dist/derivatives/e2ee/attestation.js +248 -0
- package/dist/derivatives/e2ee/attestation.js.map +1 -0
- package/dist/derivatives/e2ee/index.d.ts +6 -0
- package/dist/derivatives/e2ee/index.d.ts.map +1 -0
- package/dist/derivatives/e2ee/index.js +6 -0
- package/dist/derivatives/e2ee/index.js.map +1 -0
- package/dist/derivatives/e2ee/jcs.d.ts +17 -0
- package/dist/derivatives/e2ee/jcs.d.ts.map +1 -0
- package/dist/derivatives/e2ee/jcs.js +55 -0
- package/dist/derivatives/e2ee/jcs.js.map +1 -0
- package/dist/derivatives/e2ee/phala.d.ts +62 -0
- package/dist/derivatives/e2ee/phala.d.ts.map +1 -0
- package/dist/derivatives/e2ee/phala.js +168 -0
- package/dist/derivatives/e2ee/phala.js.map +1 -0
- package/dist/derivatives/e2ee/suite.d.ts +56 -0
- package/dist/derivatives/e2ee/suite.d.ts.map +1 -0
- package/dist/derivatives/e2ee/suite.js +164 -0
- package/dist/derivatives/e2ee/suite.js.map +1 -0
- package/dist/derivatives/index.d.ts +10 -0
- package/dist/derivatives/index.d.ts.map +1 -0
- package/dist/derivatives/index.js +10 -0
- package/dist/derivatives/index.js.map +1 -0
- package/dist/derivatives/inference.d.ts +151 -0
- package/dist/derivatives/inference.d.ts.map +1 -0
- package/dist/derivatives/inference.js +262 -0
- package/dist/derivatives/inference.js.map +1 -0
- package/dist/derivatives/prompt.d.ts +64 -0
- package/dist/derivatives/prompt.d.ts.map +1 -0
- package/dist/derivatives/prompt.js +193 -0
- package/dist/derivatives/prompt.js.map +1 -0
- package/dist/derivatives/registration.d.ts +54 -0
- package/dist/derivatives/registration.d.ts.map +1 -0
- package/dist/derivatives/registration.js +180 -0
- package/dist/derivatives/registration.js.map +1 -0
- package/dist/derivatives/scheduler.d.ts +84 -0
- package/dist/derivatives/scheduler.d.ts.map +1 -0
- package/dist/derivatives/scheduler.js +257 -0
- package/dist/derivatives/scheduler.js.map +1 -0
- package/dist/derivatives/store.d.ts +14 -0
- package/dist/derivatives/store.d.ts.map +1 -0
- package/dist/derivatives/store.js +77 -0
- package/dist/derivatives/store.js.map +1 -0
- package/dist/derivatives/types.d.ts +92 -0
- package/dist/derivatives/types.d.ts.map +1 -0
- package/dist/derivatives/types.js +27 -0
- package/dist/derivatives/types.js.map +1 -0
- package/dist/errors/catalog.d.ts +75 -0
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/catalog.js +104 -0
- package/dist/errors/catalog.js.map +1 -1
- package/dist/grants/index.d.ts +2 -1
- package/dist/grants/index.d.ts.map +1 -1
- package/dist/grants/index.js +1 -1
- package/dist/grants/index.js.map +1 -1
- package/dist/keys/index.d.ts +1 -0
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +1 -1
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/public-only-account.d.ts +8 -0
- package/dist/keys/public-only-account.d.ts.map +1 -0
- package/dist/keys/public-only-account.js +21 -0
- package/dist/keys/public-only-account.js.map +1 -0
- package/dist/keys/server-account.d.ts +2 -2
- package/dist/keys/server-account.d.ts.map +1 -1
- package/dist/lineage/attestation.d.ts +42 -0
- package/dist/lineage/attestation.d.ts.map +1 -0
- package/dist/lineage/attestation.js +25 -0
- package/dist/lineage/attestation.js.map +1 -0
- package/dist/lineage/gateway.d.ts +105 -0
- package/dist/lineage/gateway.d.ts.map +1 -0
- package/dist/lineage/gateway.js +242 -0
- package/dist/lineage/gateway.js.map +1 -0
- package/dist/lineage/index.d.ts +5 -0
- package/dist/lineage/index.d.ts.map +1 -0
- package/dist/lineage/index.js +5 -0
- package/dist/lineage/index.js.map +1 -0
- package/dist/lineage/lineage.d.ts +141 -0
- package/dist/lineage/lineage.d.ts.map +1 -0
- package/dist/lineage/lineage.js +255 -0
- package/dist/lineage/lineage.js.map +1 -0
- package/dist/logging/access-log.d.ts +10 -1
- package/dist/logging/access-log.d.ts.map +1 -1
- package/dist/mcp/activity.d.ts.map +1 -1
- package/dist/mcp/activity.js.map +1 -1
- package/dist/mcp/connection-api.d.ts.map +1 -1
- package/dist/mcp/connection-api.js +0 -1
- package/dist/mcp/connection-api.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/read-client.d.ts +61 -1
- package/dist/mcp/read-client.d.ts.map +1 -1
- package/dist/mcp/read-client.js +397 -42
- package/dist/mcp/read-client.js.map +1 -1
- 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.map +1 -1
- package/dist/mcp/search/index.js +108 -21
- package/dist/mcp/search/index.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +27 -9
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +160 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +243 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/tools.d.ts +29 -3
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +515 -48
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/types.d.ts.map +1 -1
- 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 +18 -2
- package/dist/policy/data-read.d.ts.map +1 -1
- package/dist/policy/data-read.js +80 -49
- package/dist/policy/data-read.js.map +1 -1
- package/dist/policy/data-write.d.ts +69 -0
- package/dist/policy/data-write.d.ts.map +1 -0
- package/dist/policy/data-write.js +131 -0
- package/dist/policy/data-write.js.map +1 -0
- package/dist/policy/index.d.ts +3 -1
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +3 -1
- package/dist/policy/index.js.map +1 -1
- package/dist/policy/signed-artifacts.d.ts +17 -0
- package/dist/policy/signed-artifacts.d.ts.map +1 -0
- package/dist/policy/signed-artifacts.js +110 -0
- package/dist/policy/signed-artifacts.js.map +1 -0
- package/dist/ports/index.d.ts +159 -32
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js +6 -5
- package/dist/ports/index.js.map +1 -1
- package/dist/schemas/server-config.d.ts +49 -4
- package/dist/schemas/server-config.d.ts.map +1 -1
- package/dist/schemas/server-config.js +95 -13
- package/dist/schemas/server-config.js.map +1 -1
- package/dist/signing/index.d.ts +2 -2
- package/dist/signing/index.d.ts.map +1 -1
- package/dist/signing/index.js +1 -1
- package/dist/signing/index.js.map +1 -1
- package/dist/signing/request-signer.d.ts +2 -0
- package/dist/signing/request-signer.d.ts.map +1 -1
- package/dist/signing/request-signer.js +1 -0
- package/dist/signing/request-signer.js.map +1 -1
- package/dist/signing/signer.d.ts +27 -25
- package/dist/signing/signer.d.ts.map +1 -1
- package/dist/signing/signer.js +27 -51
- package/dist/signing/signer.js.map +1 -1
- package/dist/storage/adapters/index.d.ts +1 -1
- package/dist/storage/adapters/index.d.ts.map +1 -1
- package/dist/storage/adapters/index.js +1 -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 +11 -1
- package/dist/storage/adapters/sdk.d.ts.map +1 -1
- package/dist/storage/adapters/sdk.js +38 -3
- package/dist/storage/adapters/sdk.js.map +1 -1
- package/dist/storage/adapters/vana.d.ts +3 -0
- package/dist/storage/adapters/vana.d.ts.map +1 -1
- package/dist/storage/adapters/vana.js +95 -2
- package/dist/storage/adapters/vana.js.map +1 -1
- package/dist/storage/blocks/errors.d.ts.map +1 -1
- package/dist/storage/blocks/errors.js.map +1 -1
- package/dist/storage/blocks/index.d.ts +2 -0
- package/dist/storage/blocks/index.d.ts.map +1 -1
- package/dist/storage/blocks/index.js +2 -0
- package/dist/storage/blocks/index.js.map +1 -1
- package/dist/storage/blocks/select.d.ts +29 -0
- package/dist/storage/blocks/select.d.ts.map +1 -0
- package/dist/storage/blocks/select.js +120 -0
- package/dist/storage/blocks/select.js.map +1 -0
- package/dist/storage/blocks/types.d.ts +7 -0
- package/dist/storage/blocks/types.d.ts.map +1 -1
- package/dist/storage/index/manager.d.ts +30 -2
- package/dist/storage/index/manager.d.ts.map +1 -1
- package/dist/storage/index/schema.d.ts +1 -1
- package/dist/storage/index/schema.js +1 -1
- package/dist/storage/index/types.d.ts +6 -1
- package/dist/storage/index/types.d.ts.map +1 -1
- package/dist/sync/data-point-feed.d.ts +35 -0
- package/dist/sync/data-point-feed.d.ts.map +1 -0
- package/dist/sync/data-point-feed.js +122 -0
- package/dist/sync/data-point-feed.js.map +1 -0
- package/dist/sync/data-point-id.d.ts +7 -0
- package/dist/sync/data-point-id.d.ts.map +1 -0
- package/dist/sync/data-point-id.js +16 -0
- package/dist/sync/data-point-id.js.map +1 -0
- package/dist/sync/delete-data-port.d.ts +50 -0
- package/dist/sync/delete-data-port.d.ts.map +1 -0
- package/dist/sync/delete-data-port.js +244 -0
- package/dist/sync/delete-data-port.js.map +1 -0
- package/dist/sync/engine/sync-manager.d.ts +16 -4
- package/dist/sync/engine/sync-manager.d.ts.map +1 -1
- package/dist/sync/engine/sync-manager.js +93 -32
- package/dist/sync/engine/sync-manager.js.map +1 -1
- package/dist/sync/index.d.ts +7 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +7 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/issues.d.ts.map +1 -1
- package/dist/sync/issues.js +21 -3
- package/dist/sync/issues.js.map +1 -1
- package/dist/sync/pending-blob-deletions.d.ts +29 -0
- package/dist/sync/pending-blob-deletions.d.ts.map +1 -0
- package/dist/sync/pending-blob-deletions.js +116 -0
- package/dist/sync/pending-blob-deletions.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/scope-deletions.d.ts +155 -0
- package/dist/sync/scope-deletions.d.ts.map +1 -0
- package/dist/sync/scope-deletions.js +225 -0
- package/dist/sync/scope-deletions.js.map +1 -0
- package/dist/sync/tombstone.d.ts +27 -0
- package/dist/sync/tombstone.d.ts.map +1 -0
- package/dist/sync/tombstone.js +28 -0
- package/dist/sync/tombstone.js.map +1 -0
- package/dist/sync/workers/delete.d.ts +152 -31
- package/dist/sync/workers/delete.d.ts.map +1 -1
- package/dist/sync/workers/delete.js +457 -55
- package/dist/sync/workers/delete.js.map +1 -1
- package/dist/sync/workers/download.d.ts +108 -27
- package/dist/sync/workers/download.d.ts.map +1 -1
- package/dist/sync/workers/download.js +321 -126
- package/dist/sync/workers/download.js.map +1 -1
- package/dist/sync/workers/upload.d.ts +65 -10
- package/dist/sync/workers/upload.d.ts.map +1 -1
- package/dist/sync/workers/upload.js +371 -42
- package/dist/sync/workers/upload.js.map +1 -1
- package/dist/test-utils/e2ee-fixtures.d.ts +94 -0
- package/dist/test-utils/e2ee-fixtures.d.ts.map +1 -0
- package/dist/test-utils/e2ee-fixtures.js +116 -0
- package/dist/test-utils/e2ee-fixtures.js.map +1 -0
- package/dist/test-utils/e2ee-gateway.d.ts +61 -0
- package/dist/test-utils/e2ee-gateway.d.ts.map +1 -0
- package/dist/test-utils/e2ee-gateway.js +248 -0
- package/dist/test-utils/e2ee-gateway.js.map +1 -0
- package/dist/test-utils/index.d.ts +3 -0
- package/dist/test-utils/index.d.ts.map +1 -1
- package/dist/test-utils/index.js +3 -0
- package/dist/test-utils/index.js.map +1 -1
- package/dist/test-utils/memory-storage.d.ts +12 -0
- package/dist/test-utils/memory-storage.d.ts.map +1 -0
- package/dist/test-utils/memory-storage.js +146 -0
- package/dist/test-utils/memory-storage.js.map +1 -0
- package/dist/test-utils/wallet.d.ts +6 -0
- package/dist/test-utils/wallet.d.ts.map +1 -1
- package/dist/test-utils/wallet.js +46 -0
- package/dist/test-utils/wallet.js.map +1 -1
- package/dist/write/attribution.d.ts +185 -0
- package/dist/write/attribution.d.ts.map +1 -0
- package/dist/write/attribution.js +450 -0
- package/dist/write/attribution.js.map +1 -0
- package/dist/write/authorize.d.ts +77 -0
- package/dist/write/authorize.d.ts.map +1 -0
- package/dist/write/authorize.js +109 -0
- package/dist/write/authorize.js.map +1 -0
- package/dist/write/index.d.ts +5 -0
- package/dist/write/index.d.ts.map +1 -0
- package/dist/write/index.js +5 -0
- package/dist/write/index.js.map +1 -0
- package/dist/write/route.d.ts +61 -0
- package/dist/write/route.d.ts.map +1 -0
- package/dist/write/route.js +132 -0
- package/dist/write/route.js.map +1 -0
- package/dist/write/session.d.ts +96 -0
- package/dist/write/session.d.ts.map +1 -0
- package/dist/write/session.js +136 -0
- package/dist/write/session.js.map +1 -0
- package/package.json +19 -3
|
@@ -1,57 +1,71 @@
|
|
|
1
1
|
import { DataFileEnvelopeSchema, decryptWithPassword, deriveScopeKey, } from "@opendatalabs/vana-sdk/browser";
|
|
2
|
+
import { feedFromGatewayClient } from "../data-point-feed.js";
|
|
3
|
+
import { deletionTimestamp, isEntryCoveredByTombstone, tombstoneVersion, } from "../scope-deletions.js";
|
|
2
4
|
import { buildDataBlocksAsync } from "../../storage/blocks/build.js";
|
|
3
5
|
import { classifySyncFailure, inferPayloadKind, } from "../issues.js";
|
|
6
|
+
import { downloadRetryKey } from "../retry-memory.js";
|
|
7
|
+
import { readStoredLineage } from "../../lineage/lineage.js";
|
|
4
8
|
/**
|
|
5
|
-
* Download and process a single
|
|
6
|
-
* 1. Check dedup: skip if
|
|
7
|
-
* 2.
|
|
8
|
-
*
|
|
9
|
+
* Download and process a single DPv2 data point from the storage backend:
|
|
10
|
+
* 1. Check dedup: skip if dataPointId already in local index
|
|
11
|
+
* 2. Reconstruct the blob URL from (scope, expectedVersion) — DataPointRecords
|
|
12
|
+
* carry no URL, so we rebuild the version-keyed `{scope}/{version}` key the
|
|
13
|
+
* upload worker wrote under and resolve it via storageAdapter.urlForKey
|
|
14
|
+
* 3. Download the OpenPGP encrypted binary from storage
|
|
9
15
|
* 4. Derive scope key from master key → hex-encode as OpenPGP password
|
|
16
|
+
* (scope comes straight off the record — no schema lookup needed)
|
|
10
17
|
* 5. Decrypt with OpenPGP password-based decryption → plaintext JSON
|
|
11
18
|
* 6. Parse as DataFileEnvelope (validate)
|
|
12
|
-
* 7. Write to local filesystem
|
|
13
|
-
* 8. Insert into local index (with
|
|
19
|
+
* 7. Write to local filesystem + bounded block sidecars
|
|
20
|
+
* 8. Insert into local index (with dataPointId + version)
|
|
14
21
|
*/
|
|
15
22
|
export async function downloadOne(deps, record) {
|
|
16
|
-
const { storage, storageAdapter,
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
const { storage, storageAdapter, masterKey, logger, diagnostics } = deps;
|
|
24
|
+
const ctx = { dataPointId: record.id, schemaId: null };
|
|
25
|
+
// 1. Check dedup: skip if dataPointId already in local index
|
|
26
|
+
const existing = storage.findByDataPointId(record.id);
|
|
19
27
|
if (existing) {
|
|
20
|
-
await repairMissingBlockSidecars(storage, logger, record, existing, diagnostics);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const repairResult = await repairMissingBlockSidecars(storage, logger, { dataPointId: record.id, schemaId: existing.schemaId }, existing, diagnostics);
|
|
29
|
+
if (repairResult === "missing-envelope") {
|
|
30
|
+
logger.warn({
|
|
31
|
+
dataPointId: record.id,
|
|
32
|
+
scope: existing.scope,
|
|
33
|
+
collectedAt: existing.collectedAt,
|
|
34
|
+
}, "Local index entry was missing its envelope; re-downloading data point");
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
logger.debug({ dataPointId: record.id }, "Data point already in index, skipping");
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// 2. Reconstruct the version-keyed storage URL.
|
|
42
|
+
const storageKey = `${record.scope}/${record.expectedVersion}`;
|
|
43
|
+
const url = storageAdapter.urlForKey(storageKey);
|
|
44
|
+
// 3. Download OpenPGP encrypted binary from storage backend. A download
|
|
45
|
+
// failure throws and blocks the cursor (correct: a transient error retries
|
|
46
|
+
// next cycle rather than risk a silent gap). Stamp the stage so the
|
|
47
|
+
// classifier can tell a deterministic 404 (quarantine, never retry) from a
|
|
48
|
+
// transient storage error — without it the failure infers stage "unknown"
|
|
49
|
+
// and every 404 is retried forever.
|
|
50
|
+
diagnostics?.onDownloadStart(record.id);
|
|
34
51
|
let encrypted;
|
|
35
52
|
try {
|
|
36
|
-
encrypted = await storageAdapter.download(
|
|
53
|
+
encrypted = await storageAdapter.download(url);
|
|
37
54
|
}
|
|
38
55
|
catch (err) {
|
|
39
56
|
const detail = err.message;
|
|
40
|
-
diagnostics?.onDownloadError(record.
|
|
41
|
-
throw err
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (!schema) {
|
|
46
|
-
diagnostics?.onDownloadError(record.fileId, `No schema found for schemaId: ${record.schemaId}`);
|
|
47
|
-
throw new Error(`No schema found for schemaId: ${record.schemaId}`);
|
|
57
|
+
diagnostics?.onDownloadError(record.id, detail);
|
|
58
|
+
throw withSyncFailureMetadata(err, {
|
|
59
|
+
stage: "download",
|
|
60
|
+
scope: record.scope,
|
|
61
|
+
});
|
|
48
62
|
}
|
|
49
|
-
diagnostics?.onDownloadEnd(record.
|
|
50
|
-
// 4. Derive scope key → hex-encode as OpenPGP password
|
|
51
|
-
const scopeKey = deriveScopeKey(masterKey,
|
|
63
|
+
diagnostics?.onDownloadEnd(record.id, record.scope);
|
|
64
|
+
// 4. Derive scope key → hex-encode as OpenPGP password (scope off the record)
|
|
65
|
+
const scopeKey = deriveScopeKey(masterKey, record.scope);
|
|
52
66
|
const scopeKeyHex = uint8ToHex(scopeKey);
|
|
53
67
|
// 5. Decrypt with OpenPGP password-based decryption
|
|
54
|
-
diagnostics?.onDecryptStart(record.
|
|
68
|
+
diagnostics?.onDecryptStart(record.id, record.scope);
|
|
55
69
|
let plaintext;
|
|
56
70
|
try {
|
|
57
71
|
plaintext = await decryptWithPassword(encrypted, scopeKeyHex);
|
|
@@ -64,9 +78,9 @@ export async function downloadOne(deps, record) {
|
|
|
64
78
|
`payloadKind=${payloadKind}`,
|
|
65
79
|
`encryptedSizeBytes=${encryptedSizeBytes}`,
|
|
66
80
|
].join("; ");
|
|
67
|
-
diagnostics?.onDecryptError(record.
|
|
81
|
+
diagnostics?.onDecryptError(record.id, record.scope, detail);
|
|
68
82
|
throw withSyncFailureMetadata(err, {
|
|
69
|
-
scope:
|
|
83
|
+
scope: record.scope,
|
|
70
84
|
payloadKind,
|
|
71
85
|
encryptedSizeBytes,
|
|
72
86
|
});
|
|
@@ -79,97 +93,198 @@ export async function downloadOne(deps, record) {
|
|
|
79
93
|
}
|
|
80
94
|
catch (err) {
|
|
81
95
|
const detail = err.message;
|
|
82
|
-
diagnostics?.onDecryptError(record.
|
|
96
|
+
diagnostics?.onDecryptError(record.id, record.scope, detail);
|
|
83
97
|
throw err;
|
|
84
98
|
}
|
|
85
|
-
|
|
99
|
+
ctx.schemaId = envelope.schemaId ?? null;
|
|
100
|
+
diagnostics?.onDecryptEnd(record.id, envelope.scope);
|
|
86
101
|
const existingByVersion = storage.findEntry({
|
|
87
102
|
scope: envelope.scope,
|
|
88
103
|
at: envelope.collectedAt,
|
|
89
104
|
});
|
|
90
105
|
if (existingByVersion !== undefined &&
|
|
91
106
|
existingByVersion.collectedAt === envelope.collectedAt) {
|
|
92
|
-
if (existingByVersion.
|
|
93
|
-
await storage.
|
|
107
|
+
if (existingByVersion.dataPointId !== record.id) {
|
|
108
|
+
await storage.updateDataPointId(existingByVersion.path, record.id);
|
|
94
109
|
}
|
|
95
|
-
await repairMissingBlockSidecars(storage, logger,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
const repairResult = await repairMissingBlockSidecars(storage, logger, {
|
|
111
|
+
dataPointId: record.id,
|
|
112
|
+
schemaId: existingByVersion.schemaId ?? ctx.schemaId,
|
|
113
|
+
}, existingByVersion, diagnostics);
|
|
114
|
+
if (repairResult !== "missing-envelope") {
|
|
115
|
+
logger.debug({
|
|
116
|
+
dataPointId: record.id,
|
|
117
|
+
scope: envelope.scope,
|
|
118
|
+
collectedAt: envelope.collectedAt,
|
|
119
|
+
}, "Data version already exists locally, skipping");
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
logger.warn({
|
|
123
|
+
dataPointId: record.id,
|
|
102
124
|
scope: envelope.scope,
|
|
103
125
|
collectedAt: envelope.collectedAt,
|
|
104
|
-
}, "
|
|
105
|
-
return null;
|
|
126
|
+
}, "Local version entry was missing its envelope; rewriting downloaded file");
|
|
106
127
|
}
|
|
107
|
-
// 7. Write to local storage
|
|
108
|
-
diagnostics?.onIndexStart(record.
|
|
128
|
+
// 7. Write to local storage + bounded block sidecars
|
|
129
|
+
diagnostics?.onIndexStart(record.id, envelope.scope);
|
|
109
130
|
let relativePath;
|
|
110
131
|
let sizeBytes;
|
|
111
132
|
try {
|
|
112
133
|
({ relativePath, sizeBytes } = await storage.writeEnvelope(envelope));
|
|
113
|
-
await writeBlockSidecars(storage, envelope, logger,
|
|
134
|
+
await writeBlockSidecars(storage, envelope, logger, ctx, relativePath, diagnostics);
|
|
114
135
|
}
|
|
115
136
|
catch (err) {
|
|
116
137
|
const detail = err.message;
|
|
117
|
-
diagnostics?.onIndexError(record.
|
|
138
|
+
diagnostics?.onIndexError(record.id, envelope.scope, detail);
|
|
118
139
|
throw err;
|
|
119
140
|
}
|
|
120
|
-
// 8. Insert into local index (with
|
|
141
|
+
// 8. Insert into local index (with dataPointId + version)
|
|
121
142
|
try {
|
|
122
143
|
await storage.insertEntry({
|
|
123
|
-
fileId:
|
|
124
|
-
schemaId:
|
|
144
|
+
fileId: null,
|
|
145
|
+
schemaId: ctx.schemaId,
|
|
125
146
|
path: relativePath,
|
|
126
147
|
scope: envelope.scope,
|
|
127
148
|
collectedAt: envelope.collectedAt,
|
|
128
149
|
sizeBytes,
|
|
150
|
+
version: Number(record.expectedVersion),
|
|
151
|
+
dataPointId: record.id,
|
|
129
152
|
});
|
|
130
153
|
}
|
|
131
154
|
catch (err) {
|
|
132
155
|
const detail = err.message;
|
|
133
|
-
diagnostics?.onIndexError(record.
|
|
156
|
+
diagnostics?.onIndexError(record.id, envelope.scope, detail);
|
|
134
157
|
throw err;
|
|
135
158
|
}
|
|
136
|
-
diagnostics?.onIndexEnd(record.
|
|
137
|
-
logger.info({
|
|
159
|
+
diagnostics?.onIndexEnd(record.id, envelope.scope);
|
|
160
|
+
logger.info({ dataPointId: record.id, scope: envelope.scope, path: relativePath }, "Downloaded and indexed data point");
|
|
161
|
+
if (deps.onDataPointIndexed) {
|
|
162
|
+
try {
|
|
163
|
+
let lineageSources;
|
|
164
|
+
try {
|
|
165
|
+
lineageSources = readStoredLineage(envelope.data)?.sources;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// A malformed $lineage is the upload worker's problem; the hook
|
|
169
|
+
// still learns about the new version.
|
|
170
|
+
}
|
|
171
|
+
deps.onDataPointIndexed({
|
|
172
|
+
scope: envelope.scope,
|
|
173
|
+
dataPointId: record.id,
|
|
174
|
+
version: Number(record.expectedVersion),
|
|
175
|
+
collectedAt: envelope.collectedAt,
|
|
176
|
+
lineageSources,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
logger.warn({ scope: envelope.scope, error: err.message }, "onDataPointIndexed hook failed; data point already indexed");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
138
183
|
return {
|
|
139
|
-
|
|
184
|
+
dataPointId: record.id,
|
|
140
185
|
scope: envelope.scope,
|
|
141
186
|
collectedAt: envelope.collectedAt,
|
|
142
187
|
path: relativePath,
|
|
143
188
|
};
|
|
144
189
|
}
|
|
145
190
|
/**
|
|
146
|
-
* Poll Gateway for
|
|
147
|
-
*
|
|
191
|
+
* Poll Gateway for the owner's data points since the stored cursor, download
|
|
192
|
+
* each, and advance the cursor only when the page fully succeeds.
|
|
148
193
|
*/
|
|
149
|
-
export async function downloadAll(deps) {
|
|
194
|
+
export async function downloadAll(deps, options = {}) {
|
|
150
195
|
const { gateway, cursor, serverOwner, logger } = deps;
|
|
151
196
|
const syncRunId = createSyncRunId();
|
|
152
|
-
await repairLocalMissingBlockSidecars(deps);
|
|
153
|
-
// 1. Read cursor
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
197
|
+
const repairSummary = await repairLocalMissingBlockSidecars(deps);
|
|
198
|
+
// 1. Read cursor (opaque pagination cursor persisted across runs)
|
|
199
|
+
const lastCursor = options.fullReconcile || repairSummary.missingEnvelopeEntries > 0
|
|
200
|
+
? null
|
|
201
|
+
: await cursor.read();
|
|
202
|
+
if (options.fullReconcile) {
|
|
203
|
+
logger.info("Running full registry reconciliation to repair any missing local scope records");
|
|
204
|
+
}
|
|
205
|
+
if (repairSummary.missingEnvelopeEntries > 0) {
|
|
206
|
+
logger.warn({ missingEnvelopeEntries: repairSummary.missingEnvelopeEntries }, "Resetting this sync listing to repair stale local index entries");
|
|
207
|
+
}
|
|
208
|
+
if (options.fullReconcile || repairSummary.missingEnvelopeEntries > 0) {
|
|
209
|
+
// An EXPLICIT listing restart (full reconcile, or the repair path above)
|
|
210
|
+
// exists to re-fetch — give transient failures a fresh attempt budget so
|
|
211
|
+
// it can; 404s stay dead. Note `lastCursor === null` alone is NOT a
|
|
212
|
+
// reset signal: single-page listings never advance the cursor, so null
|
|
213
|
+
// is their steady state and clearing on it would erase the backoff
|
|
214
|
+
// memory every cycle.
|
|
215
|
+
options.retryMemory?.onListingReset();
|
|
216
|
+
}
|
|
217
|
+
// 2. Poll gateway for the owner's data points, tombstones included: a
|
|
218
|
+
// deletion made on another replica must reach this one or its local copy
|
|
219
|
+
// would be re-uploaded and resurrect the point.
|
|
220
|
+
const feed = deps.dataPointFeed ?? feedFromGatewayClient(gateway);
|
|
221
|
+
const { dataPoints, cursor: nextCursor } = await feed.listDataPointsByOwner(serverOwner, lastCursor, { includeDeleted: true });
|
|
222
|
+
// Feed the read-side deletion memory before any per-record work so a
|
|
223
|
+
// tombstone is refused by reads even if its local reconcile below fails.
|
|
224
|
+
// Only a listing with no further pages proves the memory complete: a
|
|
225
|
+
// multi-page backlog leaves it "stale" and reads fall back to lookups. A
|
|
226
|
+
// listing from no cursor covered the whole registry (`full`), which is
|
|
227
|
+
// what re-validates remembered tombstones; an incremental pass does not.
|
|
228
|
+
if (deps.scopeDeletions) {
|
|
229
|
+
for (const dataPoint of dataPoints) {
|
|
230
|
+
const deletedAt = deletionTimestamp(dataPoint);
|
|
231
|
+
if (deletedAt !== null) {
|
|
232
|
+
deps.scopeDeletions.markDeleted(dataPoint.scope, {
|
|
233
|
+
deletedAt,
|
|
234
|
+
version: tombstoneVersion(dataPoint),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
deps.scopeDeletions.markLive(dataPoint.scope);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (nextCursor === null) {
|
|
242
|
+
deps.scopeDeletions.noteFeedSynced(undefined, {
|
|
243
|
+
full: lastCursor === null,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
158
247
|
const results = [];
|
|
159
248
|
let failed = false;
|
|
160
|
-
// 3. Process each
|
|
161
|
-
for (const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
249
|
+
// 3. Process each data point record
|
|
250
|
+
for (const dataPoint of dataPoints) {
|
|
251
|
+
const deletedAt = deletionTimestamp(dataPoint);
|
|
252
|
+
if (deletedAt !== null) {
|
|
253
|
+
// Tombstone: never download; drop what the deletion covers locally.
|
|
254
|
+
// A failure here blocks the cursor so the tombstone is re-listed and
|
|
255
|
+
// the reconcile retried next cycle.
|
|
256
|
+
try {
|
|
257
|
+
await reconcileDeletedDataPoint(deps, dataPoint, deletedAt);
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
logger.error({
|
|
261
|
+
dataPointId: dataPoint.id,
|
|
262
|
+
scope: dataPoint.scope,
|
|
263
|
+
error: err.message,
|
|
264
|
+
}, "Failed to reconcile deleted data point locally");
|
|
265
|
+
failed = true;
|
|
171
266
|
}
|
|
172
|
-
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const retryKey = downloadRetryKey(dataPoint);
|
|
270
|
+
const decision = options.retryMemory?.decide(retryKey) ?? "attempt";
|
|
271
|
+
if (decision === "give-up") {
|
|
272
|
+
// Non-retryable failure (e.g. blob 404) or exhausted transient
|
|
273
|
+
// retries — skip without blocking the cursor so one dead record
|
|
274
|
+
// can't wedge the rest of the listing. A new registered version
|
|
275
|
+
// (different retry key) starts fresh.
|
|
276
|
+
logger.debug({ dataPointId: dataPoint.id, scope: dataPoint.scope }, "Skipping data point after exhausted download retries");
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (decision === "backoff") {
|
|
280
|
+
// Waiting out the backoff window. Block the cursor so the record is
|
|
281
|
+
// still listed when the window expires.
|
|
282
|
+
failed = true;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const result = await downloadOne(deps, dataPoint);
|
|
287
|
+
options.retryMemory?.recordSuccess(retryKey);
|
|
173
288
|
if (result) {
|
|
174
289
|
results.push(result);
|
|
175
290
|
}
|
|
@@ -178,32 +293,31 @@ export async function downloadAll(deps) {
|
|
|
178
293
|
const metadata = getSyncFailureMetadata(err);
|
|
179
294
|
const classified = classifySyncFailure({
|
|
180
295
|
error: err,
|
|
181
|
-
fileId:
|
|
182
|
-
schemaId: file.schemaId,
|
|
296
|
+
fileId: dataPoint.id,
|
|
183
297
|
syncRunId,
|
|
184
298
|
stage: metadata.stage,
|
|
185
|
-
scope: metadata.scope,
|
|
299
|
+
scope: metadata.scope ?? dataPoint.scope,
|
|
186
300
|
payloadKind: metadata.payloadKind,
|
|
187
301
|
encryptedSizeBytes: metadata.encryptedSizeBytes,
|
|
188
302
|
});
|
|
303
|
+
options.retryMemory?.recordFailure(retryKey, classified.issue.retryable);
|
|
189
304
|
if (!classified.issue.retryable) {
|
|
190
305
|
logger.warn({
|
|
191
|
-
|
|
192
|
-
schemaId: file.schemaId,
|
|
306
|
+
dataPointId: dataPoint.id,
|
|
193
307
|
scope: classified.issue.scope,
|
|
194
308
|
stage: classified.issue.stage,
|
|
195
309
|
payloadKind: classified.issue.payloadKind,
|
|
196
310
|
encryptedSizeBytes: classified.issue.encryptedSizeBytes,
|
|
197
311
|
errorClass: classified.issue.errorClass,
|
|
198
312
|
message: classified.issue.message,
|
|
199
|
-
}, "Quarantined corrupt synced
|
|
313
|
+
}, "Quarantined corrupt synced data point");
|
|
200
314
|
continue;
|
|
201
315
|
}
|
|
202
316
|
logger.error({
|
|
203
|
-
|
|
204
|
-
|
|
317
|
+
dataPointId: dataPoint.id,
|
|
318
|
+
scope: dataPoint.scope,
|
|
205
319
|
error: err.message,
|
|
206
|
-
}, "Failed to download
|
|
320
|
+
}, "Failed to download data point");
|
|
207
321
|
failed = true;
|
|
208
322
|
}
|
|
209
323
|
}
|
|
@@ -212,19 +326,78 @@ export async function downloadAll(deps) {
|
|
|
212
326
|
await cursor.write(nextCursor);
|
|
213
327
|
}
|
|
214
328
|
else if (failed) {
|
|
215
|
-
logger.warn({ nextCursor }, "Download cursor not advanced because one or more
|
|
329
|
+
logger.warn({ nextCursor }, "Download cursor not advanced because one or more data points failed");
|
|
216
330
|
}
|
|
217
331
|
return results;
|
|
218
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* Apply a gateway tombstone to the local index: remove every local version
|
|
335
|
+
* the tombstone covers (synced rows at or below its version, and unsynced
|
|
336
|
+
* rows ingested without knowledge of it), keep unsynced rows ingested on top
|
|
337
|
+
* of it (a legitimate re-add that the upload worker registers after the
|
|
338
|
+
* tombstone version). See `isEntryCoveredByTombstone`.
|
|
339
|
+
*/
|
|
340
|
+
export async function reconcileDeletedDataPoint(deps, record, deletedAt) {
|
|
341
|
+
const tombstone = { version: tombstoneVersion(record) };
|
|
342
|
+
const tombstoned = tombstone.version === null ? null : BigInt(tombstone.version);
|
|
343
|
+
// Unsynced covered entries above the tombstone version: the deleting
|
|
344
|
+
// replica's exact-key pass only enumerates registry versions, so any
|
|
345
|
+
// ciphertext this replica uploaded under a higher, never-registered key is
|
|
346
|
+
// ours to clean up.
|
|
347
|
+
const orphanKeys = [];
|
|
348
|
+
const { storage, logger } = deps;
|
|
349
|
+
const PAGE_SIZE = 500;
|
|
350
|
+
const stale = [];
|
|
351
|
+
let kept = 0;
|
|
352
|
+
for (let offset = 0;; offset += PAGE_SIZE) {
|
|
353
|
+
const entries = storage.listVersions(record.scope, {
|
|
354
|
+
limit: PAGE_SIZE,
|
|
355
|
+
offset,
|
|
356
|
+
});
|
|
357
|
+
for (const entry of entries) {
|
|
358
|
+
if (isEntryCoveredByTombstone(entry, tombstone)) {
|
|
359
|
+
stale.push({ scope: entry.scope, collectedAt: entry.collectedAt });
|
|
360
|
+
if (entry.dataPointId === null &&
|
|
361
|
+
(tombstoned === null || BigInt(entry.version) > tombstoned)) {
|
|
362
|
+
orphanKeys.push({
|
|
363
|
+
scope: entry.scope,
|
|
364
|
+
version: String(entry.version),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
kept += 1;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (entries.length < PAGE_SIZE)
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
let removed = 0;
|
|
376
|
+
for (const version of stale) {
|
|
377
|
+
if (await storage.deleteVersion(version.scope, version.collectedAt)) {
|
|
378
|
+
removed += 1;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (orphanKeys.length > 0 && deps.pendingBlobDeletions) {
|
|
382
|
+
await deps.pendingBlobDeletions.add(orphanKeys);
|
|
383
|
+
}
|
|
384
|
+
if (removed > 0 || kept > 0) {
|
|
385
|
+
logger.info({ dataPointId: record.id, scope: record.scope, deletedAt, removed, kept }, "Reconciled gateway deletion against local index");
|
|
386
|
+
}
|
|
387
|
+
return { scope: record.scope, deletedAt, removed, kept };
|
|
388
|
+
}
|
|
219
389
|
export async function repairLocalMissingBlockSidecars(deps) {
|
|
220
390
|
const { storage, logger, diagnostics } = deps;
|
|
221
|
-
if (!storage.writeBlockManifest || !storage.hasScopeBlocks)
|
|
222
|
-
return 0;
|
|
391
|
+
if (!storage.writeBlockManifest || !storage.hasScopeBlocks) {
|
|
392
|
+
return { repaired: 0, missingEnvelopeEntries: 0 };
|
|
393
|
+
}
|
|
223
394
|
let repaired = 0;
|
|
395
|
+
let missingEnvelopeEntries = 0;
|
|
224
396
|
let offset = 0;
|
|
225
397
|
const limit = 100;
|
|
226
398
|
while (true) {
|
|
227
399
|
const page = storage.listScopes({ limit, offset });
|
|
400
|
+
let deletedEntryInPage = false;
|
|
228
401
|
for (const scopeSummary of page.scopes) {
|
|
229
402
|
const entry = storage.findEntry({
|
|
230
403
|
scope: scopeSummary.scope,
|
|
@@ -232,24 +405,30 @@ export async function repairLocalMissingBlockSidecars(deps) {
|
|
|
232
405
|
});
|
|
233
406
|
if (!entry)
|
|
234
407
|
continue;
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
url: "",
|
|
239
|
-
schemaId: entry.schemaId ?? "",
|
|
240
|
-
createdAt: entry.createdAt,
|
|
408
|
+
const repairResult = await repairMissingBlockSidecars(storage, logger, {
|
|
409
|
+
dataPointId: entry.dataPointId ?? entry.path,
|
|
410
|
+
schemaId: entry.schemaId,
|
|
241
411
|
}, entry, diagnostics);
|
|
242
|
-
if (
|
|
412
|
+
if (repairResult === "repaired")
|
|
243
413
|
repaired += 1;
|
|
414
|
+
if (repairResult === "missing-envelope") {
|
|
415
|
+
missingEnvelopeEntries += 1;
|
|
416
|
+
deletedEntryInPage = true;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (!deletedEntryInPage) {
|
|
420
|
+
offset += page.scopes.length;
|
|
244
421
|
}
|
|
245
|
-
offset += page.scopes.length;
|
|
246
422
|
if (offset >= page.total || page.scopes.length === 0)
|
|
247
423
|
break;
|
|
248
424
|
}
|
|
249
425
|
if (repaired > 0) {
|
|
250
426
|
logger.info({ repaired }, "Repaired missing bounded block sidecars for local indexed data");
|
|
251
427
|
}
|
|
252
|
-
|
|
428
|
+
if (missingEnvelopeEntries > 0) {
|
|
429
|
+
logger.warn({ missingEnvelopeEntries }, "Removed stale local index entries missing local envelopes");
|
|
430
|
+
}
|
|
431
|
+
return { repaired, missingEnvelopeEntries };
|
|
253
432
|
}
|
|
254
433
|
function createSyncRunId() {
|
|
255
434
|
return typeof crypto !== "undefined" &&
|
|
@@ -276,32 +455,32 @@ function getSyncFailureMetadata(error) {
|
|
|
276
455
|
return {};
|
|
277
456
|
return metadata;
|
|
278
457
|
}
|
|
279
|
-
async function writeBlockSidecars(storage, envelope, logger,
|
|
458
|
+
async function writeBlockSidecars(storage, envelope, logger, ctx, relativePath, diagnostics) {
|
|
280
459
|
if (!storage.writeBlockManifest)
|
|
281
460
|
return;
|
|
282
|
-
diagnostics?.onManifestBuildStart(
|
|
461
|
+
diagnostics?.onManifestBuildStart(ctx.dataPointId, envelope.scope);
|
|
283
462
|
try {
|
|
284
463
|
const built = await buildDataBlocksAsync({
|
|
285
464
|
scope: envelope.scope,
|
|
286
465
|
collectedAt: envelope.collectedAt,
|
|
287
|
-
schemaId:
|
|
466
|
+
schemaId: ctx.schemaId ?? undefined,
|
|
288
467
|
content: envelope,
|
|
289
468
|
});
|
|
290
469
|
await storage.writeBlockManifest(envelope.scope, envelope.collectedAt, built.manifest, built.blocks);
|
|
291
|
-
diagnostics?.onManifestBuildEnd(
|
|
470
|
+
diagnostics?.onManifestBuildEnd(ctx.dataPointId, envelope.scope);
|
|
292
471
|
}
|
|
293
472
|
catch (error) {
|
|
294
473
|
const classified = classifySyncFailure({
|
|
295
474
|
error,
|
|
296
|
-
fileId:
|
|
475
|
+
fileId: ctx.dataPointId,
|
|
297
476
|
syncRunId: "download",
|
|
298
477
|
stage: "block_build",
|
|
299
|
-
schemaId:
|
|
478
|
+
schemaId: ctx.schemaId ?? undefined,
|
|
300
479
|
scope: envelope.scope,
|
|
301
480
|
});
|
|
302
|
-
diagnostics?.onManifestBuildError(
|
|
481
|
+
diagnostics?.onManifestBuildError(ctx.dataPointId, envelope.scope, classified.issue.message);
|
|
303
482
|
logger.warn({
|
|
304
|
-
|
|
483
|
+
dataPointId: ctx.dataPointId,
|
|
305
484
|
scope: envelope.scope,
|
|
306
485
|
collectedAt: envelope.collectedAt,
|
|
307
486
|
path: relativePath,
|
|
@@ -311,24 +490,25 @@ async function writeBlockSidecars(storage, envelope, logger, record, relativePat
|
|
|
311
490
|
}, "Bounded block sidecar write failed after raw envelope write");
|
|
312
491
|
}
|
|
313
492
|
}
|
|
314
|
-
async function repairMissingBlockSidecars(storage, logger,
|
|
315
|
-
if (!storage.writeBlockManifest || !storage.hasScopeBlocks)
|
|
316
|
-
return
|
|
493
|
+
async function repairMissingBlockSidecars(storage, logger, ctx, entry, diagnostics) {
|
|
494
|
+
if (!storage.writeBlockManifest || !storage.hasScopeBlocks) {
|
|
495
|
+
return "not-repaired";
|
|
496
|
+
}
|
|
317
497
|
let hasBlocks = false;
|
|
318
498
|
try {
|
|
319
499
|
hasBlocks = await storage.hasScopeBlocks(entry.scope, entry.collectedAt);
|
|
320
500
|
}
|
|
321
501
|
catch (error) {
|
|
322
502
|
logger.warn({
|
|
323
|
-
|
|
503
|
+
dataPointId: ctx.dataPointId,
|
|
324
504
|
scope: entry.scope,
|
|
325
505
|
collectedAt: entry.collectedAt,
|
|
326
506
|
error: error.message,
|
|
327
507
|
}, "Bounded block sidecar repair skipped after readiness check failed");
|
|
328
|
-
return
|
|
508
|
+
return "not-repaired";
|
|
329
509
|
}
|
|
330
510
|
if (hasBlocks)
|
|
331
|
-
return
|
|
511
|
+
return "already-ready";
|
|
332
512
|
diagnostics?.onRepair(entry.scope, "indexed entry missing block manifest; rebuilding from local envelope");
|
|
333
513
|
let envelope;
|
|
334
514
|
try {
|
|
@@ -336,29 +516,44 @@ async function repairMissingBlockSidecars(storage, logger, record, entry, diagno
|
|
|
336
516
|
}
|
|
337
517
|
catch (error) {
|
|
338
518
|
logger.warn({
|
|
339
|
-
|
|
519
|
+
dataPointId: ctx.dataPointId,
|
|
340
520
|
scope: entry.scope,
|
|
341
521
|
collectedAt: entry.collectedAt,
|
|
342
522
|
error: error.message,
|
|
343
523
|
}, "Bounded block sidecar repair skipped because local envelope is unavailable");
|
|
344
|
-
|
|
524
|
+
// Remove the stale index entry whose payload is gone. We can only target
|
|
525
|
+
// it by gateway fileId; DataPoint-only entries (fileId === null) are left
|
|
526
|
+
// in place (no path-keyed delete on the port) and self-heal on re-download.
|
|
527
|
+
if (entry.fileId) {
|
|
528
|
+
diagnostics?.onRepair(entry.scope, "index exists without local payload; removing stale local index entry");
|
|
529
|
+
const removed = await storage.deleteByFileId(entry.fileId);
|
|
530
|
+
logger.warn({
|
|
531
|
+
dataPointId: ctx.dataPointId,
|
|
532
|
+
scope: entry.scope,
|
|
533
|
+
collectedAt: entry.collectedAt,
|
|
534
|
+
removed,
|
|
535
|
+
}, "Removed stale local index entry missing local envelope");
|
|
536
|
+
return removed ? "missing-envelope" : "not-repaired";
|
|
537
|
+
}
|
|
538
|
+
return "not-repaired";
|
|
345
539
|
}
|
|
346
540
|
await writeBlockSidecars(storage, envelope, logger, {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
schemaId: entry.schemaId ?? record.schemaId,
|
|
541
|
+
dataPointId: ctx.dataPointId,
|
|
542
|
+
schemaId: entry.schemaId ?? ctx.schemaId,
|
|
350
543
|
}, entry.path, diagnostics);
|
|
351
544
|
try {
|
|
352
|
-
return await storage.hasScopeBlocks(entry.scope, entry.collectedAt)
|
|
545
|
+
return (await storage.hasScopeBlocks(entry.scope, entry.collectedAt))
|
|
546
|
+
? "repaired"
|
|
547
|
+
: "not-repaired";
|
|
353
548
|
}
|
|
354
549
|
catch (error) {
|
|
355
550
|
logger.warn({
|
|
356
|
-
|
|
551
|
+
dataPointId: ctx.dataPointId,
|
|
357
552
|
scope: entry.scope,
|
|
358
553
|
collectedAt: entry.collectedAt,
|
|
359
554
|
error: error.message,
|
|
360
555
|
}, "Bounded block sidecar repair completed but readiness could not be verified");
|
|
361
|
-
return
|
|
556
|
+
return "not-repaired";
|
|
362
557
|
}
|
|
363
558
|
}
|
|
364
559
|
//# sourceMappingURL=download.js.map
|