@enbox/api 0.2.3 → 0.2.4
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/README.md +235 -35
- package/dist/browser.mjs +13 -13
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/dwn-api.js +24 -10
- package/dist/esm/dwn-api.js.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/live-query.js +34 -5
- package/dist/esm/live-query.js.map +1 -1
- package/dist/esm/permission-grant.js +3 -6
- package/dist/esm/permission-grant.js.map +1 -1
- package/dist/esm/permission-request.js +4 -7
- package/dist/esm/permission-request.js.map +1 -1
- package/dist/esm/record-data.js +131 -0
- package/dist/esm/record-data.js.map +1 -0
- package/dist/esm/record-types.js +9 -0
- package/dist/esm/record-types.js.map +1 -0
- package/dist/esm/record.js +58 -184
- package/dist/esm/record.js.map +1 -1
- package/dist/esm/repository-types.js +13 -0
- package/dist/esm/repository-types.js.map +1 -0
- package/dist/esm/repository.js +347 -0
- package/dist/esm/repository.js.map +1 -0
- package/dist/esm/typed-live-query.js +101 -0
- package/dist/esm/typed-live-query.js.map +1 -0
- package/dist/esm/typed-record.js +227 -0
- package/dist/esm/typed-record.js.map +1 -0
- package/dist/esm/typed-web5.js +134 -23
- package/dist/esm/typed-web5.js.map +1 -1
- package/dist/esm/web5.js +78 -20
- package/dist/esm/web5.js.map +1 -1
- package/dist/types/dwn-api.d.ts.map +1 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/live-query.d.ts +43 -4
- package/dist/types/live-query.d.ts.map +1 -1
- package/dist/types/permission-grant.d.ts +1 -1
- package/dist/types/permission-grant.d.ts.map +1 -1
- package/dist/types/permission-request.d.ts +1 -1
- package/dist/types/permission-request.d.ts.map +1 -1
- package/dist/types/record-data.d.ts +49 -0
- package/dist/types/record-data.d.ts.map +1 -0
- package/dist/types/record-types.d.ts +145 -0
- package/dist/types/record-types.d.ts.map +1 -0
- package/dist/types/record.d.ts +13 -144
- package/dist/types/record.d.ts.map +1 -1
- package/dist/types/repository-types.d.ts +137 -0
- package/dist/types/repository-types.d.ts.map +1 -0
- package/dist/types/repository.d.ts +59 -0
- package/dist/types/repository.d.ts.map +1 -0
- package/dist/types/typed-live-query.d.ts +86 -0
- package/dist/types/typed-live-query.d.ts.map +1 -0
- package/dist/types/typed-record.d.ts +179 -0
- package/dist/types/typed-record.d.ts.map +1 -0
- package/dist/types/typed-web5.d.ts +55 -24
- package/dist/types/typed-web5.d.ts.map +1 -1
- package/dist/types/web5.d.ts +47 -2
- package/dist/types/web5.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/dwn-api.ts +30 -13
- package/src/index.ts +6 -0
- package/src/live-query.ts +71 -7
- package/src/permission-grant.ts +2 -3
- package/src/permission-request.ts +3 -4
- package/src/record-data.ts +155 -0
- package/src/record-types.ts +188 -0
- package/src/record.ts +86 -389
- package/src/repository-types.ts +249 -0
- package/src/repository.ts +391 -0
- package/src/typed-live-query.ts +156 -0
- package/src/typed-record.ts +309 -0
- package/src/typed-web5.ts +202 -49
- package/src/web5.ts +150 -23
package/src/web5.ts
CHANGED
|
@@ -96,6 +96,38 @@ export type Web5AnonymousApi = {
|
|
|
96
96
|
dwn: DwnReaderApi;
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
+
/** Parameters passed to the onProviderAuthRequired callback. */
|
|
100
|
+
export type ProviderAuthParams = {
|
|
101
|
+
/** Full authorize URL to open in a browser (query params already appended). */
|
|
102
|
+
authorizeUrl: string;
|
|
103
|
+
/** The DWN endpoint URL this auth is for (informational). */
|
|
104
|
+
dwnEndpoint: string;
|
|
105
|
+
/** CSRF nonce — the provider will return this unchanged in the redirect. */
|
|
106
|
+
state: string;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** Result returned by the app after the user completes provider auth. */
|
|
110
|
+
export type ProviderAuthResult = {
|
|
111
|
+
/** Authorization code from the provider's redirect. */
|
|
112
|
+
code: string;
|
|
113
|
+
/** Must match the state from ProviderAuthParams (CSRF validation). */
|
|
114
|
+
state: string;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/** Persisted registration token data for a DWN endpoint. */
|
|
118
|
+
export type RegistrationTokenData = {
|
|
119
|
+
/** Opaque registration token for POST /registration. */
|
|
120
|
+
registrationToken: string;
|
|
121
|
+
/** Refresh token for obtaining new registration tokens. */
|
|
122
|
+
refreshToken?: string;
|
|
123
|
+
/** Unix timestamp (ms) when the token expires. Undefined = never expires. */
|
|
124
|
+
expiresAt?: number;
|
|
125
|
+
/** Provider's token exchange URL (needed for code exchange). */
|
|
126
|
+
tokenUrl: string;
|
|
127
|
+
/** Provider's refresh URL (needed for token refresh). */
|
|
128
|
+
refreshUrl?: string;
|
|
129
|
+
};
|
|
130
|
+
|
|
99
131
|
/** Optional overrides that can be provided when calling {@link Web5.connect}. */
|
|
100
132
|
export type Web5ConnectOptions = {
|
|
101
133
|
/**
|
|
@@ -190,10 +222,29 @@ export type Web5ConnectOptions = {
|
|
|
190
222
|
* If registration is successful, the `onSuccess` callback will be called.
|
|
191
223
|
*/
|
|
192
224
|
registration? : {
|
|
193
|
-
/** Called when all of the DWN registrations are successful */
|
|
194
|
-
onSuccess: () => void;
|
|
195
|
-
/** Called when any of the DWN registrations fail */
|
|
196
|
-
onFailure: (error: any) => void;
|
|
225
|
+
/** Called when all of the DWN registrations are successful. */
|
|
226
|
+
onSuccess : () => void;
|
|
227
|
+
/** Called when any of the DWN registrations fail. */
|
|
228
|
+
onFailure : (error: any) => void;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Called when a DWN endpoint requires provider auth (`'provider-auth-v0'`).
|
|
232
|
+
* The app is responsible for opening the authorizeUrl in a browser,
|
|
233
|
+
* capturing the redirect back, and returning the auth code.
|
|
234
|
+
* If not provided, provider-auth endpoints fall back to PoW registration.
|
|
235
|
+
*/
|
|
236
|
+
onProviderAuthRequired? : (params: ProviderAuthParams) => Promise<ProviderAuthResult>;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Pre-existing registration tokens from a previous session, keyed by DWN endpoint URL.
|
|
240
|
+
* If a valid (non-expired) token exists for an endpoint, it is used directly.
|
|
241
|
+
*/
|
|
242
|
+
registrationTokens? : Record<string, RegistrationTokenData>;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Called when new registration tokens are obtained so the app can persist them.
|
|
246
|
+
*/
|
|
247
|
+
onRegistrationTokens? : (tokens: Record<string, RegistrationTokenData>) => void;
|
|
197
248
|
}
|
|
198
249
|
};
|
|
199
250
|
|
|
@@ -449,17 +500,14 @@ export class Web5 {
|
|
|
449
500
|
throw new Error(`Failed to connect to wallet: ${error.message}`);
|
|
450
501
|
}
|
|
451
502
|
} else {
|
|
452
|
-
// No connected identity
|
|
453
|
-
//
|
|
503
|
+
// No connected (WalletConnect) identity and no walletConnectOptions provided.
|
|
504
|
+
// Look for an existing local identity, or create one on first use.
|
|
454
505
|
const identities = await userAgent.identity.list();
|
|
455
506
|
|
|
456
|
-
|
|
457
|
-
const existingIdentityCount = identities.length;
|
|
458
|
-
if (existingIdentityCount === 0) {
|
|
459
|
-
// since we are creating a new identity, we will want to register sync for the created Did
|
|
507
|
+
if (identities.length === 0) {
|
|
460
508
|
registerSync = true;
|
|
461
509
|
|
|
462
|
-
//
|
|
510
|
+
// First use — generate a new Identity for the end-user.
|
|
463
511
|
identity = await userAgent.identity.create({
|
|
464
512
|
didMethod : 'dht',
|
|
465
513
|
metadata : { name: 'Default' },
|
|
@@ -489,8 +537,9 @@ export class Web5 {
|
|
|
489
537
|
});
|
|
490
538
|
|
|
491
539
|
} else {
|
|
492
|
-
//
|
|
493
|
-
//
|
|
540
|
+
// Reconnecting — use the first local identity. When the agent manages
|
|
541
|
+
// multiple identities (e.g. created via agent.identity.create()), the
|
|
542
|
+
// first one returned by the store is used as the default for connect().
|
|
494
543
|
identity = identities[0];
|
|
495
544
|
}
|
|
496
545
|
}
|
|
@@ -500,27 +549,105 @@ export class Web5 {
|
|
|
500
549
|
// If the stored identity has a connected DID, use the identity DID as the delegated DID, otherwise it is undefined.
|
|
501
550
|
delegateDid = identity.metadata.connectedDid ? identity.did.uri : undefined;
|
|
502
551
|
if (registration !== undefined) {
|
|
503
|
-
|
|
552
|
+
const updatedTokens: Record<string, RegistrationTokenData> = {
|
|
553
|
+
...(registration.registrationTokens ?? {}),
|
|
554
|
+
};
|
|
555
|
+
|
|
504
556
|
try {
|
|
505
557
|
for (const dwnEndpoint of serviceEndpointNodes) {
|
|
506
|
-
// check if endpoint needs registration
|
|
507
558
|
const serverInfo = await userAgent.rpc.getServerInfo(dwnEndpoint);
|
|
559
|
+
|
|
508
560
|
if (serverInfo.registrationRequirements.length === 0) {
|
|
509
|
-
// no registration required
|
|
510
561
|
continue;
|
|
511
562
|
}
|
|
512
563
|
|
|
513
|
-
//
|
|
514
|
-
|
|
564
|
+
// Deduplicate DIDs to register.
|
|
565
|
+
const didsToRegister = [agent.agentDid.uri, connectedDid]
|
|
566
|
+
.filter((did, i, arr): did is string => arr.indexOf(did) === i);
|
|
567
|
+
|
|
568
|
+
const hasProviderAuth = serverInfo.registrationRequirements.includes('provider-auth-v0')
|
|
569
|
+
&& serverInfo.providerAuth !== undefined;
|
|
570
|
+
|
|
571
|
+
if (hasProviderAuth && registration.onProviderAuthRequired) {
|
|
572
|
+
// --- Provider Auth Path ---
|
|
573
|
+
let tokenData = updatedTokens[dwnEndpoint];
|
|
574
|
+
|
|
575
|
+
// Refresh expired tokens.
|
|
576
|
+
if (tokenData?.expiresAt !== undefined && tokenData.expiresAt < Date.now()) {
|
|
577
|
+
if (tokenData.refreshUrl && tokenData.refreshToken) {
|
|
578
|
+
const refreshed = await DwnRegistrar.refreshRegistrationToken(
|
|
579
|
+
tokenData.refreshUrl, tokenData.refreshToken,
|
|
580
|
+
);
|
|
581
|
+
tokenData = {
|
|
582
|
+
registrationToken : refreshed.registrationToken,
|
|
583
|
+
refreshToken : refreshed.refreshToken,
|
|
584
|
+
expiresAt : refreshed.expiresIn !== undefined
|
|
585
|
+
? Date.now() + (refreshed.expiresIn * 1000) : undefined,
|
|
586
|
+
tokenUrl : tokenData.tokenUrl,
|
|
587
|
+
refreshUrl : tokenData.refreshUrl,
|
|
588
|
+
};
|
|
589
|
+
updatedTokens[dwnEndpoint] = tokenData;
|
|
590
|
+
} else {
|
|
591
|
+
tokenData = undefined;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Run the auth flow if no valid token exists.
|
|
596
|
+
if (tokenData === undefined) {
|
|
597
|
+
const state = crypto.randomUUID();
|
|
598
|
+
const providerAuth = serverInfo.providerAuth!;
|
|
599
|
+
const separator = providerAuth.authorizeUrl.includes('?') ? '&' : '?';
|
|
600
|
+
const authorizeUrl = `${providerAuth.authorizeUrl}${separator}`
|
|
601
|
+
+ `redirect_uri=${encodeURIComponent(dwnEndpoint)}`
|
|
602
|
+
+ `&state=${encodeURIComponent(state)}`;
|
|
603
|
+
|
|
604
|
+
const authResult = await registration.onProviderAuthRequired({
|
|
605
|
+
authorizeUrl,
|
|
606
|
+
dwnEndpoint,
|
|
607
|
+
state,
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
if (authResult.state !== state) {
|
|
611
|
+
throw new Error('Provider auth state mismatch — possible CSRF attack.');
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const tokenResponse = await DwnRegistrar.exchangeAuthCode(
|
|
615
|
+
providerAuth.tokenUrl, authResult.code, dwnEndpoint,
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
tokenData = {
|
|
619
|
+
registrationToken : tokenResponse.registrationToken,
|
|
620
|
+
refreshToken : tokenResponse.refreshToken,
|
|
621
|
+
expiresAt : tokenResponse.expiresIn !== undefined
|
|
622
|
+
? Date.now() + (tokenResponse.expiresIn * 1000) : undefined,
|
|
623
|
+
tokenUrl : providerAuth.tokenUrl,
|
|
624
|
+
refreshUrl : providerAuth.refreshUrl,
|
|
625
|
+
};
|
|
626
|
+
updatedTokens[dwnEndpoint] = tokenData;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Register each DID using the provider auth token.
|
|
630
|
+
for (const did of didsToRegister) {
|
|
631
|
+
await DwnRegistrar.registerTenantWithToken(
|
|
632
|
+
dwnEndpoint, did, tokenData.registrationToken,
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
} else {
|
|
637
|
+
// --- Default Path (PoW / general registration) ---
|
|
638
|
+
for (const did of didsToRegister) {
|
|
639
|
+
await DwnRegistrar.registerTenant(dwnEndpoint, did);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
515
643
|
|
|
516
|
-
|
|
517
|
-
|
|
644
|
+
// Notify app of updated tokens for persistence.
|
|
645
|
+
if (registration.onRegistrationTokens) {
|
|
646
|
+
registration.onRegistrationTokens(updatedTokens);
|
|
518
647
|
}
|
|
519
648
|
|
|
520
|
-
// If no failures occurred, call the onSuccess callback
|
|
521
649
|
registration.onSuccess();
|
|
522
650
|
} catch (error) {
|
|
523
|
-
// for any failure, call the onFailure callback with the error
|
|
524
651
|
registration.onFailure(error);
|
|
525
652
|
}
|
|
526
653
|
}
|
|
@@ -599,7 +726,7 @@ export class Web5 {
|
|
|
599
726
|
const connectedProtocols = new Set<string>();
|
|
600
727
|
for (const grantMessage of grants) {
|
|
601
728
|
// use the delegateDid as the connectedDid of the grant as they do not yet support impersonation/delegation
|
|
602
|
-
const grant =
|
|
729
|
+
const grant = PermissionGrant.parse({ connectedDid: delegateDid, agent, message: grantMessage });
|
|
603
730
|
// store the grant as the owner of the DWN, this will allow the delegateDid to use the grant when impersonating the connectedDid
|
|
604
731
|
const { status } = await grant.store(true);
|
|
605
732
|
if (status.code !== 202) {
|