@kubun/plugin-connector 0.14.0 → 0.14.2
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/action.d.ts +9 -0
- package/lib/action.js +9 -1
- package/lib/auth-required.d.ts +11 -0
- package/lib/auth-required.js +17 -0
- package/lib/credential-refresh-bridge.d.ts +11 -0
- package/lib/credential-refresh-bridge.js +27 -0
- package/lib/credential.d.ts +2 -2
- package/lib/credential.js +1 -1
- package/lib/index.d.ts +8 -2
- package/lib/index.js +293 -56
- package/lib/manager.d.ts +71 -3
- package/lib/manager.js +41 -6
- package/lib/oauth.d.ts +5 -1
- package/lib/oauth.js +5 -2
- package/lib/periodic-sync.d.ts +4 -0
- package/lib/periodic-sync.js +12 -0
- package/lib/schema.d.ts +45 -1
- package/lib/schema.js +122 -0
- package/lib/sync/orchestrate.d.ts +11 -1
- package/lib/sync/orchestrate.js +118 -76
- package/lib/sync/workflow.d.ts +11 -0
- package/lib/sync/workflow.js +26 -2
- package/lib/write-grants.d.ts +10 -2
- package/lib/write-grants.js +10 -1
- package/package.json +10 -10
package/lib/sync/workflow.js
CHANGED
|
@@ -14,7 +14,7 @@ import { MAX_LOGGED_ERRORS } from './processor.js';
|
|
|
14
14
|
* definitions (two "boots" over one DB) do not share iterators — which is what
|
|
15
15
|
* makes crash-recovery re-open the provider from the checkpoint.
|
|
16
16
|
*/ export function createConnectorSyncWorkflow(params) {
|
|
17
|
-
const { registry, credentialProvider, stateStore, syncEventEmitter, stores, mutateDocuments, logger } = params;
|
|
17
|
+
const { registry, credentialProvider, stateStore, syncEventEmitter, stores, mutateDocuments, logger, emitAuthRequired } = params;
|
|
18
18
|
const sessions = new Map();
|
|
19
19
|
const sessionKey = (state)=>`${state.connectorName}:${state.ownerDID}`;
|
|
20
20
|
const effectiveBoundary = params.boundary ?? {
|
|
@@ -64,6 +64,15 @@ import { MAX_LOGGED_ERRORS } from './processor.js';
|
|
|
64
64
|
lastSyncedAt: new Date().toISOString(),
|
|
65
65
|
failedSample: []
|
|
66
66
|
};
|
|
67
|
+
// Only a genuinely absent credential asks for (re)authorization; an absent
|
|
68
|
+
// server provider stays a silent configuration outcome. Both still end
|
|
69
|
+
// offline (the event is additive, not a change to the offline outcome).
|
|
70
|
+
if (providerName != null && credential == null) {
|
|
71
|
+
emitAuthRequired?.({
|
|
72
|
+
provider: providerName,
|
|
73
|
+
ownerDID
|
|
74
|
+
});
|
|
75
|
+
}
|
|
67
76
|
return {
|
|
68
77
|
status: 'end',
|
|
69
78
|
state: offlineState,
|
|
@@ -126,6 +135,20 @@ import { MAX_LOGGED_ERRORS } from './processor.js';
|
|
|
126
135
|
}
|
|
127
136
|
const providerName = connector.auth.provider !== 'device' ? connector.auth.provider : null;
|
|
128
137
|
const credential = providerName ? await credentialProvider.get(providerName, state.ownerDID) : null;
|
|
138
|
+
// A crash-recovered batch whose credential has since been removed cannot
|
|
139
|
+
// resume: signal (re)authorization and end offline rather than opening the
|
|
140
|
+
// provider with an empty credential (which would fail mid-sync ⇒ error).
|
|
141
|
+
if (providerName != null && credential == null) {
|
|
142
|
+
emitAuthRequired?.({
|
|
143
|
+
provider: providerName,
|
|
144
|
+
ownerDID: state.ownerDID
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
status: 'end',
|
|
148
|
+
state,
|
|
149
|
+
outcome: 'offline'
|
|
150
|
+
};
|
|
151
|
+
}
|
|
129
152
|
if (connector.serverProvider == null) {
|
|
130
153
|
throw new Error(`Connector "${state.connectorName}" has no server provider`);
|
|
131
154
|
}
|
|
@@ -258,7 +281,8 @@ import { MAX_LOGGED_ERRORS } from './processor.js';
|
|
|
258
281
|
start,
|
|
259
282
|
batch,
|
|
260
283
|
finalize
|
|
261
|
-
}
|
|
284
|
+
},
|
|
285
|
+
managed: true
|
|
262
286
|
}
|
|
263
287
|
};
|
|
264
288
|
}
|
package/lib/write-grants.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type { CredentialProvider } from '@kubun/credential-types';
|
|
1
|
+
import type { Credential, CredentialProvider } from '@kubun/credential-types';
|
|
2
2
|
import { HLC } from '@kubun/hlc';
|
|
3
3
|
import { getDelegationStore } from '@kubun/store-delegation';
|
|
4
4
|
import type { ConnectorRegistry } from './registry.js';
|
|
5
5
|
import type { ConnectorWriteGrant } from './schema.js';
|
|
6
|
+
type CredentialFacade = CredentialProvider & {
|
|
7
|
+
peek?(providerName: string, ownerDID: string): Promise<Credential | null>;
|
|
8
|
+
};
|
|
6
9
|
export type WriteGrantContextParams = {
|
|
7
10
|
registry: ConnectorRegistry;
|
|
8
11
|
/** The server identity a viewer delegates its write authority to (`aud`). */
|
|
@@ -10,8 +13,12 @@ export type WriteGrantContextParams = {
|
|
|
10
13
|
/** The calling viewer (raw, unnormalized) — the grantor of every cap. */
|
|
11
14
|
viewerDID: string;
|
|
12
15
|
stores: Parameters<typeof getDelegationStore>[0];
|
|
13
|
-
credentialProvider:
|
|
16
|
+
credentialProvider: CredentialFacade;
|
|
14
17
|
hlc: HLC;
|
|
18
|
+
onCredentialRemoved?: (params: {
|
|
19
|
+
provider: string;
|
|
20
|
+
ownerDID: string;
|
|
21
|
+
}) => void;
|
|
15
22
|
};
|
|
16
23
|
export type WriteGrantContext = {
|
|
17
24
|
grantWriteCapability(args: {
|
|
@@ -33,3 +40,4 @@ export type WriteGrantContext = {
|
|
|
33
40
|
* stored `resource` string handled here is only the revoke/list selector.
|
|
34
41
|
*/
|
|
35
42
|
export declare function createWriteGrantContext(params: WriteGrantContextParams): WriteGrantContext;
|
|
43
|
+
export {};
|
package/lib/write-grants.js
CHANGED
|
@@ -240,8 +240,17 @@ async function revokeCapsSubsetOf(delegationStore, grantor, audience, allowed) {
|
|
|
240
240
|
}
|
|
241
241
|
const delegationStore = await getDelegationStore(stores);
|
|
242
242
|
const removed = await revokeCapsSubsetOf(delegationStore, viewerDID, audienceDID, providerURNs);
|
|
243
|
-
|
|
243
|
+
// Test existence with the non-refreshing peek (falling back to `get` only
|
|
244
|
+
// when a caller supplies no peek) so disconnecting an expiring credential
|
|
245
|
+
// does not trigger a transparent refresh right before the delete.
|
|
246
|
+
const credentialExisted = (credentialProvider.peek != null ? await credentialProvider.peek(provider, viewerDID) : await credentialProvider.get(provider, viewerDID)) != null;
|
|
244
247
|
await credentialProvider.delete(provider, viewerDID);
|
|
248
|
+
if (credentialExisted) {
|
|
249
|
+
params.onCredentialRemoved?.({
|
|
250
|
+
provider,
|
|
251
|
+
ownerDID: viewerDID
|
|
252
|
+
});
|
|
253
|
+
}
|
|
245
254
|
return removed > 0 || credentialExisted;
|
|
246
255
|
},
|
|
247
256
|
connectorWriteGrants: async ()=>{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/plugin-connector",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"license": "see LICENSE.md",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -18,23 +18,23 @@
|
|
|
18
18
|
"@kokuin/jwe": "^0.1.0",
|
|
19
19
|
"@kokuin/token": "^0.5.0",
|
|
20
20
|
"@kubun/connector": "^0.14.0",
|
|
21
|
-
"@kubun/credential": "^0.14.
|
|
21
|
+
"@kubun/credential": "^0.14.2",
|
|
22
22
|
"@kubun/credential-types": "^0.14.0",
|
|
23
23
|
"@kubun/db": "^0.14.0",
|
|
24
|
-
"@kubun/engine": "^0.14.
|
|
25
|
-
"@kubun/graphql": "^0.14.
|
|
24
|
+
"@kubun/engine": "^0.14.1",
|
|
25
|
+
"@kubun/graphql": "^0.14.1",
|
|
26
26
|
"@kubun/hlc": "^0.14.0",
|
|
27
27
|
"@kubun/id": "^0.14.0",
|
|
28
28
|
"@kubun/logger": "^0.14.0",
|
|
29
29
|
"@kubun/plugin-blob-api": "^0.14.0",
|
|
30
|
-
"@kubun/plugin-workflow-api": "^0.14.
|
|
31
|
-
"@kubun/protocol": "^0.14.
|
|
30
|
+
"@kubun/plugin-workflow-api": "^0.14.1",
|
|
31
|
+
"@kubun/protocol": "^0.14.1",
|
|
32
32
|
"@kubun/service-credential-api": "^0.14.0",
|
|
33
33
|
"@kubun/store-connector": "^0.14.0",
|
|
34
34
|
"@kubun/store-credential": "^0.14.0",
|
|
35
35
|
"@kubun/store-delegation": "^0.14.0",
|
|
36
|
-
"@kubun/store-graph": "^0.14.
|
|
37
|
-
"@noble/hashes": "^2.
|
|
36
|
+
"@kubun/store-graph": "^0.14.1",
|
|
37
|
+
"@noble/hashes": "^2.4.0",
|
|
38
38
|
"@sozai/async": "^0.2.1",
|
|
39
39
|
"@sozai/codec": "^0.4.0",
|
|
40
40
|
"@sozai/event": "^0.1.3",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"@kubun/plugin-credential": "^0.14.0",
|
|
57
57
|
"@kubun/plugin-service-client": "^0.14.0",
|
|
58
58
|
"@kubun/plugin-service-server": "^0.14.0",
|
|
59
|
-
"@kubun/plugin-workflow": "^0.14.
|
|
59
|
+
"@kubun/plugin-workflow": "^0.14.1",
|
|
60
60
|
"@kubun/service-controller-log-api": "^0.14.0",
|
|
61
61
|
"@kubun/store-blob": "^0.14.0",
|
|
62
62
|
"@kubun/store-controller": "^0.14.0",
|
|
63
|
-
"@kubun/store-workflow": "^0.14.
|
|
63
|
+
"@kubun/store-workflow": "^0.14.1",
|
|
64
64
|
"@kubun/test-utils": "^0.13.0",
|
|
65
65
|
"@testcontainers/postgresql": "^12.1.0",
|
|
66
66
|
"get-port": "^7.2.0"
|