@opendatalabs/personal-server-ts-core 1.6.0 → 1.8.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/api/index.d.ts +53 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +300 -25
- package/dist/api/index.js.map +1 -1
- package/dist/contracts/data.d.ts +30 -1
- package/dist/contracts/data.d.ts.map +1 -1
- package/dist/contracts/data.js +126 -33
- package/dist/contracts/data.js.map +1 -1
- package/dist/errors/catalog.d.ts +40 -0
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/catalog.js +53 -0
- package/dist/errors/catalog.js.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 +7 -1
- package/dist/logging/access-log.d.ts.map +1 -1
- package/dist/mcp/read-client.d.ts.map +1 -1
- package/dist/mcp/read-client.js +25 -1
- package/dist/mcp/read-client.js.map +1 -1
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +10 -1
- package/dist/mcp/session.js.map +1 -1
- package/dist/ports/index.d.ts +125 -0
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/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 +8 -0
- package/dist/signing/signer.d.ts.map +1 -1
- package/dist/signing/signer.js +9 -0
- 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/vana.d.ts +2 -0
- package/dist/storage/adapters/vana.d.ts.map +1 -1
- package/dist/storage/adapters/vana.js +5 -1
- package/dist/storage/adapters/vana.js.map +1 -1
- package/dist/storage/index/types.d.ts +1 -0
- 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 +14 -4
- package/dist/sync/engine/sync-manager.d.ts.map +1 -1
- package/dist/sync/engine/sync-manager.js +53 -8
- 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/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/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 +461 -27
- package/dist/sync/workers/delete.js.map +1 -1
- package/dist/sync/workers/download.d.ts +38 -1
- package/dist/sync/workers/download.d.ts.map +1 -1
- package/dist/sync/workers/download.js +106 -2
- package/dist/sync/workers/download.js.map +1 -1
- package/dist/sync/workers/upload.d.ts +41 -7
- package/dist/sync/workers/upload.d.ts.map +1 -1
- package/dist/sync/workers/upload.js +147 -21
- package/dist/sync/workers/upload.js.map +1 -1
- package/dist/write/attribution.d.ts +6 -4
- package/dist/write/attribution.d.ts.map +1 -1
- package/dist/write/attribution.js +51 -3
- package/dist/write/attribution.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
function markerId(key) {
|
|
2
|
+
const range = key.range ? `${key.range.from}-${key.range.to}` : "";
|
|
3
|
+
return `${key.scope}\u0000${key.version ?? ""}\u0000${range}`;
|
|
4
|
+
}
|
|
5
|
+
function normalizeRange(value) {
|
|
6
|
+
if (typeof value !== "object" || value === null)
|
|
7
|
+
return undefined;
|
|
8
|
+
const { from, to } = value;
|
|
9
|
+
if (typeof from !== "string" ||
|
|
10
|
+
typeof to !== "string" ||
|
|
11
|
+
!/^\d+$/.test(from) ||
|
|
12
|
+
!/^\d+$/.test(to) ||
|
|
13
|
+
BigInt(from) > BigInt(to)) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
return { from, to };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Accept the stored value from either marker generation: exact keys
|
|
20
|
+
* (`{ scope, version }`) or the pre-key format that recorded whole scopes,
|
|
21
|
+
* which maps to a version-less marker the retry expands into exact keys.
|
|
22
|
+
*/
|
|
23
|
+
export function normalizePendingBlobDeletions(stored) {
|
|
24
|
+
if (!Array.isArray(stored))
|
|
25
|
+
return [];
|
|
26
|
+
const keys = [];
|
|
27
|
+
for (const item of stored) {
|
|
28
|
+
if (typeof item === "string") {
|
|
29
|
+
keys.push({ scope: item, version: null });
|
|
30
|
+
}
|
|
31
|
+
else if (typeof item === "object" &&
|
|
32
|
+
item !== null &&
|
|
33
|
+
typeof item.scope === "string") {
|
|
34
|
+
const version = item.version;
|
|
35
|
+
const range = typeof version === "string"
|
|
36
|
+
? undefined
|
|
37
|
+
: normalizeRange(item.range);
|
|
38
|
+
keys.push({
|
|
39
|
+
scope: item.scope,
|
|
40
|
+
version: typeof version === "string" ? version : null,
|
|
41
|
+
...(range ? { range } : {}),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return keys;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Every operation is a read-modify-write of the whole marker set, so the
|
|
49
|
+
* store serialises them: a delete recording keys while a sync cycle's retry
|
|
50
|
+
* clears others must not lose either update. Ordering is FIFO, and a failed
|
|
51
|
+
* operation never blocks the ones queued behind it.
|
|
52
|
+
*/
|
|
53
|
+
export function createPendingBlobDeletionStore(kv) {
|
|
54
|
+
let queue = Promise.resolve();
|
|
55
|
+
function serialized(operation) {
|
|
56
|
+
const run = queue.then(operation, operation);
|
|
57
|
+
queue = run.catch(() => undefined);
|
|
58
|
+
return run;
|
|
59
|
+
}
|
|
60
|
+
async function current() {
|
|
61
|
+
return normalizePendingBlobDeletions(await kv.read());
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
list() {
|
|
65
|
+
return serialized(current);
|
|
66
|
+
},
|
|
67
|
+
add(keys) {
|
|
68
|
+
return serialized(async () => {
|
|
69
|
+
if (keys.length === 0)
|
|
70
|
+
return;
|
|
71
|
+
const existing = await current();
|
|
72
|
+
const known = new Set(existing.map(markerId));
|
|
73
|
+
const next = [...existing];
|
|
74
|
+
for (const key of keys) {
|
|
75
|
+
const id = markerId(key);
|
|
76
|
+
if (known.has(id))
|
|
77
|
+
continue;
|
|
78
|
+
known.add(id);
|
|
79
|
+
next.push({
|
|
80
|
+
scope: key.scope,
|
|
81
|
+
version: key.version,
|
|
82
|
+
...(key.range ? { range: { ...key.range } } : {}),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (next.length === existing.length)
|
|
86
|
+
return;
|
|
87
|
+
await kv.write(next);
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
remove(keys) {
|
|
91
|
+
return serialized(async () => {
|
|
92
|
+
if (keys.length === 0)
|
|
93
|
+
return;
|
|
94
|
+
const existing = await current();
|
|
95
|
+
const gone = new Set(keys.map(markerId));
|
|
96
|
+
const next = existing.filter((key) => !gone.has(markerId(key)));
|
|
97
|
+
if (next.length === existing.length)
|
|
98
|
+
return;
|
|
99
|
+
await kv.write(next);
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** Non-durable store: markers survive only for the process lifetime. */
|
|
105
|
+
export function createMemoryPendingBlobDeletionStore() {
|
|
106
|
+
let keys = [];
|
|
107
|
+
return createPendingBlobDeletionStore({
|
|
108
|
+
async read() {
|
|
109
|
+
return keys;
|
|
110
|
+
},
|
|
111
|
+
async write(next) {
|
|
112
|
+
keys = next.map((key) => ({ ...key }));
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=pending-blob-deletions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-blob-deletions.js","sourceRoot":"","sources":["../../src/sync/pending-blob-deletions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { DataPointFeedPort, DataPointFeedRecord } from "../ports/index.js";
|
|
2
|
+
import type { IndexEntry } from "../storage/index/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Where a deletion verdict came from. `assumed-live` is the only source that
|
|
5
|
+
* is never verified: the tracker had no fact and could not reach the gateway.
|
|
6
|
+
*/
|
|
7
|
+
export type ScopeDeletionSource = "feed" | "gateway" | "local-delete" | "assumed-live";
|
|
8
|
+
export type ScopeDeletionVerdict = {
|
|
9
|
+
deleted: true;
|
|
10
|
+
deletedAt: string;
|
|
11
|
+
/**
|
|
12
|
+
* The tombstone's registry version, or null when the gateway did not
|
|
13
|
+
* say (a 410 without a body). Null covers every local entry.
|
|
14
|
+
*/
|
|
15
|
+
version: string | null;
|
|
16
|
+
source: Exclude<ScopeDeletionSource, "assumed-live">;
|
|
17
|
+
/**
|
|
18
|
+
* False when the tombstone is older than `maxStalenessMs`, the
|
|
19
|
+
* re-check could not reach the gateway, and the last known state is
|
|
20
|
+
* being served (fail-safe: refuse rather than resurrect).
|
|
21
|
+
*/
|
|
22
|
+
verified: boolean;
|
|
23
|
+
} | {
|
|
24
|
+
deleted: false;
|
|
25
|
+
source: ScopeDeletionSource;
|
|
26
|
+
/** False when the verdict is a fallback because the gateway was unreachable. */
|
|
27
|
+
verified: boolean;
|
|
28
|
+
};
|
|
29
|
+
export interface ScopeDeletionResolveOptions {
|
|
30
|
+
/**
|
|
31
|
+
* `if-stale` (default): trust a fresh feed pass or a recent per-scope
|
|
32
|
+
* verdict, and only ask the gateway when neither exists.
|
|
33
|
+
* `always`: ask the gateway unless a recent per-scope verdict exists. Used
|
|
34
|
+
* for local misses, where "never had it" and "deleted long ago" look the
|
|
35
|
+
* same to the local index and a fresh feed pass cannot tell them apart.
|
|
36
|
+
*/
|
|
37
|
+
consultGateway?: "if-stale" | "always";
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Per-process memory of which scopes the gateway has tombstoned, kept current
|
|
41
|
+
* by every component that talks to the registry, so reads can refuse deleted
|
|
42
|
+
* data without a gateway round-trip per request.
|
|
43
|
+
*
|
|
44
|
+
* Facts flow in from:
|
|
45
|
+
* - the download worker (every listed row, deleted or live, plus a
|
|
46
|
+
* "feed pass complete" marker when the listing had no further pages),
|
|
47
|
+
* - the upload worker (a successful registration means the scope is live),
|
|
48
|
+
* - the delete worker (a landed tombstone means the scope is deleted),
|
|
49
|
+
* - direct gateway lookups made by `resolve` itself.
|
|
50
|
+
*
|
|
51
|
+
* Every remembered verdict, live or deleted, ages out after `maxStalenessMs`
|
|
52
|
+
* and is then re-established from the feed or a gateway lookup. Consistency
|
|
53
|
+
* window, stated explicitly:
|
|
54
|
+
* - a tombstone landed by THIS replica is visible to its reads immediately;
|
|
55
|
+
* - a tombstone landed by ANOTHER replica is visible here once the download
|
|
56
|
+
* feed lists it (one sync poll interval while sync is healthy), or after
|
|
57
|
+
* at most `maxStalenessMs` plus one gateway lookup when the feed is not
|
|
58
|
+
* fresh (sync off, blocked, or a multi-page backlog);
|
|
59
|
+
* - a re-add on another replica after a tombstone becomes visible here on
|
|
60
|
+
* the same schedule: the feed lists the live row, or the tombstone ages
|
|
61
|
+
* out and the next read re-checks the gateway;
|
|
62
|
+
* - while the gateway is unreachable the tracker serves its last known
|
|
63
|
+
* state (`verified: false`) rather than failing every read; it re-checks
|
|
64
|
+
* after `gatewayRetryMs`. A partitioned replica therefore keeps serving
|
|
65
|
+
* its local copy (or keeps refusing a tombstoned scope) until
|
|
66
|
+
* connectivity returns, and never longer than `maxStalenessMs` after it
|
|
67
|
+
* does.
|
|
68
|
+
*/
|
|
69
|
+
export interface ScopeDeletionTracker {
|
|
70
|
+
/**
|
|
71
|
+
* Record a gateway tombstone for `scope`. `source` names who learned it
|
|
72
|
+
* (default "feed"; the delete worker passes "local-delete").
|
|
73
|
+
*/
|
|
74
|
+
markDeleted(scope: string, tombstone: ScopeTombstone, source?: Exclude<ScopeDeletionSource, "assumed-live">): void;
|
|
75
|
+
/** Record that the registry holds a live (non-tombstoned) row for `scope`. */
|
|
76
|
+
markLive(scope: string): void;
|
|
77
|
+
/**
|
|
78
|
+
* Record that a deletion-aware feed listing completed with no further
|
|
79
|
+
* pages: every tombstone up to `at` has been fed through `markDeleted`.
|
|
80
|
+
* `full` means the listing started from no cursor and therefore covered
|
|
81
|
+
* the whole registry, so a scope re-added since its tombstone would have
|
|
82
|
+
* been listed live (and marked live); only then are the remembered
|
|
83
|
+
* tombstones re-validated as of `at`. An incremental pass (`since` the
|
|
84
|
+
* cursor) proves nothing about tombstones it did not list, so those keep
|
|
85
|
+
* ageing and are re-checked at the gateway after `maxStalenessMs`.
|
|
86
|
+
*/
|
|
87
|
+
noteFeedSynced(at?: Date, options?: {
|
|
88
|
+
full?: boolean;
|
|
89
|
+
}): void;
|
|
90
|
+
/** Synchronous view: a known tombstone, or null when none is known. */
|
|
91
|
+
knownDeletion(scope: string): ScopeTombstone | null;
|
|
92
|
+
/** Milliseconds since the last complete feed pass, or null if none yet. */
|
|
93
|
+
feedAgeMs(): number | null;
|
|
94
|
+
/** Async verdict, consulting the gateway only as described above. */
|
|
95
|
+
resolve(scope: string, options?: ScopeDeletionResolveOptions): Promise<ScopeDeletionVerdict>;
|
|
96
|
+
readonly maxStalenessMs: number;
|
|
97
|
+
}
|
|
98
|
+
export interface ScopeTombstone {
|
|
99
|
+
deletedAt: string;
|
|
100
|
+
/** Registry version of the tombstone; null when unknown (covers everything). */
|
|
101
|
+
version: string | null;
|
|
102
|
+
}
|
|
103
|
+
export interface ScopeDeletionTrackerOptions {
|
|
104
|
+
/** Deletion-aware registry lookup for on-demand checks. Optional: without it the tracker only knows what it is told. */
|
|
105
|
+
feed?: DataPointFeedPort;
|
|
106
|
+
/** Data owner used for on-demand lookups. Optional for the same reason. */
|
|
107
|
+
serverOwner?: string;
|
|
108
|
+
/**
|
|
109
|
+
* How old a complete feed pass or a per-scope verdict (live or deleted)
|
|
110
|
+
* may be before `resolve` asks the gateway again. Default 120_000 (two
|
|
111
|
+
* default sync poll intervals, so one missed cycle does not turn every
|
|
112
|
+
* read into a lookup).
|
|
113
|
+
*/
|
|
114
|
+
maxStalenessMs?: number;
|
|
115
|
+
/** Back-off after a failed gateway lookup before trying again. Default 15_000. */
|
|
116
|
+
gatewayRetryMs?: number;
|
|
117
|
+
/** Upper bound on remembered live verdicts (probing unknown scopes must not grow memory without bound). Default 10_000. */
|
|
118
|
+
maxLiveEntries?: number;
|
|
119
|
+
now?: () => Date;
|
|
120
|
+
logger?: {
|
|
121
|
+
warn?(payload: Record<string, unknown>, message: string): void;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export declare const DEFAULT_SCOPE_DELETION_MAX_STALENESS_MS = 120000;
|
|
125
|
+
export declare const DEFAULT_SCOPE_DELETION_GATEWAY_RETRY_MS = 15000;
|
|
126
|
+
export declare function createScopeDeletionTracker(options?: ScopeDeletionTrackerOptions): ScopeDeletionTracker;
|
|
127
|
+
/**
|
|
128
|
+
* A tombstone is visible either as `deletedAt` (a feed that models deletion)
|
|
129
|
+
* or, from a gateway that lists tombstones as plain rows, as the tombstone
|
|
130
|
+
* commitments themselves; `addedAt` is then the deletion time.
|
|
131
|
+
*/
|
|
132
|
+
export declare function deletionTimestamp(record: (Pick<DataPointFeedRecord, "dataHash" | "metadataHash" | "addedAt"> & {
|
|
133
|
+
deletedAt?: string | null;
|
|
134
|
+
}) | null): string | null;
|
|
135
|
+
/**
|
|
136
|
+
* The tombstone's registry version as the gateway reported it, normalised to
|
|
137
|
+
* a positive decimal string, or null when it did not say (the 410 fallback
|
|
138
|
+
* synthesises "0").
|
|
139
|
+
*/
|
|
140
|
+
export declare function tombstoneVersion(record: Pick<DataPointFeedRecord, "expectedVersion"> | null): string | null;
|
|
141
|
+
/**
|
|
142
|
+
* True when a local index entry is part of what a gateway tombstone deleted,
|
|
143
|
+
* decided causally rather than by comparing clocks across machines:
|
|
144
|
+
* - a synced entry is covered when its registry version is at or below
|
|
145
|
+
* the tombstone's (the tombstone superseded it);
|
|
146
|
+
* - an unsynced entry is covered unless it was ingested with knowledge of
|
|
147
|
+
* this tombstone or a later one (`afterTombstoneVersion` >= tombstone
|
|
148
|
+
* version), which makes it a deliberate re-add;
|
|
149
|
+
* - a tombstone of unknown version covers everything.
|
|
150
|
+
* Data ingested concurrently with a deletion elsewhere (before this replica
|
|
151
|
+
* learned of it) is therefore covered too: refusing to resurrect is the
|
|
152
|
+
* failure this guards against, and the window is one sync poll interval.
|
|
153
|
+
*/
|
|
154
|
+
export declare function isEntryCoveredByTombstone(entry: Pick<IndexEntry, "version" | "dataPointId" | "afterTombstoneVersion">, tombstone: Pick<ScopeTombstone, "version">): boolean;
|
|
155
|
+
//# sourceMappingURL=scope-deletions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-deletions.d.ts","sourceRoot":"","sources":["../../src/sync/scope-deletions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { isTombstoneRecord } from "./tombstone.js";
|
|
2
|
+
export const DEFAULT_SCOPE_DELETION_MAX_STALENESS_MS = 120_000;
|
|
3
|
+
export const DEFAULT_SCOPE_DELETION_GATEWAY_RETRY_MS = 15_000;
|
|
4
|
+
const DEFAULT_MAX_LIVE_ENTRIES = 10_000;
|
|
5
|
+
export function createScopeDeletionTracker(options = {}) {
|
|
6
|
+
const maxStalenessMs = options.maxStalenessMs ?? DEFAULT_SCOPE_DELETION_MAX_STALENESS_MS;
|
|
7
|
+
const gatewayRetryMs = options.gatewayRetryMs ?? DEFAULT_SCOPE_DELETION_GATEWAY_RETRY_MS;
|
|
8
|
+
const maxLiveEntries = options.maxLiveEntries ?? DEFAULT_MAX_LIVE_ENTRIES;
|
|
9
|
+
const now = options.now ?? (() => new Date());
|
|
10
|
+
const nowMs = () => now().getTime();
|
|
11
|
+
// scope -> tombstone. Kept until the registry shows the scope live, but
|
|
12
|
+
// re-validated once older than maxStalenessMs (a re-add elsewhere must not
|
|
13
|
+
// leave this replica answering 410 forever).
|
|
14
|
+
const deleted = new Map();
|
|
15
|
+
// scope -> when a gateway lookup last confirmed the scope live (ms).
|
|
16
|
+
const live = new Map();
|
|
17
|
+
let lastFeedSyncMs = null;
|
|
18
|
+
let lastGatewayFailureMs = null;
|
|
19
|
+
// Coalesce concurrent lookups of the same scope into one request.
|
|
20
|
+
const inflight = new Map();
|
|
21
|
+
function rememberLive(scope, at) {
|
|
22
|
+
deleted.delete(scope);
|
|
23
|
+
live.delete(scope);
|
|
24
|
+
live.set(scope, at);
|
|
25
|
+
// Map preserves insertion order; the first key is the oldest verdict.
|
|
26
|
+
while (live.size > maxLiveEntries) {
|
|
27
|
+
const oldest = live.keys().next().value;
|
|
28
|
+
if (oldest === undefined)
|
|
29
|
+
break;
|
|
30
|
+
live.delete(oldest);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function rememberDeleted(scope, tombstone, source) {
|
|
34
|
+
deleted.set(scope, {
|
|
35
|
+
deletedAt: tombstone.deletedAt,
|
|
36
|
+
version: normalizeVersion(tombstone.version),
|
|
37
|
+
source,
|
|
38
|
+
verifiedAtMs: nowMs(),
|
|
39
|
+
});
|
|
40
|
+
live.delete(scope);
|
|
41
|
+
}
|
|
42
|
+
function isFresh(at) {
|
|
43
|
+
return at !== null && nowMs() - at <= maxStalenessMs;
|
|
44
|
+
}
|
|
45
|
+
function verdictFromRecord(scope, record) {
|
|
46
|
+
const deletedAt = deletionTimestamp(record);
|
|
47
|
+
if (deletedAt !== null) {
|
|
48
|
+
const version = tombstoneVersion(record);
|
|
49
|
+
rememberDeleted(scope, { deletedAt, version }, "gateway");
|
|
50
|
+
return {
|
|
51
|
+
deleted: true,
|
|
52
|
+
deletedAt,
|
|
53
|
+
version,
|
|
54
|
+
source: "gateway",
|
|
55
|
+
verified: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
rememberLive(scope, nowMs());
|
|
59
|
+
return { deleted: false, source: "gateway", verified: true };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Ask the registry. Null when it cannot be asked right now (no feed or
|
|
63
|
+
* owner wired, inside the failure back-off, or the request failed): the
|
|
64
|
+
* caller falls back to its last known state.
|
|
65
|
+
*/
|
|
66
|
+
async function lookup(scope) {
|
|
67
|
+
const feed = options.feed;
|
|
68
|
+
const owner = options.serverOwner;
|
|
69
|
+
if (!feed || !owner)
|
|
70
|
+
return null;
|
|
71
|
+
if (lastGatewayFailureMs !== null &&
|
|
72
|
+
nowMs() - lastGatewayFailureMs < gatewayRetryMs) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const pending = inflight.get(scope);
|
|
76
|
+
if (pending)
|
|
77
|
+
return pending;
|
|
78
|
+
const request = (async () => {
|
|
79
|
+
try {
|
|
80
|
+
const record = await feed.getDataPoint({
|
|
81
|
+
ownerAddress: owner,
|
|
82
|
+
scope,
|
|
83
|
+
});
|
|
84
|
+
lastGatewayFailureMs = null;
|
|
85
|
+
return verdictFromRecord(scope, record);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
lastGatewayFailureMs = nowMs();
|
|
89
|
+
options.logger?.warn?.({
|
|
90
|
+
scope,
|
|
91
|
+
error: err instanceof Error ? err.message : String(err),
|
|
92
|
+
retryAfterMs: gatewayRetryMs,
|
|
93
|
+
}, "Could not check gateway deletion state; serving last known state");
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
inflight.delete(scope);
|
|
98
|
+
}
|
|
99
|
+
})();
|
|
100
|
+
inflight.set(scope, request);
|
|
101
|
+
return request;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
maxStalenessMs,
|
|
105
|
+
markDeleted(scope, tombstone, source = "feed") {
|
|
106
|
+
rememberDeleted(scope, tombstone, source);
|
|
107
|
+
},
|
|
108
|
+
markLive(scope) {
|
|
109
|
+
rememberLive(scope, nowMs());
|
|
110
|
+
},
|
|
111
|
+
noteFeedSynced(at, options) {
|
|
112
|
+
lastFeedSyncMs = (at ?? now()).getTime();
|
|
113
|
+
if (!options?.full)
|
|
114
|
+
return;
|
|
115
|
+
// A full listing would have shown any re-add as a live row (and marked
|
|
116
|
+
// it live above), so every tombstone still here is confirmed. Tombstones
|
|
117
|
+
// an incremental pass happened to list were refreshed by markDeleted.
|
|
118
|
+
for (const tombstone of deleted.values()) {
|
|
119
|
+
tombstone.verifiedAtMs = Math.max(tombstone.verifiedAtMs, lastFeedSyncMs);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
knownDeletion(scope) {
|
|
123
|
+
const known = deleted.get(scope);
|
|
124
|
+
return known === undefined
|
|
125
|
+
? null
|
|
126
|
+
: { deletedAt: known.deletedAt, version: known.version };
|
|
127
|
+
},
|
|
128
|
+
feedAgeMs() {
|
|
129
|
+
return lastFeedSyncMs === null ? null : nowMs() - lastFeedSyncMs;
|
|
130
|
+
},
|
|
131
|
+
async resolve(scope, resolveOptions) {
|
|
132
|
+
const known = deleted.get(scope);
|
|
133
|
+
if (known !== undefined) {
|
|
134
|
+
if (isFresh(known.verifiedAtMs)) {
|
|
135
|
+
return {
|
|
136
|
+
deleted: true,
|
|
137
|
+
deletedAt: known.deletedAt,
|
|
138
|
+
version: known.version,
|
|
139
|
+
source: known.source,
|
|
140
|
+
verified: true,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Stale tombstone: the scope may have been re-added elsewhere. Ask;
|
|
144
|
+
// if the registry cannot be reached, keep refusing (never resurrect
|
|
145
|
+
// on a guess) and say the verdict is unverified.
|
|
146
|
+
const rechecked = await lookup(scope);
|
|
147
|
+
if (rechecked !== null)
|
|
148
|
+
return rechecked;
|
|
149
|
+
return {
|
|
150
|
+
deleted: true,
|
|
151
|
+
deletedAt: known.deletedAt,
|
|
152
|
+
version: known.version,
|
|
153
|
+
source: known.source,
|
|
154
|
+
verified: false,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (isFresh(live.get(scope) ?? null)) {
|
|
158
|
+
return { deleted: false, source: "gateway", verified: true };
|
|
159
|
+
}
|
|
160
|
+
const consult = resolveOptions?.consultGateway ?? "if-stale";
|
|
161
|
+
if (consult === "if-stale" && isFresh(lastFeedSyncMs)) {
|
|
162
|
+
return { deleted: false, source: "feed", verified: true };
|
|
163
|
+
}
|
|
164
|
+
return ((await lookup(scope)) ?? {
|
|
165
|
+
deleted: false,
|
|
166
|
+
source: "assumed-live",
|
|
167
|
+
verified: false,
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* A tombstone is visible either as `deletedAt` (a feed that models deletion)
|
|
174
|
+
* or, from a gateway that lists tombstones as plain rows, as the tombstone
|
|
175
|
+
* commitments themselves; `addedAt` is then the deletion time.
|
|
176
|
+
*/
|
|
177
|
+
export function deletionTimestamp(record) {
|
|
178
|
+
if (!record)
|
|
179
|
+
return null;
|
|
180
|
+
if (record.deletedAt)
|
|
181
|
+
return record.deletedAt;
|
|
182
|
+
return isTombstoneRecord(record) ? record.addedAt : null;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The tombstone's registry version as the gateway reported it, normalised to
|
|
186
|
+
* a positive decimal string, or null when it did not say (the 410 fallback
|
|
187
|
+
* synthesises "0").
|
|
188
|
+
*/
|
|
189
|
+
export function tombstoneVersion(record) {
|
|
190
|
+
return normalizeVersion(record?.expectedVersion ?? null);
|
|
191
|
+
}
|
|
192
|
+
function normalizeVersion(value) {
|
|
193
|
+
if (typeof value !== "string" || !/^\d+$/.test(value))
|
|
194
|
+
return null;
|
|
195
|
+
return BigInt(value) > 0n ? BigInt(value).toString() : null;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* True when a local index entry is part of what a gateway tombstone deleted,
|
|
199
|
+
* decided causally rather than by comparing clocks across machines:
|
|
200
|
+
* - a synced entry is covered when its registry version is at or below
|
|
201
|
+
* the tombstone's (the tombstone superseded it);
|
|
202
|
+
* - an unsynced entry is covered unless it was ingested with knowledge of
|
|
203
|
+
* this tombstone or a later one (`afterTombstoneVersion` >= tombstone
|
|
204
|
+
* version), which makes it a deliberate re-add;
|
|
205
|
+
* - a tombstone of unknown version covers everything.
|
|
206
|
+
* Data ingested concurrently with a deletion elsewhere (before this replica
|
|
207
|
+
* learned of it) is therefore covered too: refusing to resurrect is the
|
|
208
|
+
* failure this guards against, and the window is one sync poll interval.
|
|
209
|
+
*/
|
|
210
|
+
export function isEntryCoveredByTombstone(entry, tombstone) {
|
|
211
|
+
const version = normalizeVersion(tombstone.version);
|
|
212
|
+
if (version === null)
|
|
213
|
+
return true;
|
|
214
|
+
const tombstoned = BigInt(version);
|
|
215
|
+
if (entry.dataPointId !== null)
|
|
216
|
+
return BigInt(entry.version) <= tombstoned;
|
|
217
|
+
const marker = entry.afterTombstoneVersion;
|
|
218
|
+
if (marker === null ||
|
|
219
|
+
marker === undefined ||
|
|
220
|
+
!Number.isSafeInteger(marker)) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
return BigInt(marker) < tombstoned;
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=scope-deletions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-deletions.js","sourceRoot":"","sources":["../../src/sync/scope-deletions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable-deletion tombstone commitments.
|
|
3
|
+
*
|
|
4
|
+
* A deletion is an owner-signed DPv2 AddData for version `current + 1` whose
|
|
5
|
+
* dataHash / metadataHash are these well-known constants. The gateway, the
|
|
6
|
+
* SDK and every personal server derive the SAME bytes from the same UTF-8
|
|
7
|
+
* labels, so a tombstone is recognisable on-chain and in every feed without
|
|
8
|
+
* any out-of-band flag. Do not change the labels: they are the cross-repo
|
|
9
|
+
* contract.
|
|
10
|
+
*
|
|
11
|
+
* TODO(vana-sdk > 3.14.0): the SDK now exports TOMBSTONE_DATA_HASH,
|
|
12
|
+
* TOMBSTONE_METADATA_HASH, isTombstoneHashes(), isDataPointTombstone() and
|
|
13
|
+
* deriveDataPointId() with these exact values. Switch to those exports and
|
|
14
|
+
* drop the local copies once the PS pins a release that ships them.
|
|
15
|
+
*/
|
|
16
|
+
export declare const TOMBSTONE_DATA_HASH_LABEL = "vana.data-point.tombstone.v1";
|
|
17
|
+
export declare const TOMBSTONE_METADATA_HASH_LABEL = "vana.data-point.tombstone.metadata.v1";
|
|
18
|
+
/** keccak256(utf8("vana.data-point.tombstone.v1")) */
|
|
19
|
+
export declare const TOMBSTONE_DATA_HASH: `0x${string}`;
|
|
20
|
+
/** keccak256(utf8("vana.data-point.tombstone.metadata.v1")) */
|
|
21
|
+
export declare const TOMBSTONE_METADATA_HASH: `0x${string}`;
|
|
22
|
+
/** True when a data-point record's commitments are the tombstone constants. */
|
|
23
|
+
export declare function isTombstoneRecord(record: {
|
|
24
|
+
dataHash: string;
|
|
25
|
+
metadataHash: string;
|
|
26
|
+
}): boolean;
|
|
27
|
+
//# sourceMappingURL=tombstone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tombstone.d.ts","sourceRoot":"","sources":["../../src/sync/tombstone.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yBAAyB,iCAAiC,CAAC;AACxE,eAAO,MAAM,6BAA6B,0CACD,CAAC;AAE1C,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,EAAE,KAAK,MAAM,EAE5C,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,uBAAuB,EAAE,KAAK,MAAM,EAEhD,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAKV"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { keccak256, stringToHex } from "viem";
|
|
2
|
+
/**
|
|
3
|
+
* Durable-deletion tombstone commitments.
|
|
4
|
+
*
|
|
5
|
+
* A deletion is an owner-signed DPv2 AddData for version `current + 1` whose
|
|
6
|
+
* dataHash / metadataHash are these well-known constants. The gateway, the
|
|
7
|
+
* SDK and every personal server derive the SAME bytes from the same UTF-8
|
|
8
|
+
* labels, so a tombstone is recognisable on-chain and in every feed without
|
|
9
|
+
* any out-of-band flag. Do not change the labels: they are the cross-repo
|
|
10
|
+
* contract.
|
|
11
|
+
*
|
|
12
|
+
* TODO(vana-sdk > 3.14.0): the SDK now exports TOMBSTONE_DATA_HASH,
|
|
13
|
+
* TOMBSTONE_METADATA_HASH, isTombstoneHashes(), isDataPointTombstone() and
|
|
14
|
+
* deriveDataPointId() with these exact values. Switch to those exports and
|
|
15
|
+
* drop the local copies once the PS pins a release that ships them.
|
|
16
|
+
*/
|
|
17
|
+
export const TOMBSTONE_DATA_HASH_LABEL = "vana.data-point.tombstone.v1";
|
|
18
|
+
export const TOMBSTONE_METADATA_HASH_LABEL = "vana.data-point.tombstone.metadata.v1";
|
|
19
|
+
/** keccak256(utf8("vana.data-point.tombstone.v1")) */
|
|
20
|
+
export const TOMBSTONE_DATA_HASH = keccak256(stringToHex(TOMBSTONE_DATA_HASH_LABEL));
|
|
21
|
+
/** keccak256(utf8("vana.data-point.tombstone.metadata.v1")) */
|
|
22
|
+
export const TOMBSTONE_METADATA_HASH = keccak256(stringToHex(TOMBSTONE_METADATA_HASH_LABEL));
|
|
23
|
+
/** True when a data-point record's commitments are the tombstone constants. */
|
|
24
|
+
export function isTombstoneRecord(record) {
|
|
25
|
+
return (record.dataHash.toLowerCase() === TOMBSTONE_DATA_HASH &&
|
|
26
|
+
record.metadataHash.toLowerCase() === TOMBSTONE_METADATA_HASH);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=tombstone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tombstone.js","sourceRoot":"","sources":["../../src/sync/tombstone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAE9C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,8BAA8B,CAAC;AACxE,MAAM,CAAC,MAAM,6BAA6B,GACxC,uCAAuC,CAAC;AAE1C,sDAAsD;AACtD,MAAM,CAAC,MAAM,mBAAmB,GAAkB,SAAS,CACzD,WAAW,CAAC,yBAAyB,CAAC,CACvC,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,uBAAuB,GAAkB,SAAS,CAC7D,WAAW,CAAC,6BAA6B,CAAC,CAC3C,CAAC;AAEF,+EAA+E;AAC/E,MAAM,UAAU,iBAAiB,CAAC,MAGjC;IACC,OAAO,CACL,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,mBAAmB;QACrD,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,uBAAuB,CAC9D,CAAC;AACJ,CAAC"}
|