@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,180 @@
|
|
|
1
|
+
import { decodeJSONColumn, encodeJSONColumn } from './serialization.js';
|
|
2
|
+
export const MAX_SWEEP_APPLY_ATTEMPTS = 5;
|
|
3
|
+
export function createSweepStateMethods({ db, deps }) {
|
|
4
|
+
const now = deps.now ?? Date.now;
|
|
5
|
+
return {
|
|
6
|
+
async recordRelistReceipt (receipt, candidates) {
|
|
7
|
+
if (!receipt.complete) return;
|
|
8
|
+
await db.transaction().execute(async (tx)=>{
|
|
9
|
+
const existing = await tx.selectFrom('kubun_connector_sweep_receipt').select('committed_at').where('owner_did', '=', receipt.ownerDID).where('connector_name', '=', receipt.connectorName).where('partition', '=', receipt.partition).where('provenance', '=', receipt.provenance).executeTakeFirst();
|
|
10
|
+
// A replay of a committed receipt has nothing left to verify.
|
|
11
|
+
if (existing?.committed_at != null) return;
|
|
12
|
+
if (existing == null) {
|
|
13
|
+
// Recording order per partition, so an older receipt that commits
|
|
14
|
+
// late never moves the sweep checkpoint back.
|
|
15
|
+
const latest = await tx.selectFrom('kubun_connector_sweep_receipt').select((eb)=>eb.fn.max('sequence').as('sequence')).where('owner_did', '=', receipt.ownerDID).where('connector_name', '=', receipt.connectorName).where('partition', '=', receipt.partition).executeTakeFirst();
|
|
16
|
+
await tx.insertInto('kubun_connector_sweep_receipt').values({
|
|
17
|
+
owner_did: receipt.ownerDID,
|
|
18
|
+
connector_name: receipt.connectorName,
|
|
19
|
+
partition: receipt.partition,
|
|
20
|
+
provenance: receipt.provenance,
|
|
21
|
+
sequence: Number(latest?.sequence ?? 0) + 1,
|
|
22
|
+
model_id: receipt.modelID,
|
|
23
|
+
scope: encodeJSONColumn(receipt.scope),
|
|
24
|
+
keep: encodeJSONColumn(receipt.keep),
|
|
25
|
+
source_fetch_hlc: receipt.sourceFetchHLC,
|
|
26
|
+
predicate: encodeJSONColumn(receipt.predicate),
|
|
27
|
+
kind: receipt.kind,
|
|
28
|
+
complete: 1,
|
|
29
|
+
created_at: now(),
|
|
30
|
+
committed_at: null
|
|
31
|
+
}).execute();
|
|
32
|
+
}
|
|
33
|
+
for (const candidate of candidates){
|
|
34
|
+
await tx.insertInto('kubun_connector_sweep_candidate').values({
|
|
35
|
+
owner_did: receipt.ownerDID,
|
|
36
|
+
connector_name: receipt.connectorName,
|
|
37
|
+
partition: receipt.partition,
|
|
38
|
+
provenance: receipt.provenance,
|
|
39
|
+
document_id: candidate.documentID,
|
|
40
|
+
source_id: candidate.sourceID,
|
|
41
|
+
model_id: candidate.modelID,
|
|
42
|
+
state: 'pending',
|
|
43
|
+
attempts: 0,
|
|
44
|
+
apply_failures: 0,
|
|
45
|
+
last_error: null,
|
|
46
|
+
updated_at: now(),
|
|
47
|
+
next_attempt_at: 0
|
|
48
|
+
}).onConflict((oc)=>oc.doNothing()).execute();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
async listSweepReceipts (key) {
|
|
53
|
+
const rows = await db.selectFrom('kubun_connector_sweep_receipt').selectAll().where('owner_did', '=', key.ownerDID).where('connector_name', '=', key.connectorName).where('committed_at', 'is', null).orderBy('created_at').execute();
|
|
54
|
+
return rows.map((row)=>({
|
|
55
|
+
ownerDID: row.owner_did,
|
|
56
|
+
connectorName: row.connector_name,
|
|
57
|
+
partition: row.partition,
|
|
58
|
+
provenance: row.provenance,
|
|
59
|
+
modelID: row.model_id,
|
|
60
|
+
scope: decodeJSONColumn(row.scope),
|
|
61
|
+
keep: decodeJSONColumn(row.keep),
|
|
62
|
+
sourceFetchHLC: row.source_fetch_hlc,
|
|
63
|
+
predicate: decodeJSONColumn(row.predicate),
|
|
64
|
+
complete: row.complete === 1,
|
|
65
|
+
kind: row.kind
|
|
66
|
+
}));
|
|
67
|
+
},
|
|
68
|
+
async listSweepCandidates (key) {
|
|
69
|
+
let query = db.selectFrom('kubun_connector_sweep_candidate').selectAll().where('owner_did', '=', key.ownerDID).where('connector_name', '=', key.connectorName).orderBy('updated_at').orderBy('document_id');
|
|
70
|
+
if (key.dueAt != null) {
|
|
71
|
+
query = query.where('state', 'not in', [
|
|
72
|
+
'applied',
|
|
73
|
+
'refused'
|
|
74
|
+
]).where('next_attempt_at', '<=', key.dueAt);
|
|
75
|
+
}
|
|
76
|
+
const rows = await query.execute();
|
|
77
|
+
return rows.map((row)=>({
|
|
78
|
+
ownerDID: row.owner_did,
|
|
79
|
+
connectorName: row.connector_name,
|
|
80
|
+
partition: row.partition,
|
|
81
|
+
provenance: row.provenance,
|
|
82
|
+
documentID: row.document_id,
|
|
83
|
+
sourceID: row.source_id,
|
|
84
|
+
modelID: row.model_id,
|
|
85
|
+
state: row.state,
|
|
86
|
+
attempts: row.attempts,
|
|
87
|
+
applyFailures: row.apply_failures,
|
|
88
|
+
lastError: row.last_error,
|
|
89
|
+
updatedAt: Number(row.updated_at),
|
|
90
|
+
nextAttemptAt: Number(row.next_attempt_at)
|
|
91
|
+
}));
|
|
92
|
+
},
|
|
93
|
+
async recordSweepOutcome (input) {
|
|
94
|
+
await db.updateTable('kubun_connector_sweep_candidate').set((eb)=>({
|
|
95
|
+
state: input.outcome,
|
|
96
|
+
attempts: eb('attempts', '+', 1),
|
|
97
|
+
last_error: input.lastError ?? null,
|
|
98
|
+
updated_at: now(),
|
|
99
|
+
next_attempt_at: input.nextAttemptAt ?? 0
|
|
100
|
+
})).where('owner_did', '=', input.ownerDID).where('connector_name', '=', input.connectorName).where('partition', '=', input.partition).where('provenance', '=', input.provenance).where('document_id', '=', input.documentID).where('state', 'not in', [
|
|
101
|
+
'applied',
|
|
102
|
+
'refused'
|
|
103
|
+
]).execute();
|
|
104
|
+
},
|
|
105
|
+
async recordSweepApplyFailure (input) {
|
|
106
|
+
const row = await db.updateTable('kubun_connector_sweep_candidate').set((eb)=>({
|
|
107
|
+
state: eb.case().when('apply_failures', '>=', MAX_SWEEP_APPLY_ATTEMPTS - 1).then('refused').else('pending').end(),
|
|
108
|
+
apply_failures: eb('apply_failures', '+', 1),
|
|
109
|
+
last_error: input.lastError,
|
|
110
|
+
next_attempt_at: input.nextAttemptAt,
|
|
111
|
+
updated_at: now()
|
|
112
|
+
})).where('owner_did', '=', input.ownerDID).where('connector_name', '=', input.connectorName).where('partition', '=', input.partition).where('provenance', '=', input.provenance).where('document_id', '=', input.documentID).where('state', 'not in', [
|
|
113
|
+
'applied',
|
|
114
|
+
'refused'
|
|
115
|
+
]).returning('state').executeTakeFirst();
|
|
116
|
+
return row?.state === 'refused';
|
|
117
|
+
},
|
|
118
|
+
async markSweepCandidateApplied (key) {
|
|
119
|
+
await db.updateTable('kubun_connector_sweep_candidate').set({
|
|
120
|
+
state: 'applied',
|
|
121
|
+
last_error: null,
|
|
122
|
+
next_attempt_at: 0,
|
|
123
|
+
updated_at: now()
|
|
124
|
+
}).where('owner_did', '=', key.ownerDID).where('connector_name', '=', key.connectorName).where('partition', '=', key.partition).where('provenance', '=', key.provenance).where('document_id', '=', key.documentID).where('state', 'in', [
|
|
125
|
+
'present',
|
|
126
|
+
'absent',
|
|
127
|
+
'cancelled'
|
|
128
|
+
]).execute();
|
|
129
|
+
},
|
|
130
|
+
async commitSweepCheckpoint (key) {
|
|
131
|
+
return db.transaction().execute(async (tx)=>{
|
|
132
|
+
const receipt = await tx.selectFrom('kubun_connector_sweep_receipt').select([
|
|
133
|
+
'complete',
|
|
134
|
+
'committed_at',
|
|
135
|
+
'sequence'
|
|
136
|
+
]).where('owner_did', '=', key.ownerDID).where('connector_name', '=', key.connectorName).where('partition', '=', key.partition).where('provenance', '=', key.provenance).executeTakeFirst();
|
|
137
|
+
if (receipt?.complete !== 1 || receipt.committed_at != null) return false;
|
|
138
|
+
const unresolved = await tx.selectFrom('kubun_connector_sweep_candidate').select('document_id').where('owner_did', '=', key.ownerDID).where('connector_name', '=', key.connectorName).where('partition', '=', key.partition).where('provenance', '=', key.provenance).where('state', 'not in', [
|
|
139
|
+
'applied',
|
|
140
|
+
'refused'
|
|
141
|
+
]).executeTakeFirst();
|
|
142
|
+
if (unresolved != null) return false;
|
|
143
|
+
const sequence = Number(receipt.sequence);
|
|
144
|
+
// A later relist must not advance past an earlier unresolved candidate.
|
|
145
|
+
const earlier = await tx.selectFrom('kubun_connector_sweep_candidate as candidate').innerJoin('kubun_connector_sweep_receipt as receipt', (join)=>join.onRef('receipt.owner_did', '=', 'candidate.owner_did').onRef('receipt.connector_name', '=', 'candidate.connector_name').onRef('receipt.partition', '=', 'candidate.partition').onRef('receipt.provenance', '=', 'candidate.provenance')).select('candidate.document_id').where('candidate.owner_did', '=', key.ownerDID).where('candidate.connector_name', '=', key.connectorName).where('candidate.partition', '=', key.partition).where('candidate.state', 'not in', [
|
|
146
|
+
'applied',
|
|
147
|
+
'refused'
|
|
148
|
+
]).where('receipt.sequence', '<', sequence).executeTakeFirst();
|
|
149
|
+
if (earlier != null) return false;
|
|
150
|
+
await tx.insertInto('kubun_connector_sweep_checkpoint').values({
|
|
151
|
+
owner_did: key.ownerDID,
|
|
152
|
+
connector_name: key.connectorName,
|
|
153
|
+
partition: key.partition,
|
|
154
|
+
provenance: key.provenance,
|
|
155
|
+
receipt_sequence: sequence,
|
|
156
|
+
updated_at: now()
|
|
157
|
+
}).onConflict((oc)=>oc.columns([
|
|
158
|
+
'owner_did',
|
|
159
|
+
'connector_name',
|
|
160
|
+
'partition'
|
|
161
|
+
]).doUpdateSet({
|
|
162
|
+
provenance: key.provenance,
|
|
163
|
+
receipt_sequence: sequence,
|
|
164
|
+
updated_at: now()
|
|
165
|
+
}).where('kubun_connector_sweep_checkpoint.receipt_sequence', '<', sequence)).execute();
|
|
166
|
+
// Applied rows can be discarded; refused rows retain their error for inspection.
|
|
167
|
+
// The committed receipt prevents replay from re-verifying either outcome.
|
|
168
|
+
await tx.deleteFrom('kubun_connector_sweep_candidate').where('owner_did', '=', key.ownerDID).where('connector_name', '=', key.connectorName).where('partition', '=', key.partition).where('provenance', '=', key.provenance).where('state', '=', 'applied').execute();
|
|
169
|
+
await tx.updateTable('kubun_connector_sweep_receipt').set({
|
|
170
|
+
committed_at: now()
|
|
171
|
+
}).where('owner_did', '=', key.ownerDID).where('connector_name', '=', key.connectorName).where('partition', '=', key.partition).where('provenance', '=', key.provenance).execute();
|
|
172
|
+
return true;
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
async getSweepCheckpoint (key) {
|
|
176
|
+
const row = await db.selectFrom('kubun_connector_sweep_checkpoint').select('provenance').where('owner_did', '=', key.ownerDID).where('connector_name', '=', key.connectorName).where('partition', '=', key.partition).executeTakeFirst();
|
|
177
|
+
return row?.provenance ?? null;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ConnectorStoreAPI } from './api-types.js';
|
|
2
|
+
import type { ConnectorStoreContext } from './store-context.js';
|
|
3
|
+
export type SyncStateMethods = Pick<ConnectorStoreAPI, 'getSyncState' | 'setSyncState' | 'updateSyncProgress' | 'deleteSyncState' | 'listSyncStatesForConnector'>;
|
|
4
|
+
export declare function createSyncStateMethods(context: ConnectorStoreContext): SyncStateMethods;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { decodeJSONColumn, encodeJSONColumn, rowToSyncState } from './serialization.js';
|
|
2
|
+
export function createSyncStateMethods(context) {
|
|
3
|
+
const { db, getRandomID } = context;
|
|
4
|
+
const methods = {
|
|
5
|
+
// --- Sync state operations ---
|
|
6
|
+
async getSyncState (connectorName, ownerDID) {
|
|
7
|
+
const row = await db.selectFrom('kubun_connector_sync_state').selectAll().where('connector_name', '=', connectorName).where('owner_did', '=', ownerDID).executeTakeFirst();
|
|
8
|
+
if (row == null) return null;
|
|
9
|
+
return rowToSyncState(row);
|
|
10
|
+
},
|
|
11
|
+
async setSyncState (connectorName, ownerDID, state) {
|
|
12
|
+
// `lease_expires_at` is dead: a fresh INSERT sets it null and a conflicting
|
|
13
|
+
// update leaves it untouched. A fresh INSERT seeds version 0 and a random
|
|
14
|
+
// incarnation; a conflicting update leaves both untouched (this path is not
|
|
15
|
+
// the CAS write — it does not bump version or churn the incarnation).
|
|
16
|
+
const checkpoint = encodeJSONColumn(state.checkpoint);
|
|
17
|
+
const recovery = encodeJSONColumn(state.recovery);
|
|
18
|
+
await db.insertInto('kubun_connector_sync_state').values({
|
|
19
|
+
connector_name: connectorName,
|
|
20
|
+
owner_did: ownerDID,
|
|
21
|
+
checkpoint,
|
|
22
|
+
last_synced_at: state.lastSyncedAt,
|
|
23
|
+
entity_count: state.entityCount,
|
|
24
|
+
status: state.status,
|
|
25
|
+
error: state.error ?? null,
|
|
26
|
+
lease_expires_at: null,
|
|
27
|
+
recovery,
|
|
28
|
+
version: 0,
|
|
29
|
+
incarnation: getRandomID()
|
|
30
|
+
}).onConflict((oc)=>oc.columns([
|
|
31
|
+
'connector_name',
|
|
32
|
+
'owner_did'
|
|
33
|
+
]).doUpdateSet({
|
|
34
|
+
checkpoint,
|
|
35
|
+
last_synced_at: state.lastSyncedAt,
|
|
36
|
+
entity_count: state.entityCount,
|
|
37
|
+
status: state.status,
|
|
38
|
+
error: state.error ?? null,
|
|
39
|
+
recovery
|
|
40
|
+
})).execute();
|
|
41
|
+
},
|
|
42
|
+
async updateSyncProgress ({ connectorName, ownerDID, recompute, options }) {
|
|
43
|
+
// The caller passes the authoritative MAX_CAS_RETRIES; 5 is a defensive
|
|
44
|
+
// fallback for direct callers/tests.
|
|
45
|
+
const maxRetries = options?.maxRetries ?? 5;
|
|
46
|
+
for(let attempt = 0; attempt <= maxRetries; attempt++){
|
|
47
|
+
const row = await db.selectFrom('kubun_connector_sync_state').selectAll().where('connector_name', '=', connectorName).where('owner_did', '=', ownerDID).executeTakeFirst();
|
|
48
|
+
if (row == null) {
|
|
49
|
+
// No row yet: INSERT ... ON CONFLICT DO NOTHING so a lost insert race
|
|
50
|
+
// (a concurrent writer created the row first) falls through to reload
|
|
51
|
+
// and take the update path.
|
|
52
|
+
const result = await recompute(null);
|
|
53
|
+
const inserted = await db.insertInto('kubun_connector_sync_state').values({
|
|
54
|
+
connector_name: connectorName,
|
|
55
|
+
owner_did: ownerDID,
|
|
56
|
+
checkpoint: encodeJSONColumn(result.checkpoint),
|
|
57
|
+
last_synced_at: result.lastSyncedAt,
|
|
58
|
+
entity_count: result.entityCount,
|
|
59
|
+
status: 'idle',
|
|
60
|
+
error: result.error ?? null,
|
|
61
|
+
lease_expires_at: null,
|
|
62
|
+
recovery: encodeJSONColumn(result.recovery),
|
|
63
|
+
version: 0,
|
|
64
|
+
incarnation: getRandomID()
|
|
65
|
+
}).onConflict((oc)=>oc.columns([
|
|
66
|
+
'connector_name',
|
|
67
|
+
'owner_did'
|
|
68
|
+
]).doNothing()).executeTakeFirst();
|
|
69
|
+
if ((inserted?.numInsertedOrUpdatedRows ?? 0n) > 0n) return;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const result = await recompute({
|
|
73
|
+
checkpoint: decodeJSONColumn(row.checkpoint),
|
|
74
|
+
recovery: decodeJSONColumn(row.recovery),
|
|
75
|
+
version: row.version,
|
|
76
|
+
incarnation: row.incarnation ?? ''
|
|
77
|
+
});
|
|
78
|
+
// Single conditional UPDATE guarded on the read version AND incarnation.
|
|
79
|
+
// `status` is deliberately omitted (workflow owns it). Zero rows means a
|
|
80
|
+
// concurrent writer won, or the row was delete+recreated (incarnation
|
|
81
|
+
// differs) ⇒ reload and recompute.
|
|
82
|
+
const updated = await db.updateTable('kubun_connector_sync_state').set({
|
|
83
|
+
checkpoint: encodeJSONColumn(result.checkpoint),
|
|
84
|
+
last_synced_at: result.lastSyncedAt,
|
|
85
|
+
entity_count: result.entityCount,
|
|
86
|
+
error: result.error ?? null,
|
|
87
|
+
recovery: encodeJSONColumn(result.recovery),
|
|
88
|
+
version: row.version + 1
|
|
89
|
+
}).where('connector_name', '=', connectorName).where('owner_did', '=', ownerDID).where('version', '=', row.version).where('incarnation', '=', row.incarnation).executeTakeFirst();
|
|
90
|
+
if ((updated?.numUpdatedRows ?? 0n) > 0n) return;
|
|
91
|
+
}
|
|
92
|
+
throw new Error(`updateSyncProgress: CAS failed after ${maxRetries + 1} attempts for ${connectorName}/${ownerDID}`);
|
|
93
|
+
},
|
|
94
|
+
async deleteSyncState (connectorName, ownerDID) {
|
|
95
|
+
await db.deleteFrom('kubun_connector_sync_state').where('connector_name', '=', connectorName).where('owner_did', '=', ownerDID).execute();
|
|
96
|
+
},
|
|
97
|
+
async listSyncStatesForConnector (connectorName) {
|
|
98
|
+
const rows = await db.selectFrom('kubun_connector_sync_state').selectAll().where('connector_name', '=', connectorName).execute();
|
|
99
|
+
return rows.map((row)=>({
|
|
100
|
+
...rowToSyncState(row),
|
|
101
|
+
connectorName: row.connector_name,
|
|
102
|
+
ownerDID: row.owner_did
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
return methods;
|
|
107
|
+
}
|
package/lib/tables.d.ts
CHANGED
|
@@ -13,6 +13,45 @@ export type ConnectorSyncStateTable = {
|
|
|
13
13
|
incarnation: string | null;
|
|
14
14
|
};
|
|
15
15
|
export type ConnectorSyncState = Selectable<ConnectorSyncStateTable>;
|
|
16
|
+
export type ConnectorSweepReceiptTable = {
|
|
17
|
+
owner_did: string;
|
|
18
|
+
connector_name: string;
|
|
19
|
+
partition: string;
|
|
20
|
+
provenance: string;
|
|
21
|
+
sequence: number;
|
|
22
|
+
model_id: string;
|
|
23
|
+
scope: string;
|
|
24
|
+
keep: string;
|
|
25
|
+
source_fetch_hlc: string;
|
|
26
|
+
predicate: string | null;
|
|
27
|
+
kind: string;
|
|
28
|
+
complete: number;
|
|
29
|
+
created_at: number;
|
|
30
|
+
committed_at: number | null;
|
|
31
|
+
};
|
|
32
|
+
export type ConnectorSweepCandidateTable = {
|
|
33
|
+
owner_did: string;
|
|
34
|
+
connector_name: string;
|
|
35
|
+
partition: string;
|
|
36
|
+
provenance: string;
|
|
37
|
+
document_id: string;
|
|
38
|
+
source_id: string;
|
|
39
|
+
model_id: string;
|
|
40
|
+
state: string;
|
|
41
|
+
attempts: number;
|
|
42
|
+
apply_failures: number;
|
|
43
|
+
last_error: string | null;
|
|
44
|
+
updated_at: number;
|
|
45
|
+
next_attempt_at: number;
|
|
46
|
+
};
|
|
47
|
+
export type ConnectorSweepCheckpointTable = {
|
|
48
|
+
owner_did: string;
|
|
49
|
+
connector_name: string;
|
|
50
|
+
partition: string;
|
|
51
|
+
provenance: string;
|
|
52
|
+
receipt_sequence: number;
|
|
53
|
+
updated_at: number;
|
|
54
|
+
};
|
|
16
55
|
export type ConnectorPendingAuthTable = {
|
|
17
56
|
state: string;
|
|
18
57
|
code_verifier: string;
|
|
@@ -34,10 +73,98 @@ export type ConnectorPendingAuthTable = {
|
|
|
34
73
|
redirect_url: string;
|
|
35
74
|
/** JSON-encoded `Array<string>`. See `PendingAuthRecord.scopes`. */
|
|
36
75
|
scopes: string;
|
|
76
|
+
/** Null means the request predates incremental-mode tracking. */
|
|
77
|
+
is_incremental: number;
|
|
37
78
|
expires_at: number;
|
|
38
79
|
};
|
|
39
80
|
export type ConnectorPendingAuth = Selectable<ConnectorPendingAuthTable>;
|
|
81
|
+
export type ConnectorOperationTable = {
|
|
82
|
+
caller_did: string;
|
|
83
|
+
operation_id: string;
|
|
84
|
+
node_key: string;
|
|
85
|
+
owner_did: string;
|
|
86
|
+
connector_name: string;
|
|
87
|
+
model: string;
|
|
88
|
+
op: string;
|
|
89
|
+
account_key: string;
|
|
90
|
+
target: string;
|
|
91
|
+
source_id: string | null;
|
|
92
|
+
deterministic_event_id: string;
|
|
93
|
+
payload_hash: string;
|
|
94
|
+
reissue_intent: string;
|
|
95
|
+
provider_etag: string | null;
|
|
96
|
+
document_id: string | null;
|
|
97
|
+
result_entity: string | null;
|
|
98
|
+
state: string;
|
|
99
|
+
attempts: number;
|
|
100
|
+
next_attempt_at: number | null;
|
|
101
|
+
last_kind: string | null;
|
|
102
|
+
last_http_status: number | null;
|
|
103
|
+
defer_reason: string | null;
|
|
104
|
+
send_marker: string | null;
|
|
105
|
+
version: number;
|
|
106
|
+
incarnation: string | null;
|
|
107
|
+
lease_token: string | null;
|
|
108
|
+
lease_expires_at: number | null;
|
|
109
|
+
created_at: number;
|
|
110
|
+
updated_at: number;
|
|
111
|
+
};
|
|
112
|
+
export type ConnectorOperation = Selectable<ConnectorOperationTable>;
|
|
113
|
+
export type ConnectorProjectionWatchTable = {
|
|
114
|
+
owner_did: string;
|
|
115
|
+
connector_name: string;
|
|
116
|
+
model: string;
|
|
117
|
+
account_key: string;
|
|
118
|
+
source_id: string;
|
|
119
|
+
document_id: string;
|
|
120
|
+
verified_state_digest: string | null;
|
|
121
|
+
observed_winner_digest: string | null;
|
|
122
|
+
verification_status: string;
|
|
123
|
+
verification_attempts: number;
|
|
124
|
+
repair_generation: number;
|
|
125
|
+
next_attempt_at: number | null;
|
|
126
|
+
claimed_until: number | null;
|
|
127
|
+
defer_reason: string | null;
|
|
128
|
+
version: number;
|
|
129
|
+
created_at: number;
|
|
130
|
+
updated_at: number;
|
|
131
|
+
};
|
|
132
|
+
export type ConnectorProjectionWatch = Selectable<ConnectorProjectionWatchTable>;
|
|
133
|
+
export type ConnectorEntityTrimTable = {
|
|
134
|
+
document_id: string;
|
|
135
|
+
owner_did: string;
|
|
136
|
+
connector_name: string;
|
|
137
|
+
trimmed_fields: string;
|
|
138
|
+
updated_at: number;
|
|
139
|
+
};
|
|
140
|
+
export type ConnectorEntityTrim = Selectable<ConnectorEntityTrimTable>;
|
|
141
|
+
export type ConnectorObservationRecordTable = {
|
|
142
|
+
type: string;
|
|
143
|
+
owner: string;
|
|
144
|
+
id: string;
|
|
145
|
+
op_hash: string;
|
|
146
|
+
hlc: string;
|
|
147
|
+
op_jwt: string;
|
|
148
|
+
payload: Uint8Array | null;
|
|
149
|
+
};
|
|
150
|
+
export type ConnectorObservationRepairTable = {
|
|
151
|
+
document_id: string;
|
|
152
|
+
owner: string;
|
|
153
|
+
fetch_hlc: string;
|
|
154
|
+
decision_hlc: string;
|
|
155
|
+
present: number;
|
|
156
|
+
value_digest: string | null;
|
|
157
|
+
written_at: number;
|
|
158
|
+
};
|
|
40
159
|
export type ConnectorTables = {
|
|
160
|
+
kubun_connector_sweep_receipt: ConnectorSweepReceiptTable;
|
|
161
|
+
kubun_connector_sweep_candidate: ConnectorSweepCandidateTable;
|
|
162
|
+
kubun_connector_sweep_checkpoint: ConnectorSweepCheckpointTable;
|
|
163
|
+
kubun_connector_observation_records: ConnectorObservationRecordTable;
|
|
164
|
+
kubun_connector_observation_repair: ConnectorObservationRepairTable;
|
|
41
165
|
kubun_connector_sync_state: ConnectorSyncStateTable;
|
|
42
166
|
kubun_connector_pending_auth: ConnectorPendingAuthTable;
|
|
167
|
+
kubun_connector_operation: ConnectorOperationTable;
|
|
168
|
+
kubun_connector_projection_watch: ConnectorProjectionWatchTable;
|
|
169
|
+
kubun_connector_entity_trim: ConnectorEntityTrimTable;
|
|
43
170
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/store-connector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "see LICENSE.md",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -15,14 +15,20 @@
|
|
|
15
15
|
"LICENSE.md"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@
|
|
19
|
-
"@kubun/db
|
|
20
|
-
"
|
|
18
|
+
"@kokuin/token": "^0.5.1",
|
|
19
|
+
"@kubun/db": "^0.18.0",
|
|
20
|
+
"@kubun/db-adapter": "^0.18.0",
|
|
21
|
+
"@kubun/records": "^0.18.0",
|
|
22
|
+
"kysely": "^0.29.7"
|
|
21
23
|
},
|
|
22
24
|
"devDependencies": {
|
|
23
|
-
"@kubun/db-better-sqlite": "^0.
|
|
24
|
-
"@kubun/db-postgres": "^0.
|
|
25
|
-
"@
|
|
25
|
+
"@kubun/db-better-sqlite": "^0.18.0",
|
|
26
|
+
"@kubun/db-postgres": "^0.18.0",
|
|
27
|
+
"@kubun/id": "^0.18.0",
|
|
28
|
+
"@kubun/mutation": "^0.18.0",
|
|
29
|
+
"@kubun/protocol": "^0.18.0",
|
|
30
|
+
"@kubun/store-graph": "^0.18.0",
|
|
31
|
+
"@testcontainers/postgresql": "^12.2.1"
|
|
26
32
|
},
|
|
27
33
|
"publishConfig": {
|
|
28
34
|
"access": "public"
|