@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.
Files changed (106) hide show
  1. package/dist/api/index.d.ts +53 -2
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +300 -25
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/contracts/data.d.ts +30 -1
  6. package/dist/contracts/data.d.ts.map +1 -1
  7. package/dist/contracts/data.js +126 -33
  8. package/dist/contracts/data.js.map +1 -1
  9. package/dist/errors/catalog.d.ts +40 -0
  10. package/dist/errors/catalog.d.ts.map +1 -1
  11. package/dist/errors/catalog.js +53 -0
  12. package/dist/errors/catalog.js.map +1 -1
  13. package/dist/lineage/attestation.d.ts +42 -0
  14. package/dist/lineage/attestation.d.ts.map +1 -0
  15. package/dist/lineage/attestation.js +25 -0
  16. package/dist/lineage/attestation.js.map +1 -0
  17. package/dist/lineage/gateway.d.ts +105 -0
  18. package/dist/lineage/gateway.d.ts.map +1 -0
  19. package/dist/lineage/gateway.js +242 -0
  20. package/dist/lineage/gateway.js.map +1 -0
  21. package/dist/lineage/index.d.ts +5 -0
  22. package/dist/lineage/index.d.ts.map +1 -0
  23. package/dist/lineage/index.js +5 -0
  24. package/dist/lineage/index.js.map +1 -0
  25. package/dist/lineage/lineage.d.ts +141 -0
  26. package/dist/lineage/lineage.d.ts.map +1 -0
  27. package/dist/lineage/lineage.js +255 -0
  28. package/dist/lineage/lineage.js.map +1 -0
  29. package/dist/logging/access-log.d.ts +7 -1
  30. package/dist/logging/access-log.d.ts.map +1 -1
  31. package/dist/mcp/read-client.d.ts.map +1 -1
  32. package/dist/mcp/read-client.js +25 -1
  33. package/dist/mcp/read-client.js.map +1 -1
  34. package/dist/mcp/session.d.ts.map +1 -1
  35. package/dist/mcp/session.js +10 -1
  36. package/dist/mcp/session.js.map +1 -1
  37. package/dist/ports/index.d.ts +125 -0
  38. package/dist/ports/index.d.ts.map +1 -1
  39. package/dist/ports/index.js.map +1 -1
  40. package/dist/signing/request-signer.d.ts +2 -0
  41. package/dist/signing/request-signer.d.ts.map +1 -1
  42. package/dist/signing/request-signer.js +1 -0
  43. package/dist/signing/request-signer.js.map +1 -1
  44. package/dist/signing/signer.d.ts +8 -0
  45. package/dist/signing/signer.d.ts.map +1 -1
  46. package/dist/signing/signer.js +9 -0
  47. package/dist/signing/signer.js.map +1 -1
  48. package/dist/storage/adapters/index.d.ts +1 -1
  49. package/dist/storage/adapters/index.d.ts.map +1 -1
  50. package/dist/storage/adapters/index.js +1 -1
  51. package/dist/storage/adapters/index.js.map +1 -1
  52. package/dist/storage/adapters/vana.d.ts +2 -0
  53. package/dist/storage/adapters/vana.d.ts.map +1 -1
  54. package/dist/storage/adapters/vana.js +5 -1
  55. package/dist/storage/adapters/vana.js.map +1 -1
  56. package/dist/storage/index/types.d.ts +1 -0
  57. package/dist/storage/index/types.d.ts.map +1 -1
  58. package/dist/sync/data-point-feed.d.ts +35 -0
  59. package/dist/sync/data-point-feed.d.ts.map +1 -0
  60. package/dist/sync/data-point-feed.js +122 -0
  61. package/dist/sync/data-point-feed.js.map +1 -0
  62. package/dist/sync/data-point-id.d.ts +7 -0
  63. package/dist/sync/data-point-id.d.ts.map +1 -0
  64. package/dist/sync/data-point-id.js +16 -0
  65. package/dist/sync/data-point-id.js.map +1 -0
  66. package/dist/sync/delete-data-port.d.ts +50 -0
  67. package/dist/sync/delete-data-port.d.ts.map +1 -0
  68. package/dist/sync/delete-data-port.js +244 -0
  69. package/dist/sync/delete-data-port.js.map +1 -0
  70. package/dist/sync/engine/sync-manager.d.ts +14 -4
  71. package/dist/sync/engine/sync-manager.d.ts.map +1 -1
  72. package/dist/sync/engine/sync-manager.js +53 -8
  73. package/dist/sync/engine/sync-manager.js.map +1 -1
  74. package/dist/sync/index.d.ts +7 -0
  75. package/dist/sync/index.d.ts.map +1 -1
  76. package/dist/sync/index.js +7 -0
  77. package/dist/sync/index.js.map +1 -1
  78. package/dist/sync/pending-blob-deletions.d.ts +29 -0
  79. package/dist/sync/pending-blob-deletions.d.ts.map +1 -0
  80. package/dist/sync/pending-blob-deletions.js +116 -0
  81. package/dist/sync/pending-blob-deletions.js.map +1 -0
  82. package/dist/sync/scope-deletions.d.ts +155 -0
  83. package/dist/sync/scope-deletions.d.ts.map +1 -0
  84. package/dist/sync/scope-deletions.js +225 -0
  85. package/dist/sync/scope-deletions.js.map +1 -0
  86. package/dist/sync/tombstone.d.ts +27 -0
  87. package/dist/sync/tombstone.d.ts.map +1 -0
  88. package/dist/sync/tombstone.js +28 -0
  89. package/dist/sync/tombstone.js.map +1 -0
  90. package/dist/sync/workers/delete.d.ts +152 -31
  91. package/dist/sync/workers/delete.d.ts.map +1 -1
  92. package/dist/sync/workers/delete.js +461 -27
  93. package/dist/sync/workers/delete.js.map +1 -1
  94. package/dist/sync/workers/download.d.ts +38 -1
  95. package/dist/sync/workers/download.d.ts.map +1 -1
  96. package/dist/sync/workers/download.js +106 -2
  97. package/dist/sync/workers/download.js.map +1 -1
  98. package/dist/sync/workers/upload.d.ts +41 -7
  99. package/dist/sync/workers/upload.d.ts.map +1 -1
  100. package/dist/sync/workers/upload.js +147 -21
  101. package/dist/sync/workers/upload.js.map +1 -1
  102. package/dist/write/attribution.d.ts +6 -4
  103. package/dist/write/attribution.d.ts.map +1 -1
  104. package/dist/write/attribution.js +51 -3
  105. package/dist/write/attribution.js.map +1 -1
  106. 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"}