@kubun/store-connector 0.17.1 → 0.18.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/lib/api-types.d.ts +434 -0
- package/lib/api-types.js +2 -0
- package/lib/api.d.ts +4 -60
- package/lib/api.js +118 -173
- package/lib/definition.d.ts +7 -2
- package/lib/definition.js +11 -5
- package/lib/document-sweep.d.ts +8 -0
- package/lib/document-sweep.js +46 -0
- package/lib/entity-trim.d.ts +4 -0
- package/lib/entity-trim.js +36 -0
- package/lib/index.d.ts +6 -3
- package/lib/index.js +4 -1
- package/lib/migrations.js +119 -1
- package/lib/observation-records.d.ts +52 -0
- package/lib/observation-records.js +310 -0
- package/lib/operations.d.ts +4 -0
- package/lib/operations.js +223 -0
- package/lib/pending-auth.d.ts +4 -0
- package/lib/pending-auth.js +69 -0
- package/lib/projection-watches.d.ts +4 -0
- package/lib/projection-watches.js +265 -0
- package/lib/retention.d.ts +4 -0
- package/lib/retention.js +193 -0
- package/lib/serialization.d.ts +63 -0
- package/lib/serialization.js +79 -0
- package/lib/store-context.d.ts +11 -0
- package/lib/store-context.js +1 -0
- package/lib/sweep-state.d.ts +5 -0
- package/lib/sweep-state.js +180 -0
- package/lib/sync-state.d.ts +4 -0
- package/lib/sync-state.js +107 -0
- package/lib/tables.d.ts +127 -0
- package/package.json +13 -7
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { ObservationRecordMethods } from './observation-records.js';
|
|
3
|
+
import type { ConnectorTables } from './tables.js';
|
|
4
|
+
export type SyncStatus = 'idle' | 'syncing' | 'error';
|
|
5
|
+
export type SyncState = {
|
|
6
|
+
checkpoint: unknown;
|
|
7
|
+
lastSyncedAt: string;
|
|
8
|
+
entityCount: number;
|
|
9
|
+
status: SyncStatus;
|
|
10
|
+
error?: string;
|
|
11
|
+
leaseExpiresAt?: number | null;
|
|
12
|
+
recovery?: unknown;
|
|
13
|
+
version?: number;
|
|
14
|
+
incarnation?: string;
|
|
15
|
+
};
|
|
16
|
+
export type SyncStateEntry = SyncState & {
|
|
17
|
+
connectorName: string;
|
|
18
|
+
ownerDID: string;
|
|
19
|
+
};
|
|
20
|
+
export type SweepKey = {
|
|
21
|
+
connectorName: string;
|
|
22
|
+
ownerDID: string;
|
|
23
|
+
};
|
|
24
|
+
export type SweepReceiptKey = SweepKey & {
|
|
25
|
+
partition: string;
|
|
26
|
+
provenance: string;
|
|
27
|
+
};
|
|
28
|
+
export type SweepCandidateKey = SweepReceiptKey & {
|
|
29
|
+
documentID: string;
|
|
30
|
+
};
|
|
31
|
+
export type SweepReceipt = SweepReceiptKey & {
|
|
32
|
+
modelID: string;
|
|
33
|
+
scope: Record<string, string>;
|
|
34
|
+
keep: Array<string>;
|
|
35
|
+
sourceFetchHLC: string;
|
|
36
|
+
predicate: {
|
|
37
|
+
field: string;
|
|
38
|
+
from: string | null;
|
|
39
|
+
to: string | null;
|
|
40
|
+
} | null;
|
|
41
|
+
complete: boolean;
|
|
42
|
+
kind: 'relist' | 'partition-removal';
|
|
43
|
+
};
|
|
44
|
+
export type SweepCandidateInput = {
|
|
45
|
+
documentID: string;
|
|
46
|
+
sourceID: string;
|
|
47
|
+
modelID: string;
|
|
48
|
+
};
|
|
49
|
+
export type SweepCandidate = SweepCandidateKey & SweepCandidateInput & {
|
|
50
|
+
state: 'pending' | 'present' | 'absent' | 'cancelled' | 'uncertain' | 'applied' | 'refused';
|
|
51
|
+
attempts: number;
|
|
52
|
+
applyFailures: number;
|
|
53
|
+
lastError: string | null;
|
|
54
|
+
updatedAt: number;
|
|
55
|
+
nextAttemptAt: number;
|
|
56
|
+
};
|
|
57
|
+
export type SweepOutcomeInput = SweepCandidateKey & {
|
|
58
|
+
outcome: Exclude<SweepCandidate['state'], 'pending' | 'applied' | 'refused'>;
|
|
59
|
+
lastError?: string;
|
|
60
|
+
nextAttemptAt?: number;
|
|
61
|
+
};
|
|
62
|
+
export type SweepApplyFailureInput = SweepCandidateKey & {
|
|
63
|
+
lastError: string;
|
|
64
|
+
nextAttemptAt: number;
|
|
65
|
+
};
|
|
66
|
+
export type SyncProgressCASCurrent = {
|
|
67
|
+
checkpoint: unknown;
|
|
68
|
+
recovery: unknown;
|
|
69
|
+
version: number;
|
|
70
|
+
incarnation: string;
|
|
71
|
+
};
|
|
72
|
+
export type SyncProgressCASResult = {
|
|
73
|
+
checkpoint: unknown;
|
|
74
|
+
recovery: unknown;
|
|
75
|
+
lastSyncedAt: string;
|
|
76
|
+
entityCount: number;
|
|
77
|
+
error?: string | null;
|
|
78
|
+
};
|
|
79
|
+
export type SyncProgressRecompute = (current: SyncProgressCASCurrent | null) => SyncProgressCASResult | Promise<SyncProgressCASResult>;
|
|
80
|
+
export type UpdateSyncProgressParams = {
|
|
81
|
+
connectorName: string;
|
|
82
|
+
ownerDID: string;
|
|
83
|
+
recompute: SyncProgressRecompute;
|
|
84
|
+
options?: {
|
|
85
|
+
maxRetries?: number;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
export type EntityTrimInput = {
|
|
89
|
+
documentID: string;
|
|
90
|
+
ownerDID: string;
|
|
91
|
+
connectorName: string;
|
|
92
|
+
trimmedFields: unknown;
|
|
93
|
+
};
|
|
94
|
+
export type EntityTrimRecord = EntityTrimInput & {
|
|
95
|
+
updatedAt: number;
|
|
96
|
+
};
|
|
97
|
+
export type PendingAuthRecord = {
|
|
98
|
+
state: string;
|
|
99
|
+
codeVerifier: string;
|
|
100
|
+
provider: string;
|
|
101
|
+
ownerDID: string;
|
|
102
|
+
/** The owner's DID in a form that can be encrypted to. See the table type. */
|
|
103
|
+
ownerWrappableDID?: string | null;
|
|
104
|
+
/** A recovery recipient's DID in a form that can be encrypted to. See the table type. */
|
|
105
|
+
controllerWrappableDID?: string | null;
|
|
106
|
+
createdAt: number;
|
|
107
|
+
/** The callback's redirect target, needed to complete the token exchange. */
|
|
108
|
+
redirectURL: string;
|
|
109
|
+
/** The scopes requested at authorize time. */
|
|
110
|
+
scopes: Array<string>;
|
|
111
|
+
/** Whether start-auth requested scopes incrementally. */
|
|
112
|
+
incremental: boolean;
|
|
113
|
+
expiresAt: number;
|
|
114
|
+
};
|
|
115
|
+
export type ConnectorOperationOp = 'create' | 'update';
|
|
116
|
+
export type ConnectorOperationState = 'attempted' | 'committed' | 'reconcile_only' | 'dead';
|
|
117
|
+
export type ClaimOperationInput = {
|
|
118
|
+
callerDID: string;
|
|
119
|
+
operationID: string;
|
|
120
|
+
ownerDID: string;
|
|
121
|
+
connectorName: string;
|
|
122
|
+
model: string;
|
|
123
|
+
op: ConnectorOperationOp;
|
|
124
|
+
accountKey: string;
|
|
125
|
+
target: string;
|
|
126
|
+
sourceID: string | null;
|
|
127
|
+
deterministicEventID: string;
|
|
128
|
+
payloadHash: string;
|
|
129
|
+
reissueIntent: unknown;
|
|
130
|
+
/**
|
|
131
|
+
* The provider's conditional-write token (e.g. an ETag) of the state the
|
|
132
|
+
* write is based on, pinned for every attempt of the operation. Captured
|
|
133
|
+
* state rather than part of the envelope: a replay never compares it.
|
|
134
|
+
*/
|
|
135
|
+
providerEtag?: string | null;
|
|
136
|
+
/**
|
|
137
|
+
* The document the operation writes, when it is known before any provider
|
|
138
|
+
* I/O (an update's target, a create whose provider id is predetermined).
|
|
139
|
+
* Recorded on the first claim only; like `providerEtag`, a replay never
|
|
140
|
+
* compares it. Finalize records the document the write actually projected
|
|
141
|
+
* into, which takes precedence.
|
|
142
|
+
*/
|
|
143
|
+
documentID?: string | null;
|
|
144
|
+
leaseToken: string;
|
|
145
|
+
leaseExpiresAt: number;
|
|
146
|
+
};
|
|
147
|
+
export type RecordOperationInput = {
|
|
148
|
+
callerDID: string;
|
|
149
|
+
operationID: string;
|
|
150
|
+
fromState: ConnectorOperationState;
|
|
151
|
+
toState: Exclude<ConnectorOperationState, 'committed'>;
|
|
152
|
+
version: number;
|
|
153
|
+
leaseToken: string;
|
|
154
|
+
providerEtag?: string | null;
|
|
155
|
+
nextAttemptAt?: number | null;
|
|
156
|
+
kind?: string | null;
|
|
157
|
+
httpStatus?: number | null;
|
|
158
|
+
/**
|
|
159
|
+
* Whether this transition consumes a recovery attempt (default `true`).
|
|
160
|
+
* `false` records a failure's classification against an attempt already
|
|
161
|
+
* consumed earlier in the same recovery pass.
|
|
162
|
+
*/
|
|
163
|
+
countAttempt?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Gives back the attempt this recovery pass consumed (`attempts - 1`), for a
|
|
166
|
+
* pass that turned out never to reach the provider. Takes precedence over
|
|
167
|
+
* `countAttempt`.
|
|
168
|
+
*/
|
|
169
|
+
refundAttempt?: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Marks the `nextAttemptAt` this records as a deferral that lapses on its
|
|
172
|
+
* own (see `DeferReason`). Omitted clears any earlier mark.
|
|
173
|
+
*/
|
|
174
|
+
deferReason?: DeferReason;
|
|
175
|
+
/**
|
|
176
|
+
* With `refundAttempt`: the marker of the run asking (see
|
|
177
|
+
* `markSendStarted`). The refund is refused when a different run started
|
|
178
|
+
* a provider write in this attempt, including when this run marked too.
|
|
179
|
+
*/
|
|
180
|
+
sendMarker?: string;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* A deferral that lapses on its own: `'offline'` — the device was offline,
|
|
184
|
+
* made due again on reconnect (`resumeOfflineDeferred`); `'auth'` — the
|
|
185
|
+
* owner's credential could not authorize the write (none stored, or refused by
|
|
186
|
+
* the provider), made due again once the owner authorizes
|
|
187
|
+
* (`resumeAuthDeferred`).
|
|
188
|
+
*/
|
|
189
|
+
export type DeferReason = 'offline' | 'auth';
|
|
190
|
+
export type FinalizeOperationInput = {
|
|
191
|
+
key: {
|
|
192
|
+
callerDID: string;
|
|
193
|
+
operationID: string;
|
|
194
|
+
};
|
|
195
|
+
version: number;
|
|
196
|
+
leaseToken: string;
|
|
197
|
+
documentID: string;
|
|
198
|
+
resultEntity: unknown;
|
|
199
|
+
watchIdentity: ProjectionWatchIdentity;
|
|
200
|
+
currentWinnerDigest: string | null;
|
|
201
|
+
};
|
|
202
|
+
export type ConnectorOperation = {
|
|
203
|
+
callerDID: string;
|
|
204
|
+
operationID: string;
|
|
205
|
+
/**
|
|
206
|
+
* Opaque random key assigned when the operation was first claimed: its
|
|
207
|
+
* public identity, unlike `operationID`, which is the caller's idempotency
|
|
208
|
+
* key.
|
|
209
|
+
*/
|
|
210
|
+
nodeKey: string;
|
|
211
|
+
ownerDID: string;
|
|
212
|
+
connectorName: string;
|
|
213
|
+
model: string;
|
|
214
|
+
op: ConnectorOperationOp;
|
|
215
|
+
accountKey: string;
|
|
216
|
+
target: string;
|
|
217
|
+
sourceID: string | null;
|
|
218
|
+
deterministicEventID: string;
|
|
219
|
+
payloadHash: string;
|
|
220
|
+
reissueIntent: unknown;
|
|
221
|
+
providerEtag: string | null;
|
|
222
|
+
documentID: string | null;
|
|
223
|
+
resultEntity: unknown;
|
|
224
|
+
state: ConnectorOperationState;
|
|
225
|
+
attempts: number;
|
|
226
|
+
nextAttemptAt: number | null;
|
|
227
|
+
lastKind: string | null;
|
|
228
|
+
lastHTTPStatus: number | null;
|
|
229
|
+
version: number;
|
|
230
|
+
incarnation: string | null;
|
|
231
|
+
leaseToken: string | null;
|
|
232
|
+
leaseExpiresAt: number | null;
|
|
233
|
+
createdAt: number;
|
|
234
|
+
updatedAt: number;
|
|
235
|
+
};
|
|
236
|
+
export type AcquireLeaseParams = {
|
|
237
|
+
callerDID: string;
|
|
238
|
+
operationID: string;
|
|
239
|
+
token: string;
|
|
240
|
+
expiresAt: number;
|
|
241
|
+
};
|
|
242
|
+
export type RenewLeaseParams = {
|
|
243
|
+
callerDID: string;
|
|
244
|
+
operationID: string;
|
|
245
|
+
token: string;
|
|
246
|
+
expiresAt: number;
|
|
247
|
+
};
|
|
248
|
+
export type ReleaseLeaseParams = {
|
|
249
|
+
callerDID: string;
|
|
250
|
+
operationID: string;
|
|
251
|
+
token: string;
|
|
252
|
+
};
|
|
253
|
+
export type ProjectionWatchIdentity = {
|
|
254
|
+
ownerDID: string;
|
|
255
|
+
connectorName: string;
|
|
256
|
+
model: string;
|
|
257
|
+
accountKey: string;
|
|
258
|
+
sourceID: string;
|
|
259
|
+
};
|
|
260
|
+
export type ProjectionWatchVerificationStatus = 'pending_verification' | 'verified' | 'repair_failed';
|
|
261
|
+
export type ProjectionWatch = {
|
|
262
|
+
ownerDID: string;
|
|
263
|
+
connectorName: string;
|
|
264
|
+
model: string;
|
|
265
|
+
accountKey: string;
|
|
266
|
+
sourceID: string;
|
|
267
|
+
documentID: string;
|
|
268
|
+
verifiedStateDigest: string | null;
|
|
269
|
+
observedWinnerDigest: string | null;
|
|
270
|
+
verificationStatus: ProjectionWatchVerificationStatus;
|
|
271
|
+
verificationAttempts: number;
|
|
272
|
+
repairGeneration: number;
|
|
273
|
+
nextAttemptAt: number | null;
|
|
274
|
+
version: number;
|
|
275
|
+
createdAt: number;
|
|
276
|
+
updatedAt: number;
|
|
277
|
+
};
|
|
278
|
+
/**
|
|
279
|
+
* Filters for `listTrackedOperations`, all optional and combined with AND. An
|
|
280
|
+
* empty `states` or `documentIDs` list matches nothing.
|
|
281
|
+
*/
|
|
282
|
+
export type TrackedOperationFilter = {
|
|
283
|
+
nodeKey?: string;
|
|
284
|
+
callerDID?: string;
|
|
285
|
+
operationID?: string;
|
|
286
|
+
/** Operations the DID claimed as caller, or owns as credential owner. */
|
|
287
|
+
visibleTo?: string;
|
|
288
|
+
connectorName?: string;
|
|
289
|
+
states?: Array<ConnectorOperationState>;
|
|
290
|
+
/** Operations linked to one of these documents. */
|
|
291
|
+
documentIDs?: Array<string>;
|
|
292
|
+
};
|
|
293
|
+
export type ArmWatchReason = 'finalize' | 'ingress' | 'manual';
|
|
294
|
+
export type ArmWatchOutcome = 'opened' | 'coalesced' | 'noop';
|
|
295
|
+
export type ArmWatchOptions = {
|
|
296
|
+
triggeringWinner?: string;
|
|
297
|
+
dueAt: number;
|
|
298
|
+
};
|
|
299
|
+
export type RecordVerificationInput = {
|
|
300
|
+
fetchedDigest: string | null;
|
|
301
|
+
generation: number;
|
|
302
|
+
maxAttempts: number;
|
|
303
|
+
dueAt: number;
|
|
304
|
+
claimedUntil?: number;
|
|
305
|
+
};
|
|
306
|
+
export type RecordVerificationOutcome = 'verified' | 'pending' | 'repair_failed' | 'stale-generation' | 'stale-claim';
|
|
307
|
+
/**
|
|
308
|
+
* Reads a document's current winner state digest (`W`) INSIDE the caller's
|
|
309
|
+
* transaction — wraps `readWinnerStateDigest(tx, documentID)`
|
|
310
|
+
* (exported from `@kubun/store-graph`). Graph and connector tables share one
|
|
311
|
+
* DB/connection via the KubunDB coordinator, so the wired implementation
|
|
312
|
+
* must be invoked with the `tx` handed to this callback, never a top-level
|
|
313
|
+
* `db` — passing the top-level db would nest a transaction and, on SQLite's
|
|
314
|
+
* single connection, deadlock.
|
|
315
|
+
*/
|
|
316
|
+
export type ReadWinner = (documentID: string, tx: Kysely<ConnectorTables>) => Promise<string>;
|
|
317
|
+
/**
|
|
318
|
+
* Batch sibling of `ReadWinner`: the winner digests of many documents in one
|
|
319
|
+
* pass. A document whose winner cannot be read is absent from the map, where
|
|
320
|
+
* `ReadWinner` would throw.
|
|
321
|
+
*/
|
|
322
|
+
export type ReadWinners = (documentIDs: ReadonlyArray<string>, tx: Kysely<ConnectorTables>) => Promise<Map<string, string>>;
|
|
323
|
+
export type ConnectorStoreDeps = {
|
|
324
|
+
now?: () => number;
|
|
325
|
+
/**
|
|
326
|
+
* Required by `armWatch`'s `'ingress'`/`'manual'` reasons and by
|
|
327
|
+
* `recordVerification`; a caller that reaches either without it gets a clear
|
|
328
|
+
* throw rather than a silent no-op. The connector plugin passes one over
|
|
329
|
+
* `readWinnerStateDigest`; tests pass a scripted fake so the CAS interleaves
|
|
330
|
+
* are deterministic.
|
|
331
|
+
*/
|
|
332
|
+
readWinner?: ReadWinner;
|
|
333
|
+
/**
|
|
334
|
+
* The source of every random id the store mints: an operation's node key and
|
|
335
|
+
* a sync state's incarnation. The connector plugin passes its runtime's
|
|
336
|
+
* `getRandomID`.
|
|
337
|
+
*/
|
|
338
|
+
getRandomID: () => string;
|
|
339
|
+
};
|
|
340
|
+
export type ConnectorStoreAPI = ObservationRecordMethods & {
|
|
341
|
+
recordRelistReceipt(receipt: SweepReceipt, candidates: Array<SweepCandidateInput>): Promise<void>;
|
|
342
|
+
listSweepReceipts(key: SweepKey): Promise<Array<SweepReceipt>>;
|
|
343
|
+
listSweepCandidates(key: SweepKey & {
|
|
344
|
+
dueAt?: number;
|
|
345
|
+
}): Promise<Array<SweepCandidate>>;
|
|
346
|
+
recordSweepOutcome(input: SweepOutcomeInput): Promise<void>;
|
|
347
|
+
/** Returns true only when this failure transitions the candidate to refused. */
|
|
348
|
+
recordSweepApplyFailure(input: SweepApplyFailureInput): Promise<boolean>;
|
|
349
|
+
markSweepCandidateApplied(key: SweepCandidateKey): Promise<void>;
|
|
350
|
+
commitSweepCheckpoint(key: SweepReceiptKey): Promise<boolean>;
|
|
351
|
+
getSweepCheckpoint(key: SweepKey & {
|
|
352
|
+
partition: string;
|
|
353
|
+
}): Promise<string | null>;
|
|
354
|
+
getSyncState(connectorName: string, ownerDID: string): Promise<SyncState | null>;
|
|
355
|
+
setSyncState(connectorName: string, ownerDID: string, state: SyncState): Promise<void>;
|
|
356
|
+
updateSyncProgress(params: UpdateSyncProgressParams): Promise<void>;
|
|
357
|
+
deleteSyncState(connectorName: string, ownerDID: string): Promise<void>;
|
|
358
|
+
listSyncStatesForConnector(connectorName: string): Promise<Array<SyncStateEntry>>;
|
|
359
|
+
getEntityTrim(documentID: string): Promise<EntityTrimRecord | null>;
|
|
360
|
+
setEntityTrim(input: EntityTrimInput): Promise<void>;
|
|
361
|
+
clearEntityTrim(documentID: string): Promise<void>;
|
|
362
|
+
hasDocumentConnectorWork(documentID: string): Promise<boolean>;
|
|
363
|
+
sweepDocumentRows(documentID: string): Promise<void>;
|
|
364
|
+
createPendingAuth(record: PendingAuthRecord): Promise<void>;
|
|
365
|
+
getPendingAuth(state: string): Promise<PendingAuthRecord | null>;
|
|
366
|
+
consumePendingAuth(state: string): Promise<PendingAuthRecord | null>;
|
|
367
|
+
deleteExpiredPendingAuth(cutoffMs: number): Promise<void>;
|
|
368
|
+
claimOperation(input: ClaimOperationInput): Promise<{
|
|
369
|
+
status: 'claimed';
|
|
370
|
+
record: ConnectorOperation;
|
|
371
|
+
} | {
|
|
372
|
+
status: 'exists';
|
|
373
|
+
record: ConnectorOperation;
|
|
374
|
+
} | {
|
|
375
|
+
status: 'conflict';
|
|
376
|
+
}>;
|
|
377
|
+
acquireLease(params: AcquireLeaseParams): Promise<boolean>;
|
|
378
|
+
renewLease(params: RenewLeaseParams): Promise<boolean>;
|
|
379
|
+
releaseLease(params: ReleaseLeaseParams): Promise<boolean>;
|
|
380
|
+
getOperation(callerDID: string, operationID: string): Promise<ConnectorOperation | null>;
|
|
381
|
+
listTrackedOperations(filter: TrackedOperationFilter): Promise<Array<ConnectorOperation>>;
|
|
382
|
+
hasNewerOperationOnDocument(callerDID: string, operationID: string): Promise<boolean>;
|
|
383
|
+
markSendStarted(params: {
|
|
384
|
+
callerDID: string;
|
|
385
|
+
operationID: string;
|
|
386
|
+
leaseToken: string;
|
|
387
|
+
marker: string;
|
|
388
|
+
}): Promise<boolean>;
|
|
389
|
+
deferOperation(params: {
|
|
390
|
+
callerDID: string;
|
|
391
|
+
operationID: string;
|
|
392
|
+
nextAttemptAt: number;
|
|
393
|
+
leaseToken?: string;
|
|
394
|
+
reason?: DeferReason;
|
|
395
|
+
}): Promise<boolean>;
|
|
396
|
+
recordOperation(input: RecordOperationInput): Promise<boolean>;
|
|
397
|
+
finalizeOperationAndArmWatch(input: FinalizeOperationInput): Promise<boolean>;
|
|
398
|
+
getWatch(identity: ProjectionWatchIdentity): Promise<ProjectionWatch | null>;
|
|
399
|
+
getWatchByDocument(documentID: string): Promise<ProjectionWatch | null>;
|
|
400
|
+
listWatchesByDocuments(documentIDs: Array<string>): Promise<Array<ProjectionWatch>>;
|
|
401
|
+
armWatch(identity: ProjectionWatchIdentity, reason: ArmWatchReason, opts: ArmWatchOptions): Promise<ArmWatchOutcome>;
|
|
402
|
+
deferWatch(identity: ProjectionWatchIdentity, opts: {
|
|
403
|
+
generation: number;
|
|
404
|
+
nextAttemptAt: number;
|
|
405
|
+
reason?: DeferReason;
|
|
406
|
+
claimedUntil: number;
|
|
407
|
+
}): Promise<boolean>;
|
|
408
|
+
resumeOfflineDeferred(now: number): Promise<void>;
|
|
409
|
+
resumeAuthDeferred(params: {
|
|
410
|
+
now: number;
|
|
411
|
+
ownerDID: string;
|
|
412
|
+
connectorNames: Array<string>;
|
|
413
|
+
}): Promise<void>;
|
|
414
|
+
claimWatchAttempt(identity: ProjectionWatchIdentity, opts: {
|
|
415
|
+
generation: number;
|
|
416
|
+
now: number;
|
|
417
|
+
until: number;
|
|
418
|
+
}): Promise<boolean>;
|
|
419
|
+
recordVerification(identity: ProjectionWatchIdentity, input: RecordVerificationInput): Promise<RecordVerificationOutcome>;
|
|
420
|
+
requestRepull(identity: ProjectionWatchIdentity, opts: {
|
|
421
|
+
dueAt: number;
|
|
422
|
+
}): Promise<void>;
|
|
423
|
+
listDueOperations(now: number, opts: {
|
|
424
|
+
readWinner: ReadWinner;
|
|
425
|
+
readWinners?: ReadWinners;
|
|
426
|
+
}): Promise<{
|
|
427
|
+
operations: Array<ConnectorOperation>;
|
|
428
|
+
watches: Array<ProjectionWatch>;
|
|
429
|
+
}>;
|
|
430
|
+
collectFinishedOperations(params: {
|
|
431
|
+
before: number;
|
|
432
|
+
limit: number;
|
|
433
|
+
}): Promise<number>;
|
|
434
|
+
};
|
package/lib/api-types.js
ADDED
package/lib/api.d.ts
CHANGED
|
@@ -1,62 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Adapter } from '@kubun/db-adapter';
|
|
2
2
|
import type { Kysely } from 'kysely';
|
|
3
|
+
import type { ConnectorStoreAPI, ConnectorStoreDeps } from './api-types.js';
|
|
3
4
|
import type { ConnectorTables } from './tables.js';
|
|
4
|
-
export type
|
|
5
|
-
export
|
|
6
|
-
checkpoint: unknown;
|
|
7
|
-
lastSyncedAt: string;
|
|
8
|
-
entityCount: number;
|
|
9
|
-
status: SyncStatus;
|
|
10
|
-
error?: string;
|
|
11
|
-
leaseExpiresAt?: number | null;
|
|
12
|
-
recovery?: unknown;
|
|
13
|
-
version?: number;
|
|
14
|
-
incarnation?: string;
|
|
15
|
-
};
|
|
16
|
-
export type SyncStateEntry = SyncState & {
|
|
17
|
-
connectorName: string;
|
|
18
|
-
ownerDID: string;
|
|
19
|
-
};
|
|
20
|
-
export type SyncProgressCASCurrent = {
|
|
21
|
-
checkpoint: unknown;
|
|
22
|
-
recovery: unknown;
|
|
23
|
-
version: number;
|
|
24
|
-
incarnation: string;
|
|
25
|
-
};
|
|
26
|
-
export type SyncProgressCASResult = {
|
|
27
|
-
checkpoint: unknown;
|
|
28
|
-
recovery: unknown;
|
|
29
|
-
lastSyncedAt: string;
|
|
30
|
-
entityCount: number;
|
|
31
|
-
error?: string | null;
|
|
32
|
-
};
|
|
33
|
-
export type SyncProgressRecompute = (current: SyncProgressCASCurrent | null) => SyncProgressCASResult | Promise<SyncProgressCASResult>;
|
|
34
|
-
export type PendingAuthRecord = {
|
|
35
|
-
state: string;
|
|
36
|
-
codeVerifier: string;
|
|
37
|
-
provider: string;
|
|
38
|
-
ownerDID: string;
|
|
39
|
-
/** The owner's DID in a form that can be encrypted to. See the table type. */
|
|
40
|
-
ownerWrappableDID?: string | null;
|
|
41
|
-
/** A recovery recipient's DID in a form that can be encrypted to. See the table type. */
|
|
42
|
-
controllerWrappableDID?: string | null;
|
|
43
|
-
createdAt: number;
|
|
44
|
-
/** The callback's redirect target, needed to complete the token exchange. */
|
|
45
|
-
redirectURL: string;
|
|
46
|
-
/** The scopes requested at authorize time. */
|
|
47
|
-
scopes: Array<string>;
|
|
48
|
-
expiresAt: number;
|
|
49
|
-
};
|
|
50
|
-
export type ConnectorStoreAPI = {
|
|
51
|
-
getSyncState(connectorName: string, ownerDID: string): Promise<SyncState | null>;
|
|
52
|
-
setSyncState(connectorName: string, ownerDID: string, state: SyncState): Promise<void>;
|
|
53
|
-
updateSyncProgress(connectorName: string, ownerDID: string, recompute: SyncProgressRecompute, options?: {
|
|
54
|
-
maxRetries?: number;
|
|
55
|
-
}): Promise<void>;
|
|
56
|
-
deleteSyncState(connectorName: string, ownerDID: string): Promise<void>;
|
|
57
|
-
listSyncStatesForConnector(connectorName: string): Promise<Array<SyncStateEntry>>;
|
|
58
|
-
createPendingAuth(record: PendingAuthRecord): Promise<void>;
|
|
59
|
-
consumePendingAuth(state: string): Promise<PendingAuthRecord | null>;
|
|
60
|
-
deleteExpiredPendingAuth(cutoffMs: number): Promise<void>;
|
|
61
|
-
};
|
|
62
|
-
export declare function createConnectorStore(db: Kysely<ConnectorTables>, _adapter: Adapter): ConnectorStoreAPI;
|
|
5
|
+
export type * from './api-types.js';
|
|
6
|
+
export declare function createConnectorStore(db: Kysely<ConnectorTables>, adapter: Adapter, deps: ConnectorStoreDeps): ConnectorStoreAPI;
|