@kubun/store-connector 0.15.0 → 0.17.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.d.ts +11 -0
- package/lib/api.js +42 -6
- package/lib/migrations.js +8 -1
- package/lib/tables.d.ts +4 -0
- package/package.json +7 -7
package/lib/api.d.ts
CHANGED
|
@@ -24,10 +24,21 @@ export type PendingAuthRecord = {
|
|
|
24
24
|
/** A recovery recipient's DID in a form that can be encrypted to. See the table type. */
|
|
25
25
|
controllerWrappableDID?: string | null;
|
|
26
26
|
createdAt: number;
|
|
27
|
+
/** The callback's redirect target, needed to complete the token exchange. */
|
|
28
|
+
redirectURL: string;
|
|
29
|
+
/** The scopes requested at authorize time. */
|
|
30
|
+
scopes: Array<string>;
|
|
31
|
+
expiresAt: number;
|
|
27
32
|
};
|
|
28
33
|
export type ConnectorStoreAPI = {
|
|
29
34
|
getSyncState(connectorName: string, ownerDID: string): Promise<SyncState | null>;
|
|
30
35
|
setSyncState(connectorName: string, ownerDID: string, state: SyncState): Promise<void>;
|
|
36
|
+
updateSyncProgress(connectorName: string, ownerDID: string, progress: {
|
|
37
|
+
checkpoint: unknown;
|
|
38
|
+
lastSyncedAt: string;
|
|
39
|
+
entityCount: number;
|
|
40
|
+
error?: string | null;
|
|
41
|
+
}): Promise<void>;
|
|
31
42
|
deleteSyncState(connectorName: string, ownerDID: string): Promise<void>;
|
|
32
43
|
listSyncStatesForConnector(connectorName: string): Promise<Array<SyncStateEntry>>;
|
|
33
44
|
createPendingAuth(record: PendingAuthRecord): Promise<void>;
|
package/lib/api.js
CHANGED
|
@@ -27,9 +27,8 @@ export function createConnectorStore(db, _adapter) {
|
|
|
27
27
|
return rowToSyncState(row);
|
|
28
28
|
},
|
|
29
29
|
async setSyncState (connectorName, ownerDID, state) {
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
// untouched.
|
|
30
|
+
// `lease_expires_at` is dead: a fresh INSERT sets it null and a conflicting
|
|
31
|
+
// update leaves it untouched.
|
|
33
32
|
await db.insertInto('kubun_connector_sync_state').values({
|
|
34
33
|
connector_name: connectorName,
|
|
35
34
|
owner_did: ownerDID,
|
|
@@ -50,6 +49,28 @@ export function createConnectorStore(db, _adapter) {
|
|
|
50
49
|
error: state.error ?? null
|
|
51
50
|
})).execute();
|
|
52
51
|
},
|
|
52
|
+
async updateSyncProgress (connectorName, ownerDID, progress) {
|
|
53
|
+
const checkpoint = progress.checkpoint != null ? JSON.stringify(progress.checkpoint) : null;
|
|
54
|
+
const error = progress.error ?? null;
|
|
55
|
+
await db.insertInto('kubun_connector_sync_state').values({
|
|
56
|
+
connector_name: connectorName,
|
|
57
|
+
owner_did: ownerDID,
|
|
58
|
+
checkpoint,
|
|
59
|
+
last_synced_at: progress.lastSyncedAt,
|
|
60
|
+
entity_count: progress.entityCount,
|
|
61
|
+
status: 'idle',
|
|
62
|
+
error,
|
|
63
|
+
lease_expires_at: null
|
|
64
|
+
}).onConflict((oc)=>oc.columns([
|
|
65
|
+
'connector_name',
|
|
66
|
+
'owner_did'
|
|
67
|
+
]).doUpdateSet({
|
|
68
|
+
checkpoint,
|
|
69
|
+
last_synced_at: progress.lastSyncedAt,
|
|
70
|
+
entity_count: progress.entityCount,
|
|
71
|
+
error
|
|
72
|
+
})).execute();
|
|
73
|
+
},
|
|
53
74
|
async deleteSyncState (connectorName, ownerDID) {
|
|
54
75
|
await db.deleteFrom('kubun_connector_sync_state').where('connector_name', '=', connectorName).where('owner_did', '=', ownerDID).execute();
|
|
55
76
|
},
|
|
@@ -70,7 +91,10 @@ export function createConnectorStore(db, _adapter) {
|
|
|
70
91
|
owner_did: record.ownerDID,
|
|
71
92
|
owner_wrappable_did: record.ownerWrappableDID ?? null,
|
|
72
93
|
controller_wrappable_did: record.controllerWrappableDID ?? null,
|
|
73
|
-
created_at: record.createdAt
|
|
94
|
+
created_at: record.createdAt,
|
|
95
|
+
redirect_url: record.redirectURL,
|
|
96
|
+
scopes: JSON.stringify(record.scopes),
|
|
97
|
+
expires_at: record.expiresAt
|
|
74
98
|
}).execute();
|
|
75
99
|
},
|
|
76
100
|
async consumePendingAuth (state) {
|
|
@@ -85,11 +109,23 @@ export function createConnectorStore(db, _adapter) {
|
|
|
85
109
|
ownerDID: row.owner_did,
|
|
86
110
|
ownerWrappableDID: row.owner_wrappable_did,
|
|
87
111
|
controllerWrappableDID: row.controller_wrappable_did,
|
|
88
|
-
createdAt: row.created_at
|
|
112
|
+
createdAt: row.created_at,
|
|
113
|
+
redirectURL: row.redirect_url,
|
|
114
|
+
// ParseJSONResultsPlugin may have already parsed this column, same as
|
|
115
|
+
// `checkpoint` in `rowToSyncState`.
|
|
116
|
+
scopes: typeof row.scopes === 'string' ? JSON.parse(row.scopes) : row.scopes,
|
|
117
|
+
expiresAt: row.expires_at
|
|
89
118
|
};
|
|
90
119
|
},
|
|
91
120
|
async deleteExpiredPendingAuth (cutoffMs) {
|
|
92
|
-
|
|
121
|
+
// Strict `<` matches @kokuin/oauth `completeAuthorization`, which treats a
|
|
122
|
+
// record as expired only when `expiresAt < now`. A record at exactly
|
|
123
|
+
// `expiresAt === cutoffMs` is therefore still consumable and is kept here
|
|
124
|
+
// (the next sweep, once `now > expiresAt`, removes it). This intentionally
|
|
125
|
+
// keeps one more record than the `PendingAuthStore.deleteExpired` port doc
|
|
126
|
+
// ("at or before nowMs") so the sweep and the consume check agree on the
|
|
127
|
+
// expiry boundary.
|
|
128
|
+
await db.deleteFrom('kubun_connector_pending_auth').where('expires_at', '<', cutoffMs).execute();
|
|
93
129
|
}
|
|
94
130
|
};
|
|
95
131
|
}
|
package/lib/migrations.js
CHANGED
|
@@ -19,7 +19,14 @@ export function getConnectorMigrations(ctx) {
|
|
|
19
19
|
// callback so the mint can add the controller recovery wrap.
|
|
20
20
|
.addColumn('controller_wrappable_did', t.text)// ms-epoch (Date.now) overflows int32 on Postgres; bigint holds it and
|
|
21
21
|
// the adapter's int8 parser reads it back as a JS number.
|
|
22
|
-
.addColumn('created_at', t.bigint, (col)=>col.notNull())
|
|
22
|
+
.addColumn('created_at', t.bigint, (col)=>col.notNull())// The callback's redirect target and the scopes requested at authorize
|
|
23
|
+
// time, both needed to complete the token exchange and to fill in
|
|
24
|
+
// `PendingAuthRecord`'s required fields.
|
|
25
|
+
.addColumn('redirect_url', t.text, (col)=>col.notNull())// JSON-encoded `Array<string>`.
|
|
26
|
+
.addColumn('scopes', t.text, (col)=>col.notNull())// ms-epoch (Date.now) overflows int32 on Postgres; bigint holds it and
|
|
27
|
+
// the adapter's int8 parser reads it back as a JS number. The sweep in
|
|
28
|
+
// `deleteExpiredPendingAuth` keys off this column, not `created_at`.
|
|
29
|
+
.addColumn('expires_at', t.bigint, (col)=>col.notNull()).execute();
|
|
23
30
|
},
|
|
24
31
|
async down (db) {
|
|
25
32
|
await db.schema.dropTable('kubun_connector_pending_auth').ifExists().execute();
|
package/lib/tables.d.ts
CHANGED
|
@@ -28,6 +28,10 @@ export type ConnectorPendingAuthTable = {
|
|
|
28
28
|
*/
|
|
29
29
|
controller_wrappable_did: string | null;
|
|
30
30
|
created_at: number;
|
|
31
|
+
redirect_url: string;
|
|
32
|
+
/** JSON-encoded `Array<string>`. See `PendingAuthRecord.scopes`. */
|
|
33
|
+
scopes: string;
|
|
34
|
+
expires_at: number;
|
|
31
35
|
};
|
|
32
36
|
export type ConnectorPendingAuth = Selectable<ConnectorPendingAuthTable>;
|
|
33
37
|
export type ConnectorTables = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/store-connector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "see LICENSE.md",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"LICENSE.md"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@kubun/db": "^0.
|
|
19
|
-
"@kubun/db-adapter": "^0.
|
|
18
|
+
"@kubun/db": "^0.17.0",
|
|
19
|
+
"@kubun/db-adapter": "^0.17.0",
|
|
20
20
|
"kysely": "^0.29.5"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@kubun/db-better-sqlite": "^0.
|
|
24
|
-
"@kubun/db-postgres": "^0.
|
|
23
|
+
"@kubun/db-better-sqlite": "^0.17.0",
|
|
24
|
+
"@kubun/db-postgres": "^0.17.0",
|
|
25
25
|
"@testcontainers/postgresql": "^12.1.0"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
|
|
32
32
|
"build:clean": "del lib",
|
|
33
|
-
"build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
|
|
34
|
-
"build:types": "tsc --emitDeclarationOnly --skipLibCheck",
|
|
33
|
+
"build:js": "del 'lib/**/*.js' && swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
|
|
34
|
+
"build:types": "del 'lib/**/*.d.ts' 'lib/**/*.d.ts.map' && tsc --emitDeclarationOnly --skipLibCheck",
|
|
35
35
|
"build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
|
|
36
36
|
"test": "pnpm run test:types && pnpm run test:unit",
|
|
37
37
|
"test:types": "tsc --noEmit -p tsconfig.test.json",
|