@enbox/dwn-clients 0.0.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/README.md +73 -0
- package/dist/esm/dwn-registrar.js +119 -0
- package/dist/esm/dwn-registrar.js.map +1 -0
- package/dist/esm/dwn-rpc-types.js +2 -0
- package/dist/esm/dwn-rpc-types.js.map +1 -0
- package/dist/esm/dwn-server-info-cache-memory.js +74 -0
- package/dist/esm/dwn-server-info-cache-memory.js.map +1 -0
- package/dist/esm/http-dwn-rpc-client.js +115 -0
- package/dist/esm/http-dwn-rpc-client.js.map +1 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/json-rpc-socket.js +175 -0
- package/dist/esm/json-rpc-socket.js.map +1 -0
- package/dist/esm/json-rpc.js +77 -0
- package/dist/esm/json-rpc.js.map +1 -0
- package/dist/esm/registration-types.js +2 -0
- package/dist/esm/registration-types.js.map +1 -0
- package/dist/esm/rpc-client.js +123 -0
- package/dist/esm/rpc-client.js.map +1 -0
- package/dist/esm/server-info-types.js +2 -0
- package/dist/esm/server-info-types.js.map +1 -0
- package/dist/esm/utils.js +13 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/web-socket-clients.js +90 -0
- package/dist/esm/web-socket-clients.js.map +1 -0
- package/dist/types/dwn-registrar.d.ts +28 -0
- package/dist/types/dwn-registrar.d.ts.map +1 -0
- package/dist/types/dwn-rpc-types.d.ts +45 -0
- package/dist/types/dwn-rpc-types.d.ts.map +1 -0
- package/dist/types/dwn-server-info-cache-memory.d.ts +57 -0
- package/dist/types/dwn-server-info-cache-memory.d.ts.map +1 -0
- package/dist/types/http-dwn-rpc-client.d.ts +13 -0
- package/dist/types/http-dwn-rpc-client.d.ts.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/json-rpc-socket.d.ts +40 -0
- package/dist/types/json-rpc-socket.d.ts.map +1 -0
- package/dist/types/json-rpc.d.ts +64 -0
- package/dist/types/json-rpc.d.ts.map +1 -0
- package/dist/types/registration-types.d.ts +25 -0
- package/dist/types/registration-types.d.ts.map +1 -0
- package/dist/types/rpc-client.d.ts +51 -0
- package/dist/types/rpc-client.d.ts.map +1 -0
- package/dist/types/server-info-types.d.ts +28 -0
- package/dist/types/server-info-types.d.ts.map +1 -0
- package/dist/types/utils.d.ts +3 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/web-socket-clients.d.ts +10 -0
- package/dist/types/web-socket-clients.d.ts.map +1 -0
- package/package.json +66 -0
- package/src/dwn-registrar.ts +129 -0
- package/src/dwn-rpc-types.ts +55 -0
- package/src/dwn-server-info-cache-memory.ts +80 -0
- package/src/http-dwn-rpc-client.ts +122 -0
- package/src/index.ts +11 -0
- package/src/json-rpc-socket.ts +198 -0
- package/src/json-rpc.ts +142 -0
- package/src/registration-types.ts +26 -0
- package/src/rpc-client.ts +160 -0
- package/src/server-info-types.ts +29 -0
- package/src/utils.ts +14 -0
- package/src/web-socket-clients.ts +107 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @enbox/dwn-clients
|
|
2
|
+
|
|
3
|
+
Client libraries and shared types for communicating with [DWN Servers](../dwn-server).
|
|
4
|
+
|
|
5
|
+
## What's Included
|
|
6
|
+
|
|
7
|
+
### Transport Clients
|
|
8
|
+
|
|
9
|
+
| Export | Description |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `HttpDwnRpcClient` | HTTP client for DWN JSON-RPC endpoints |
|
|
12
|
+
| `WebSocketDwnRpcClient` | WebSocket client for DWN JSON-RPC with subscriptions |
|
|
13
|
+
| `Web5RpcClient` | Transport multiplexer — routes to HTTP or WebSocket based on URL scheme |
|
|
14
|
+
| `HttpWeb5RpcClient` | HTTP client with DID RPC support |
|
|
15
|
+
| `WebSocketWeb5RpcClient` | WebSocket client with DID RPC support |
|
|
16
|
+
| `DwnRegistrar` | HTTP client for DWN tenant registration (proof-of-work challenge flow) |
|
|
17
|
+
|
|
18
|
+
### JSON-RPC
|
|
19
|
+
|
|
20
|
+
| Export | Description |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `createJsonRpcRequest` | Factory for JSON-RPC 2.0 request objects |
|
|
23
|
+
| `createJsonRpcSuccessResponse` | Factory for JSON-RPC success responses |
|
|
24
|
+
| `createJsonRpcErrorResponse` | Factory for JSON-RPC error responses |
|
|
25
|
+
| `JsonRpcSocket` | Persistent WebSocket wrapper for JSON-RPC messaging |
|
|
26
|
+
|
|
27
|
+
### Shared Types
|
|
28
|
+
|
|
29
|
+
Types used by both `@enbox/dwn-server` (server-side) and clients:
|
|
30
|
+
|
|
31
|
+
| Type | Description |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `ServerInfo` | Full `/info` endpoint response shape |
|
|
34
|
+
| `ProofOfWorkChallengeModel` | `GET /registration/proof-of-work` response |
|
|
35
|
+
| `RegistrationData` | Tenant registration data (`did` + `termsOfServiceHash`) |
|
|
36
|
+
| `RegistrationRequest` | `POST /registration` request body |
|
|
37
|
+
| `JsonRpcRequest` / `JsonRpcResponse` | JSON-RPC 2.0 message envelopes |
|
|
38
|
+
| `DwnRpcRequest` / `DwnRpcResponse` | DWN-specific RPC request/response |
|
|
39
|
+
|
|
40
|
+
### Utilities
|
|
41
|
+
|
|
42
|
+
| Export | Description |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `concatenateUrl` | Join a base URL and path segment with exactly one `/` |
|
|
45
|
+
| `DwnServerInfoCacheMemory` | In-memory cache for `ServerInfo` with TTL |
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { Web5RpcClient } from '@enbox/dwn-clients';
|
|
51
|
+
|
|
52
|
+
const rpc = new Web5RpcClient();
|
|
53
|
+
|
|
54
|
+
// Send a DWN request over HTTP
|
|
55
|
+
const response = await rpc.sendDwnRequest({
|
|
56
|
+
dwnUrl : 'https://dwn.example.com',
|
|
57
|
+
targetDid : 'did:dht:abc123',
|
|
58
|
+
message : recordsWriteMessage,
|
|
59
|
+
data : dataStream,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Get server info
|
|
63
|
+
const info = await rpc.getServerInfo('https://dwn.example.com');
|
|
64
|
+
console.log(info.registrationRequirements); // ['proof-of-work-sha256-v0', 'terms-of-service']
|
|
65
|
+
|
|
66
|
+
// Register a tenant
|
|
67
|
+
import { DwnRegistrar } from '@enbox/dwn-clients';
|
|
68
|
+
await DwnRegistrar.registerTenant('https://dwn.example.com', 'did:dht:abc123');
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
Apache-2.0
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { concatenateUrl } from './utils.js';
|
|
11
|
+
import { Convert } from '@enbox/common';
|
|
12
|
+
import { CryptoUtils, Sha256 } from '@enbox/crypto';
|
|
13
|
+
/**
|
|
14
|
+
* A client for registering tenants with a DWN.
|
|
15
|
+
*/
|
|
16
|
+
export class DwnRegistrar {
|
|
17
|
+
/**
|
|
18
|
+
* Registers a new tenant with the given DWN.
|
|
19
|
+
* NOTE: Assumes the user has already accepted the terms of service.
|
|
20
|
+
* NOTE: Currently the DWN Server from `dwn-server` does not require user signature.
|
|
21
|
+
*/
|
|
22
|
+
static registerTenant(dwnEndpoint, did) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const registrationEndpoint = concatenateUrl(dwnEndpoint, 'registration');
|
|
25
|
+
const termsOfUseEndpoint = concatenateUrl(registrationEndpoint, 'terms-of-service');
|
|
26
|
+
const proofOfWorkEndpoint = concatenateUrl(registrationEndpoint, 'proof-of-work');
|
|
27
|
+
// fetch the terms-of-service
|
|
28
|
+
const termsOfServiceGetResponse = yield fetch(termsOfUseEndpoint, {
|
|
29
|
+
method: 'GET',
|
|
30
|
+
});
|
|
31
|
+
if (termsOfServiceGetResponse.status !== 200) {
|
|
32
|
+
const statusCode = termsOfServiceGetResponse.status;
|
|
33
|
+
const statusText = termsOfServiceGetResponse.statusText;
|
|
34
|
+
const errorText = yield termsOfServiceGetResponse.text();
|
|
35
|
+
throw new Error(`Failed fetching terms-of-service: ${statusCode} ${statusText}: ${errorText}`);
|
|
36
|
+
}
|
|
37
|
+
const termsOfServiceFetched = yield termsOfServiceGetResponse.text();
|
|
38
|
+
// fetch the proof-of-work challenge
|
|
39
|
+
const proofOfWorkChallengeGetResponse = yield fetch(proofOfWorkEndpoint, {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
});
|
|
42
|
+
const { challengeNonce, maximumAllowedHashValue } = yield proofOfWorkChallengeGetResponse.json();
|
|
43
|
+
// create registration data based on the hash of the terms-of-service and the DID
|
|
44
|
+
const registrationData = {
|
|
45
|
+
did,
|
|
46
|
+
termsOfServiceHash: yield DwnRegistrar.hashAsHexString(termsOfServiceFetched),
|
|
47
|
+
};
|
|
48
|
+
// compute the proof-of-work response nonce based on the proof-of-work challenge and the registration data.
|
|
49
|
+
const responseNonce = yield DwnRegistrar.findQualifiedResponseNonce({
|
|
50
|
+
challengeNonce,
|
|
51
|
+
maximumAllowedHashValue,
|
|
52
|
+
requestData: JSON.stringify(registrationData),
|
|
53
|
+
});
|
|
54
|
+
// send the registration request to the server
|
|
55
|
+
const registrationRequest = {
|
|
56
|
+
registrationData,
|
|
57
|
+
proofOfWork: {
|
|
58
|
+
challengeNonce,
|
|
59
|
+
responseNonce,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const registrationResponse = yield fetch(registrationEndpoint, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify(registrationRequest),
|
|
66
|
+
});
|
|
67
|
+
if (registrationResponse.status !== 200) {
|
|
68
|
+
const statusCode = registrationResponse.status;
|
|
69
|
+
const statusText = registrationResponse.statusText;
|
|
70
|
+
const errorText = yield registrationResponse.text();
|
|
71
|
+
throw new Error(`Registration failed: ${statusCode} ${statusText}: ${errorText}`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Computes the SHA-256 hash of the given array of strings.
|
|
77
|
+
*/
|
|
78
|
+
static hashAsHexString(input) {
|
|
79
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
const hashAsBytes = yield Sha256.digest({ data: Convert.string(input).toUint8Array() });
|
|
81
|
+
const hashAsHex = Convert.uint8Array(hashAsBytes).toHex();
|
|
82
|
+
return hashAsHex;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Finds a response nonce that qualifies the difficulty requirement for the given proof-of-work challenge and request data.
|
|
87
|
+
*/
|
|
88
|
+
static findQualifiedResponseNonce(input) {
|
|
89
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
const startTime = Date.now();
|
|
91
|
+
const { maximumAllowedHashValue, challengeNonce, requestData } = input;
|
|
92
|
+
const maximumAllowedHashValueAsBigInt = BigInt(`0x${maximumAllowedHashValue}`);
|
|
93
|
+
let iterations = 1;
|
|
94
|
+
let responseNonce;
|
|
95
|
+
let qualifiedSolutionNonceFound = false;
|
|
96
|
+
do {
|
|
97
|
+
responseNonce = yield this.generateNonce();
|
|
98
|
+
const computedHash = yield DwnRegistrar.hashAsHexString(challengeNonce + responseNonce + requestData);
|
|
99
|
+
const computedHashAsBigInt = BigInt(`0x${computedHash}`);
|
|
100
|
+
qualifiedSolutionNonceFound = computedHashAsBigInt <= maximumAllowedHashValueAsBigInt;
|
|
101
|
+
iterations++;
|
|
102
|
+
} while (!qualifiedSolutionNonceFound);
|
|
103
|
+
// Log final/successful iteration.
|
|
104
|
+
console.log(`iterations: ${iterations}, time lapsed: ${Date.now() - startTime} ms`);
|
|
105
|
+
return responseNonce;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Generates 32 random bytes expressed as a HEX string.
|
|
110
|
+
*/
|
|
111
|
+
static generateNonce() {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
const randomBytes = CryptoUtils.randomBytes(32);
|
|
114
|
+
const hexString = Convert.uint8Array(randomBytes).toHex().toUpperCase();
|
|
115
|
+
return hexString;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=dwn-registrar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dwn-registrar.js","sourceRoot":"","sources":["../../src/dwn-registrar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dwn-rpc-types.js","sourceRoot":"","sources":["../../src/dwn-rpc-types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import ms from 'ms';
|
|
11
|
+
import { TtlCache } from '@enbox/common';
|
|
12
|
+
export class DwnServerInfoCacheMemory {
|
|
13
|
+
constructor({ ttl = '15m' } = {}) {
|
|
14
|
+
this.cache = new TtlCache({ ttl: ms(ttl) });
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves a DWN ServerInfo entry from the cache.
|
|
18
|
+
*
|
|
19
|
+
* If the cached item has exceeded its TTL, it's scheduled for deletion and undefined is returned.
|
|
20
|
+
*
|
|
21
|
+
* @param dwnUrl - The DWN URL endpoint string used as the key for getting the entry.
|
|
22
|
+
* @returns The cached DWN ServerInfo entry or undefined if not found or expired.
|
|
23
|
+
*/
|
|
24
|
+
get(dwnUrl) {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
return this.cache.get(dwnUrl);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Stores a DWN ServerInfo entry in the cache with a TTL.
|
|
31
|
+
*
|
|
32
|
+
* @param dwnUrl - The DWN URL endpoint string used as the key for storing the entry.
|
|
33
|
+
* @param value - The DWN ServerInfo entry to be cached.
|
|
34
|
+
* @returns A promise that resolves when the operation is complete.
|
|
35
|
+
*/
|
|
36
|
+
set(dwnUrl, value) {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
this.cache.set(dwnUrl, value);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Deletes a DWN ServerInfo entry from the cache.
|
|
43
|
+
*
|
|
44
|
+
* @param dwnUrl - The DWN URL endpoint string used as the key for deletion.
|
|
45
|
+
* @returns A promise that resolves when the operation is complete.
|
|
46
|
+
*/
|
|
47
|
+
delete(dwnUrl) {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
this.cache.delete(dwnUrl);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Clears all entries from the cache.
|
|
54
|
+
*
|
|
55
|
+
* @returns A promise that resolves when the operation is complete.
|
|
56
|
+
*/
|
|
57
|
+
clear() {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
this.cache.clear();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* This method is a no-op but exists to be consistent with other DWN ServerInfo Cache
|
|
64
|
+
* implementations.
|
|
65
|
+
*
|
|
66
|
+
* @returns A promise that resolves immediately.
|
|
67
|
+
*/
|
|
68
|
+
close() {
|
|
69
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
+
// No-op since there is no underlying store to close.
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=dwn-server-info-cache-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dwn-server-info-cache-memory.js","sourceRoot":"","sources":["../../src/dwn-server-info-cache-memory.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAmBzC,MAAM,OAAO,wBAAwB;IAGnC,YAAY,EAAE,GAAG,GAAG,KAAK,KAAoC,EAAE;QAC7D,IAAI,CAAC,KAAK,GAAG,IAAI,QAAQ,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;OAOG;IACU,GAAG,CAAC,MAAc;;YAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;KAAA;IAED;;;;;;OAMG;IACU,GAAG,CAAC,MAAc,EAAE,KAAiB;;YAChD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;KAAA;IAED;;;;;OAKG;IACU,MAAM,CAAC,MAAc;;YAChC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;KAAA;IAED;;;;OAIG;IACU,KAAK;;YAChB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KAAA;IAED;;;;;OAKG;IACU,KAAK;;YAChB,qDAAqD;QACvD,CAAC;KAAA;CACF"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { CryptoUtils } from '@enbox/crypto';
|
|
11
|
+
import { DataStream } from '@enbox/dwn-sdk-js';
|
|
12
|
+
import { DwnServerInfoCacheMemory } from './dwn-server-info-cache-memory.js';
|
|
13
|
+
import { createJsonRpcRequest, parseJson } from './json-rpc.js';
|
|
14
|
+
/**
|
|
15
|
+
* HTTP client that can be used to communicate with Dwn Servers
|
|
16
|
+
*/
|
|
17
|
+
export class HttpDwnRpcClient {
|
|
18
|
+
constructor(serverInfoCache) {
|
|
19
|
+
this.serverInfoCache = serverInfoCache !== null && serverInfoCache !== void 0 ? serverInfoCache : new DwnServerInfoCacheMemory();
|
|
20
|
+
}
|
|
21
|
+
get transportProtocols() { return ['http:', 'https:']; }
|
|
22
|
+
sendDwnRequest(request) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const requestId = CryptoUtils.randomUuid();
|
|
25
|
+
const jsonRpcRequest = createJsonRpcRequest(requestId, 'dwn.processMessage', {
|
|
26
|
+
target: request.targetDid,
|
|
27
|
+
message: request.message
|
|
28
|
+
});
|
|
29
|
+
const requestHeaders = {
|
|
30
|
+
'dwn-request': JSON.stringify(jsonRpcRequest)
|
|
31
|
+
};
|
|
32
|
+
const fetchOpts = {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: requestHeaders,
|
|
35
|
+
};
|
|
36
|
+
if (request.data) {
|
|
37
|
+
requestHeaders['content-type'] = 'application/octet-stream';
|
|
38
|
+
fetchOpts.body = request.data;
|
|
39
|
+
}
|
|
40
|
+
const resp = yield fetch(request.dwnUrl, fetchOpts);
|
|
41
|
+
let dwnRpcResponse;
|
|
42
|
+
// When the server streams record data back, the JSON-RPC envelope is in the
|
|
43
|
+
// `dwn-response` header and the body is the raw data stream. Otherwise the
|
|
44
|
+
// entire JSON-RPC response is the body.
|
|
45
|
+
const hasDataStream = resp.headers.has('dwn-response');
|
|
46
|
+
if (hasDataStream) {
|
|
47
|
+
const jsonRpcResponse = parseJson(resp.headers.get('dwn-response'));
|
|
48
|
+
if (jsonRpcResponse == null) {
|
|
49
|
+
throw new Error(`failed to parse json rpc response. dwn url: ${request.dwnUrl}`);
|
|
50
|
+
}
|
|
51
|
+
dwnRpcResponse = jsonRpcResponse;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const responseBody = yield resp.text();
|
|
55
|
+
dwnRpcResponse = JSON.parse(responseBody);
|
|
56
|
+
}
|
|
57
|
+
if (dwnRpcResponse.error) {
|
|
58
|
+
const { code, message } = dwnRpcResponse.error;
|
|
59
|
+
throw new Error(`(${code}) - ${message}`);
|
|
60
|
+
}
|
|
61
|
+
// Materialise the response body before attaching to the reply.
|
|
62
|
+
// Bun (<=1.3.9) has a bug where resp.body.getReader() can intermittently
|
|
63
|
+
// return undefined, crashing DataStream.toBytes() in its finally block.
|
|
64
|
+
// Buffering via arrayBuffer() is a safe workaround.
|
|
65
|
+
// TODO: https://github.com/enboxorg/enbox/issues/90 — remove once Bun ships fix
|
|
66
|
+
const { reply } = dwnRpcResponse.result;
|
|
67
|
+
if (hasDataStream) {
|
|
68
|
+
const bodyBytes = new Uint8Array(yield resp.arrayBuffer());
|
|
69
|
+
const dataStream = DataStream.fromBytes(bodyBytes);
|
|
70
|
+
if (reply.record) {
|
|
71
|
+
reply.record.data = dataStream;
|
|
72
|
+
}
|
|
73
|
+
else if (reply.entry) {
|
|
74
|
+
reply.entry.data = dataStream;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return reply;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
getServerInfo(dwnUrl) {
|
|
81
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
82
|
+
const serverInfo = yield this.serverInfoCache.get(dwnUrl);
|
|
83
|
+
if (serverInfo) {
|
|
84
|
+
return serverInfo;
|
|
85
|
+
}
|
|
86
|
+
const url = new URL(dwnUrl);
|
|
87
|
+
// add `/info` to the dwn server url path
|
|
88
|
+
url.pathname.endsWith('/') ? url.pathname += 'info' : url.pathname += '/info';
|
|
89
|
+
try {
|
|
90
|
+
const response = yield fetch(url.toString());
|
|
91
|
+
if (response.ok) {
|
|
92
|
+
const results = yield response.json();
|
|
93
|
+
const serverInfo = {
|
|
94
|
+
maxFileSize: results.maxFileSize,
|
|
95
|
+
registrationRequirements: results.registrationRequirements,
|
|
96
|
+
server: results.server,
|
|
97
|
+
sdkVersion: results.sdkVersion,
|
|
98
|
+
url: results.url,
|
|
99
|
+
version: results.version,
|
|
100
|
+
webSocketSupport: results.webSocketSupport,
|
|
101
|
+
};
|
|
102
|
+
this.serverInfoCache.set(dwnUrl, serverInfo);
|
|
103
|
+
return serverInfo;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
throw new Error(`HTTP (${response.status}) - ${response.statusText}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw new Error(`Error encountered while processing response from ${url.toString()}: ${error.message}`);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=http-dwn-rpc-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-dwn-rpc-client.js","sourceRoot":"","sources":["../../src/http-dwn-rpc-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './dwn-registrar.js';
|
|
2
|
+
export * from './dwn-rpc-types.js';
|
|
3
|
+
export * from './dwn-server-info-cache-memory.js';
|
|
4
|
+
export * from './http-dwn-rpc-client.js';
|
|
5
|
+
export * from './json-rpc.js';
|
|
6
|
+
export * from './json-rpc-socket.js';
|
|
7
|
+
export * from './registration-types.js';
|
|
8
|
+
export * from './rpc-client.js';
|
|
9
|
+
export * from './server-info-types.js';
|
|
10
|
+
export * from './utils.js';
|
|
11
|
+
export * from './web-socket-clients.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC;AAClD,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { CryptoUtils } from '@enbox/crypto';
|
|
11
|
+
import { createJsonRpcSubscriptionRequest, parseJson } from './json-rpc.js';
|
|
12
|
+
/**
|
|
13
|
+
* Converts WebSocket message data to a string.
|
|
14
|
+
* Bun's native WebSocket delivers `event.data` as an `ArrayBuffer`,
|
|
15
|
+
* whereas Node.js `ws` delivers it as a `string`.
|
|
16
|
+
*/
|
|
17
|
+
function toText(data) {
|
|
18
|
+
if (typeof data === 'string') {
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
21
|
+
if (data instanceof ArrayBuffer) {
|
|
22
|
+
return new TextDecoder().decode(data);
|
|
23
|
+
}
|
|
24
|
+
// Buffer / Uint8Array fallback
|
|
25
|
+
if (data instanceof Uint8Array) {
|
|
26
|
+
return new TextDecoder().decode(data);
|
|
27
|
+
}
|
|
28
|
+
return String(data);
|
|
29
|
+
}
|
|
30
|
+
// These were arbitrarily chosen, but can be modified via connect options
|
|
31
|
+
const CONNECT_TIMEOUT = 3000;
|
|
32
|
+
const RESPONSE_TIMEOUT = 30000;
|
|
33
|
+
/**
|
|
34
|
+
* JSON RPC Socket Client for WebSocket request/response and long-running subscriptions.
|
|
35
|
+
*/
|
|
36
|
+
export class JsonRpcSocket {
|
|
37
|
+
constructor(socket, responseTimeout) {
|
|
38
|
+
this.socket = socket;
|
|
39
|
+
this.responseTimeout = responseTimeout;
|
|
40
|
+
this.messageHandlers = new Map();
|
|
41
|
+
}
|
|
42
|
+
static connect(url_1) {
|
|
43
|
+
return __awaiter(this, arguments, void 0, function* (url, options = {}) {
|
|
44
|
+
const { connectTimeout = CONNECT_TIMEOUT, responseTimeout = RESPONSE_TIMEOUT, onclose, onerror } = options;
|
|
45
|
+
const socket = new WebSocket(url);
|
|
46
|
+
if (!onclose) {
|
|
47
|
+
socket.onclose = () => {
|
|
48
|
+
console.info(`JSON RPC Socket close ${url}`);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
socket.onclose = onclose;
|
|
53
|
+
}
|
|
54
|
+
if (!onerror) {
|
|
55
|
+
socket.onerror = (error) => {
|
|
56
|
+
console.error(`JSON RPC Socket error ${url}`, error);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
socket.onerror = onerror;
|
|
61
|
+
}
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
socket.addEventListener('open', () => {
|
|
64
|
+
const jsonRpcSocket = new JsonRpcSocket(socket, responseTimeout);
|
|
65
|
+
socket.addEventListener('message', (event) => {
|
|
66
|
+
const jsonRpcResponse = parseJson(toText(event.data));
|
|
67
|
+
const handler = jsonRpcSocket.messageHandlers.get(jsonRpcResponse.id);
|
|
68
|
+
if (handler) {
|
|
69
|
+
handler(event);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
resolve(jsonRpcSocket);
|
|
73
|
+
});
|
|
74
|
+
socket.addEventListener('error', (error) => {
|
|
75
|
+
reject(error);
|
|
76
|
+
});
|
|
77
|
+
setTimeout(() => reject(new Error('connect timed out')), connectTimeout);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
close() {
|
|
82
|
+
this.socket.close();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Sends a JSON-RPC request through the socket and waits for a single response.
|
|
86
|
+
*/
|
|
87
|
+
request(request) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
var _a;
|
|
91
|
+
(_a = request.id) !== null && _a !== void 0 ? _a : (request.id = CryptoUtils.randomUuid());
|
|
92
|
+
const handleResponse = (event) => {
|
|
93
|
+
const jsonRpsResponse = parseJson(toText(event.data));
|
|
94
|
+
if (jsonRpsResponse.id === request.id) {
|
|
95
|
+
// if the incoming response id matches the request id, we will remove the listener and resolve the response
|
|
96
|
+
this.messageHandlers.delete(request.id);
|
|
97
|
+
return resolve(jsonRpsResponse);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
// add the listener to the map of message handlers
|
|
101
|
+
this.messageHandlers.set(request.id, handleResponse);
|
|
102
|
+
this.send(request);
|
|
103
|
+
// reject this promise if we don't receive any response back within the timeout period
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
this.messageHandlers.delete(request.id);
|
|
106
|
+
reject(new Error('request timed out'));
|
|
107
|
+
}, this.responseTimeout);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Sends a JSON-RPC request through the socket and keeps a listener open to read associated responses as they arrive.
|
|
113
|
+
* Returns a close method to clean up the listener.
|
|
114
|
+
*/
|
|
115
|
+
subscribe(request, listener) {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
if (!request.method.startsWith('rpc.subscribe.')) {
|
|
118
|
+
throw new Error('subscribe rpc requests must include the `rpc.subscribe` prefix');
|
|
119
|
+
}
|
|
120
|
+
if (!request.subscription) {
|
|
121
|
+
throw new Error('subscribe rpc requests must include subscribe options');
|
|
122
|
+
}
|
|
123
|
+
const subscriptionId = request.subscription.id;
|
|
124
|
+
// Preserve any existing handler for this subscriptionId so that a rejected
|
|
125
|
+
// duplicate-subscribe attempt does not clobber an active subscription.
|
|
126
|
+
const existingHandler = this.messageHandlers.get(subscriptionId);
|
|
127
|
+
const socketEventListener = (event) => {
|
|
128
|
+
const jsonRpcResponse = parseJson(toText(event.data));
|
|
129
|
+
if (jsonRpcResponse.id === subscriptionId) {
|
|
130
|
+
if (jsonRpcResponse.error !== undefined) {
|
|
131
|
+
// remove the event listener upon receipt of a JSON RPC Error.
|
|
132
|
+
this.messageHandlers.delete(subscriptionId);
|
|
133
|
+
this.closeSubscription(subscriptionId).catch(() => {
|
|
134
|
+
// swallow timeout errors; the subscription is already cleaned up locally.
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
listener(jsonRpcResponse);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
this.messageHandlers.set(subscriptionId, socketEventListener);
|
|
141
|
+
const response = yield this.request(request);
|
|
142
|
+
if (response.error) {
|
|
143
|
+
// Restore the previous handler if one existed, otherwise clean up.
|
|
144
|
+
if (existingHandler) {
|
|
145
|
+
this.messageHandlers.set(subscriptionId, existingHandler);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.messageHandlers.delete(subscriptionId);
|
|
149
|
+
}
|
|
150
|
+
return { response };
|
|
151
|
+
}
|
|
152
|
+
// clean up listener and create a `rpc.subscribe.close` message to use when closing this JSON RPC subscription
|
|
153
|
+
const close = () => __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
this.messageHandlers.delete(subscriptionId);
|
|
155
|
+
yield this.closeSubscription(subscriptionId);
|
|
156
|
+
});
|
|
157
|
+
return {
|
|
158
|
+
response,
|
|
159
|
+
close
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
closeSubscription(id) {
|
|
164
|
+
const requestId = CryptoUtils.randomUuid();
|
|
165
|
+
const request = createJsonRpcSubscriptionRequest(requestId, 'rpc.subscribe.close', {}, id);
|
|
166
|
+
return this.request(request);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Sends a JSON-RPC request through the socket. You must subscribe to a message listener separately to capture the response.
|
|
170
|
+
*/
|
|
171
|
+
send(request) {
|
|
172
|
+
this.socket.send(JSON.stringify(request));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=json-rpc-socket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-rpc-socket.js","sourceRoot":"","sources":["../../src/json-rpc-socket.ts"],"names":[],"mappings":"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"}
|