space-data-module-sdk 0.5.22 → 0.6.1
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 +22 -2
- package/package.json +2 -2
- package/schemas/orbpro/BaseTypes.fbs +7 -0
- package/schemas/orbpro/CatalogQueryBase.fbs +8 -0
- package/schemas/orbpro/CatalogQueryRequest.fbs +14 -0
- package/schemas/orbpro/CatalogQueryResult.fbs +17 -0
- package/schemas/orbpro/EntityMetadata.fbs +53 -0
- package/schemas/orbpro/EntityStandardsLink.fbs +18 -0
- package/schemas/orbpro/Plugin.fbs +8 -0
- package/schemas/orbpro/Propagator.fbs +89 -0
- package/schemas/orbpro/StandardsRecordIndex.fbs +15 -0
- package/src/generated/orbpro/entity/entity-kind.js +15 -0
- package/src/generated/orbpro/entity/entity-kind.ts +15 -0
- package/src/generated/orbpro/entity/entity-metadata.js +322 -0
- package/src/generated/orbpro/entity/entity-metadata.ts +426 -0
- package/src/generated/orbpro/entity/entity-standards-link.js +143 -0
- package/src/generated/orbpro/entity/entity-standards-link.ts +183 -0
- package/src/generated/orbpro/entity/standards-record-index.js +115 -0
- package/src/generated/orbpro/entity/standards-record-index.ts +147 -0
- package/src/generated/orbpro/entity.js +10 -0
- package/src/generated/orbpro/entity.ts +5 -0
- package/src/generated/orbpro/plugin/raw-data-payload.js +79 -0
- package/src/generated/orbpro/plugin/raw-data-payload.ts +92 -0
- package/src/generated/orbpro/plugin.js +4 -0
- package/src/generated/orbpro/plugin.ts +5 -0
- package/src/generated/orbpro/propagator/propagator-describe-sources-batch-request.js +79 -0
- package/src/generated/orbpro/propagator/propagator-describe-sources-batch-request.ts +95 -0
- package/src/generated/orbpro/propagator/propagator-describe-sources-batch-result.js +70 -0
- package/src/generated/orbpro/propagator/propagator-describe-sources-batch-result.ts +78 -0
- package/src/generated/orbpro/propagator/propagator-sample-trajectory-states-request.js +97 -0
- package/src/generated/orbpro/propagator/propagator-sample-trajectory-states-request.ts +117 -0
- package/src/generated/orbpro/propagator/propagator-sample-trajectory-states-result.js +140 -0
- package/src/generated/orbpro/propagator/propagator-sample-trajectory-states-result.ts +175 -0
- package/src/generated/orbpro/propagator/propagator-source-description.js +208 -0
- package/src/generated/orbpro/propagator/propagator-source-description.ts +257 -0
- package/src/generated/orbpro/propagator/propagator-source-kind.js +8 -0
- package/src/generated/orbpro/propagator/propagator-source-kind.ts +8 -0
- package/src/generated/orbpro/propagator/reference-frame.js +12 -0
- package/src/generated/orbpro/propagator/reference-frame.ts +12 -0
- package/src/generated/orbpro/propagator/state-flags.js +13 -0
- package/src/generated/orbpro/propagator/state-flags.ts +13 -0
- package/src/generated/orbpro/propagator/state-vector.js +55 -0
- package/src/generated/orbpro/propagator/state-vector.ts +61 -0
- package/src/generated/orbpro/propagator.js +20 -0
- package/src/generated/orbpro/propagator.ts +13 -0
- package/src/generated/orbpro/query/catalog-query-kind.js +10 -0
- package/src/generated/orbpro/query/catalog-query-kind.ts +10 -0
- package/src/generated/orbpro/query/catalog-query-request.js +89 -0
- package/src/generated/orbpro/query/catalog-query-request.ts +105 -0
- package/src/generated/orbpro/query/catalog-query-result.js +145 -0
- package/src/generated/orbpro/query/catalog-query-result.ts +179 -0
- package/src/generated/orbpro/query.js +8 -0
- package/src/generated/orbpro/query.ts +4 -0
- package/src/generated/orbpro/vec3.js +39 -0
- package/src/generated/orbpro/vec3.ts +40 -0
- package/src/host/abi.js +189 -3
- package/src/host/browserEdgeShims.js +17 -0
- package/src/host/browserHost.js +486 -40
- package/src/host/isomorphicLoader.js +44 -20
- package/src/host/nodeHost.js +337 -63
- package/src/index.d.ts +99 -3
- package/src/runtime-host/index.js +90 -0
- package/src/testing/browserModuleHarness.js +22 -3
- package/src/testing/index.d.ts +1 -0
- package/src/testing/index.js +39 -1
package/src/host/browserHost.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { RuntimeTarget } from "../runtime/constants.js";
|
|
9
|
+
import { toUint8Array } from "../utils/encoding.js";
|
|
9
10
|
import {
|
|
10
11
|
parseCronExpression,
|
|
11
12
|
matchesCronExpression,
|
|
@@ -20,12 +21,21 @@ export const BrowserHostSupportedCapabilities = Object.freeze([
|
|
|
20
21
|
"schedule_cron",
|
|
21
22
|
"http",
|
|
22
23
|
"websocket",
|
|
24
|
+
"network",
|
|
23
25
|
"filesystem",
|
|
26
|
+
"ipfs",
|
|
27
|
+
"protocol_handle",
|
|
28
|
+
"protocol_dial",
|
|
24
29
|
"context_read",
|
|
25
30
|
"context_write",
|
|
26
31
|
"crypto_hash",
|
|
32
|
+
"crypto_sign",
|
|
33
|
+
"crypto_verify",
|
|
27
34
|
"crypto_encrypt",
|
|
28
35
|
"crypto_decrypt",
|
|
36
|
+
"crypto_key_agreement",
|
|
37
|
+
"crypto_kdf",
|
|
38
|
+
"wallet_sign",
|
|
29
39
|
"logging",
|
|
30
40
|
]);
|
|
31
41
|
|
|
@@ -39,11 +49,13 @@ export const BrowserHostSupportedOperations = Object.freeze([
|
|
|
39
49
|
"clock.monotonicNow",
|
|
40
50
|
"clock.nowIso",
|
|
41
51
|
"random.bytes",
|
|
52
|
+
"timers.delay",
|
|
42
53
|
"schedule.parse",
|
|
43
54
|
"schedule.matches",
|
|
44
55
|
"schedule.next",
|
|
45
56
|
"http.request",
|
|
46
57
|
"websocket.exchange",
|
|
58
|
+
"network.request",
|
|
47
59
|
"filesystem.resolvePath",
|
|
48
60
|
"filesystem.readFile",
|
|
49
61
|
"filesystem.writeFile",
|
|
@@ -53,6 +65,14 @@ export const BrowserHostSupportedOperations = Object.freeze([
|
|
|
53
65
|
"filesystem.readdir",
|
|
54
66
|
"filesystem.stat",
|
|
55
67
|
"filesystem.rename",
|
|
68
|
+
"ipfs.invoke",
|
|
69
|
+
"ipfs.add",
|
|
70
|
+
"ipfs.cat",
|
|
71
|
+
"protocol_handle.register",
|
|
72
|
+
"protocol_handle.unregister",
|
|
73
|
+
"protocol_dial.dial",
|
|
74
|
+
"protocol.request",
|
|
75
|
+
"keyslot.get",
|
|
56
76
|
"context.get",
|
|
57
77
|
"context.set",
|
|
58
78
|
"context.delete",
|
|
@@ -60,8 +80,15 @@ export const BrowserHostSupportedOperations = Object.freeze([
|
|
|
60
80
|
"context.listScopes",
|
|
61
81
|
"crypto.sha256",
|
|
62
82
|
"crypto.sha512",
|
|
83
|
+
"crypto.hkdf",
|
|
63
84
|
"crypto.aesGcmEncrypt",
|
|
64
85
|
"crypto.aesGcmDecrypt",
|
|
86
|
+
"crypto.x25519.generateKeypair",
|
|
87
|
+
"crypto.x25519.publicKey",
|
|
88
|
+
"crypto.x25519.sharedSecret",
|
|
89
|
+
"crypto.ed25519.publicKeyFromSeed",
|
|
90
|
+
"crypto.ed25519.sign",
|
|
91
|
+
"crypto.ed25519.verify",
|
|
65
92
|
]);
|
|
66
93
|
|
|
67
94
|
export class BrowserHostCapabilityError extends Error {
|
|
@@ -73,9 +100,85 @@ export class BrowserHostCapabilityError extends Error {
|
|
|
73
100
|
}
|
|
74
101
|
}
|
|
75
102
|
|
|
103
|
+
function normalizeBrowserNetworkTransport(params = {}) {
|
|
104
|
+
const explicit = String(
|
|
105
|
+
params.transport ?? params.kind ?? params.request?.transport ?? "",
|
|
106
|
+
)
|
|
107
|
+
.trim()
|
|
108
|
+
.toLowerCase();
|
|
109
|
+
if (explicit) {
|
|
110
|
+
return explicit;
|
|
111
|
+
}
|
|
112
|
+
const candidateUrl = params.url ?? params.request?.url ?? null;
|
|
113
|
+
if (candidateUrl) {
|
|
114
|
+
const protocol = new URL(candidateUrl, "https://browser-host.invalid")
|
|
115
|
+
.protocol.toLowerCase();
|
|
116
|
+
if (protocol === "http:" || protocol === "https:") {
|
|
117
|
+
return "http";
|
|
118
|
+
}
|
|
119
|
+
if (protocol === "ws:" || protocol === "wss:") {
|
|
120
|
+
return "websocket";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
throw new Error(
|
|
124
|
+
'network.request requires a transport value such as "http" or "websocket".',
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveCapabilityAdapters(options = {}) {
|
|
129
|
+
const adapters =
|
|
130
|
+
options.capabilityAdapters && typeof options.capabilityAdapters === "object"
|
|
131
|
+
? options.capabilityAdapters
|
|
132
|
+
: {};
|
|
133
|
+
return {
|
|
134
|
+
filesystem: options.filesystem ?? adapters.filesystem ?? null,
|
|
135
|
+
network: options.network ?? adapters.network ?? null,
|
|
136
|
+
ipfs: options.ipfs ?? adapters.ipfs ?? null,
|
|
137
|
+
walletSign:
|
|
138
|
+
options.walletSign ??
|
|
139
|
+
adapters.walletSign ??
|
|
140
|
+
adapters.wallet_sign ??
|
|
141
|
+
adapters.keyslot ??
|
|
142
|
+
null,
|
|
143
|
+
protocolHandle:
|
|
144
|
+
options.protocolHandle ??
|
|
145
|
+
adapters.protocolHandle ??
|
|
146
|
+
adapters.protocol_handle ??
|
|
147
|
+
null,
|
|
148
|
+
protocolDial:
|
|
149
|
+
options.protocolDial ??
|
|
150
|
+
adapters.protocolDial ??
|
|
151
|
+
adapters.protocol_dial ??
|
|
152
|
+
null,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function normalizeCryptoBytes(value) {
|
|
157
|
+
if (typeof value === "string") {
|
|
158
|
+
return new TextEncoder().encode(value);
|
|
159
|
+
}
|
|
160
|
+
return toUint8Array(value);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function invokeAdapterMethod(adapter, methodName, params, label) {
|
|
164
|
+
if (!adapter || typeof adapter !== "object") {
|
|
165
|
+
throw new Error(`${label} adapter is not configured for this host.`);
|
|
166
|
+
}
|
|
167
|
+
if (typeof adapter[methodName] === "function") {
|
|
168
|
+
return adapter[methodName](params);
|
|
169
|
+
}
|
|
170
|
+
if (typeof adapter.invoke === "function") {
|
|
171
|
+
return adapter.invoke(methodName, params);
|
|
172
|
+
}
|
|
173
|
+
throw new Error(
|
|
174
|
+
`${label} adapter does not implement "${methodName}" or invoke().`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
76
178
|
export class BrowserHost {
|
|
77
179
|
constructor(options = {}) {
|
|
78
180
|
this.runtimeTarget = RuntimeTarget.BROWSER;
|
|
181
|
+
const capabilityAdapters = resolveCapabilityAdapters(options);
|
|
79
182
|
|
|
80
183
|
const granted = options.capabilities
|
|
81
184
|
? new Set(options.capabilities)
|
|
@@ -86,7 +189,15 @@ export class BrowserHost {
|
|
|
86
189
|
WebSocket: options.WebSocket ?? options.edgeShims?.WebSocket,
|
|
87
190
|
crypto: options.crypto ?? options.edgeShims?.crypto,
|
|
88
191
|
performance: options.performance ?? options.edgeShims?.performance,
|
|
89
|
-
|
|
192
|
+
capabilityAdapters,
|
|
193
|
+
network: capabilityAdapters.network ?? options.edgeShims?.network,
|
|
194
|
+
ipfs: capabilityAdapters.ipfs ?? options.edgeShims?.ipfs,
|
|
195
|
+
protocolHandle:
|
|
196
|
+
capabilityAdapters.protocolHandle ?? options.edgeShims?.protocolHandle,
|
|
197
|
+
protocolDial:
|
|
198
|
+
capabilityAdapters.protocolDial ?? options.edgeShims?.protocolDial,
|
|
199
|
+
filesystem:
|
|
200
|
+
capabilityAdapters.filesystem ?? options.edgeShims?.filesystem,
|
|
90
201
|
filesystemRoot: options.filesystemRoot ?? options.edgeShims?.filesystemRoot,
|
|
91
202
|
});
|
|
92
203
|
const performanceApi = edgeShims.performance ?? {
|
|
@@ -94,9 +205,15 @@ export class BrowserHost {
|
|
|
94
205
|
timeOrigin: 0,
|
|
95
206
|
};
|
|
96
207
|
const cryptoApi = edgeShims.crypto;
|
|
208
|
+
const wasmWallet = options.wasmWallet ?? null;
|
|
97
209
|
const fetchImpl = edgeShims.fetch;
|
|
98
210
|
const WebSocketImpl = edgeShims.WebSocket;
|
|
99
211
|
const filesystem = edgeShims.filesystem;
|
|
212
|
+
const networkAdapter = edgeShims.network;
|
|
213
|
+
const ipfsAdapter = edgeShims.ipfs;
|
|
214
|
+
const walletSignAdapter = capabilityAdapters.walletSign;
|
|
215
|
+
const protocolHandleAdapter = edgeShims.protocolHandle;
|
|
216
|
+
const protocolDialAdapter = edgeShims.protocolDial;
|
|
100
217
|
|
|
101
218
|
this._grantedCapabilities = granted;
|
|
102
219
|
this._contextStore = options.contextStore ?? new Map();
|
|
@@ -239,6 +356,97 @@ export class BrowserHost {
|
|
|
239
356
|
},
|
|
240
357
|
});
|
|
241
358
|
|
|
359
|
+
this.network = Object.freeze({
|
|
360
|
+
request: async (params = {}) => {
|
|
361
|
+
this.#assertCapability("network", "network.request");
|
|
362
|
+
const transport = normalizeBrowserNetworkTransport(params);
|
|
363
|
+
const request = params.request ?? params;
|
|
364
|
+
if (networkAdapter) {
|
|
365
|
+
return invokeAdapterMethod(networkAdapter, "request", {
|
|
366
|
+
...request,
|
|
367
|
+
transport,
|
|
368
|
+
}, "network");
|
|
369
|
+
}
|
|
370
|
+
if (transport === "http") {
|
|
371
|
+
return this.http.request(request);
|
|
372
|
+
}
|
|
373
|
+
if (transport === "websocket") {
|
|
374
|
+
return this.websocket.exchange(request);
|
|
375
|
+
}
|
|
376
|
+
throw new Error(
|
|
377
|
+
`Browser host does not support network transport "${transport}".`,
|
|
378
|
+
);
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
this.ipfs = Object.freeze({
|
|
383
|
+
invoke: async (params = {}) => {
|
|
384
|
+
this.#assertCapability("ipfs", "ipfs.invoke");
|
|
385
|
+
const operation = String(params.operation ?? "invoke").trim();
|
|
386
|
+
if (!operation) {
|
|
387
|
+
throw new Error("ipfs.invoke requires a non-empty operation.");
|
|
388
|
+
}
|
|
389
|
+
return invokeAdapterMethod(ipfsAdapter, operation, params, "ipfs");
|
|
390
|
+
},
|
|
391
|
+
add: async (params = {}) => {
|
|
392
|
+
this.#assertCapability("ipfs", "ipfs.add");
|
|
393
|
+
return invokeAdapterMethod(ipfsAdapter, "add", params, "ipfs");
|
|
394
|
+
},
|
|
395
|
+
cat: async (params = {}) => {
|
|
396
|
+
this.#assertCapability("ipfs", "ipfs.cat");
|
|
397
|
+
return invokeAdapterMethod(ipfsAdapter, "cat", params, "ipfs");
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
this.protocolHandle = Object.freeze({
|
|
402
|
+
register: async (params = {}) => {
|
|
403
|
+
this.#assertCapability("protocol_handle", "protocol_handle.register");
|
|
404
|
+
return invokeAdapterMethod(
|
|
405
|
+
protocolHandleAdapter,
|
|
406
|
+
"register",
|
|
407
|
+
params,
|
|
408
|
+
"protocol_handle",
|
|
409
|
+
);
|
|
410
|
+
},
|
|
411
|
+
unregister: async (params = {}) => {
|
|
412
|
+
this.#assertCapability("protocol_handle", "protocol_handle.unregister");
|
|
413
|
+
return invokeAdapterMethod(
|
|
414
|
+
protocolHandleAdapter,
|
|
415
|
+
"unregister",
|
|
416
|
+
params,
|
|
417
|
+
"protocol_handle",
|
|
418
|
+
);
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
this.protocolDial = Object.freeze({
|
|
423
|
+
dial: async (params = {}) => {
|
|
424
|
+
this.#assertCapability("protocol_dial", "protocol_dial.dial");
|
|
425
|
+
return invokeAdapterMethod(
|
|
426
|
+
protocolDialAdapter,
|
|
427
|
+
"dial",
|
|
428
|
+
params,
|
|
429
|
+
"protocol_dial",
|
|
430
|
+
);
|
|
431
|
+
},
|
|
432
|
+
request: async (params = {}) => {
|
|
433
|
+
this.#assertCapability("protocol_dial", "protocol.request");
|
|
434
|
+
return invokeAdapterMethod(
|
|
435
|
+
protocolDialAdapter,
|
|
436
|
+
"request",
|
|
437
|
+
params,
|
|
438
|
+
"protocol_dial",
|
|
439
|
+
);
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
this.keyslot = Object.freeze({
|
|
444
|
+
get: async (params = {}) => {
|
|
445
|
+
this.#assertCapability("wallet_sign", "keyslot.get");
|
|
446
|
+
return invokeAdapterMethod(walletSignAdapter, "get", params, "wallet_sign");
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
|
|
242
450
|
this.context = Object.freeze({
|
|
243
451
|
get: (scope, key) => {
|
|
244
452
|
this.#assertCapability("context_read", "context.get");
|
|
@@ -267,60 +475,161 @@ export class BrowserHost {
|
|
|
267
475
|
});
|
|
268
476
|
|
|
269
477
|
this.crypto = Object.freeze({
|
|
270
|
-
sha256:
|
|
478
|
+
sha256: (data) => {
|
|
271
479
|
this.#assertCapability("crypto_hash", "crypto.sha256");
|
|
272
|
-
if (!
|
|
273
|
-
throw new Error("
|
|
480
|
+
if (!wasmWallet?.utils?.sha256) {
|
|
481
|
+
throw new Error("Browser host crypto.sha256 requires a preloaded wasmWallet.");
|
|
274
482
|
}
|
|
275
|
-
|
|
276
|
-
return new Uint8Array(await cryptoApi.subtle.digest("SHA-256", bytes));
|
|
483
|
+
return new Uint8Array(wasmWallet.utils.sha256(normalizeCryptoBytes(data)));
|
|
277
484
|
},
|
|
278
|
-
sha512:
|
|
485
|
+
sha512: (data) => {
|
|
279
486
|
this.#assertCapability("crypto_hash", "crypto.sha512");
|
|
280
|
-
if (!
|
|
281
|
-
throw new Error("
|
|
487
|
+
if (!wasmWallet?.utils?.sha512) {
|
|
488
|
+
throw new Error("Browser host crypto.sha512 requires a preloaded wasmWallet.");
|
|
282
489
|
}
|
|
283
|
-
|
|
284
|
-
return new Uint8Array(await cryptoApi.subtle.digest("SHA-512", bytes));
|
|
490
|
+
return new Uint8Array(wasmWallet.utils.sha512(normalizeCryptoBytes(data)));
|
|
285
491
|
},
|
|
286
|
-
|
|
287
|
-
this.#assertCapability("
|
|
288
|
-
if (!
|
|
289
|
-
throw new Error("
|
|
492
|
+
hkdf: (params = {}) => {
|
|
493
|
+
this.#assertCapability("crypto_kdf", "crypto.hkdf");
|
|
494
|
+
if (!wasmWallet?.utils?.hkdf) {
|
|
495
|
+
throw new Error("Browser host crypto.hkdf requires a preloaded wasmWallet.");
|
|
290
496
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
497
|
+
return new Uint8Array(
|
|
498
|
+
wasmWallet.utils.hkdf(
|
|
499
|
+
toUint8Array(params.ikm),
|
|
500
|
+
toUint8Array(params.salt),
|
|
501
|
+
params.info === undefined || params.info === null
|
|
502
|
+
? new Uint8Array()
|
|
503
|
+
: toUint8Array(params.info),
|
|
504
|
+
Number(params.length ?? 0),
|
|
505
|
+
),
|
|
297
506
|
);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
507
|
+
},
|
|
508
|
+
aesGcmEncrypt: (params = {}) => {
|
|
509
|
+
this.#assertCapability("crypto_encrypt", "crypto.aesGcmEncrypt");
|
|
510
|
+
if (!wasmWallet?.utils?.aesGcm?.encrypt) {
|
|
511
|
+
throw new Error(
|
|
512
|
+
"Browser host crypto.aesGcmEncrypt requires a preloaded wasmWallet.",
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
const iv =
|
|
516
|
+
params.iv === undefined || params.iv === null
|
|
517
|
+
? new Uint8Array(wasmWallet.utils.getRandomBytes(12))
|
|
518
|
+
: toUint8Array(params.iv);
|
|
519
|
+
const result = wasmWallet.utils.aesGcm.encrypt(
|
|
520
|
+
toUint8Array(params.key),
|
|
521
|
+
toUint8Array(params.plaintext),
|
|
522
|
+
iv,
|
|
523
|
+
params.aad === undefined || params.aad === null
|
|
524
|
+
? undefined
|
|
525
|
+
: toUint8Array(params.aad),
|
|
301
526
|
);
|
|
302
|
-
return {
|
|
527
|
+
return {
|
|
528
|
+
ciphertext: new Uint8Array(result.ciphertext),
|
|
529
|
+
tag: new Uint8Array(result.tag),
|
|
530
|
+
iv,
|
|
531
|
+
};
|
|
303
532
|
},
|
|
304
|
-
aesGcmDecrypt:
|
|
533
|
+
aesGcmDecrypt: (params = {}) => {
|
|
305
534
|
this.#assertCapability("crypto_decrypt", "crypto.aesGcmDecrypt");
|
|
306
|
-
if (!
|
|
307
|
-
throw new Error(
|
|
535
|
+
if (!wasmWallet?.utils?.aesGcm?.decrypt) {
|
|
536
|
+
throw new Error(
|
|
537
|
+
"Browser host crypto.aesGcmDecrypt requires a preloaded wasmWallet.",
|
|
538
|
+
);
|
|
308
539
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
540
|
+
return new Uint8Array(
|
|
541
|
+
wasmWallet.utils.aesGcm.decrypt(
|
|
542
|
+
toUint8Array(params.key),
|
|
543
|
+
toUint8Array(params.ciphertext),
|
|
544
|
+
toUint8Array(params.tag),
|
|
545
|
+
toUint8Array(params.iv),
|
|
546
|
+
params.aad === undefined || params.aad === null
|
|
547
|
+
? undefined
|
|
548
|
+
: toUint8Array(params.aad),
|
|
549
|
+
),
|
|
550
|
+
);
|
|
551
|
+
},
|
|
552
|
+
generateX25519Keypair: () => {
|
|
553
|
+
this.#assertCapability(
|
|
554
|
+
"crypto_key_agreement",
|
|
555
|
+
"crypto.x25519.generateKeypair",
|
|
315
556
|
);
|
|
557
|
+
if (!wasmWallet?.curves?.x25519 || !wasmWallet?.utils?.getRandomBytes) {
|
|
558
|
+
throw new Error(
|
|
559
|
+
"Browser host crypto.x25519.generateKeypair requires a preloaded wasmWallet.",
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
const privateKey = new Uint8Array(wasmWallet.utils.getRandomBytes(32));
|
|
563
|
+
const publicKey = new Uint8Array(
|
|
564
|
+
wasmWallet.curves.x25519.publicKey(privateKey),
|
|
565
|
+
);
|
|
566
|
+
return { privateKey, publicKey };
|
|
567
|
+
},
|
|
568
|
+
x25519PublicKey: (privateKey) => {
|
|
569
|
+
this.#assertCapability("crypto_key_agreement", "crypto.x25519.publicKey");
|
|
570
|
+
if (!wasmWallet?.curves?.x25519?.publicKey) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
"Browser host crypto.x25519.publicKey requires a preloaded wasmWallet.",
|
|
573
|
+
);
|
|
574
|
+
}
|
|
316
575
|
return new Uint8Array(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
576
|
+
wasmWallet.curves.x25519.publicKey(toUint8Array(privateKey)),
|
|
577
|
+
);
|
|
578
|
+
},
|
|
579
|
+
x25519SharedSecret: (privateKey, publicKey) => {
|
|
580
|
+
this.#assertCapability("crypto_key_agreement", "crypto.x25519.sharedSecret");
|
|
581
|
+
if (!wasmWallet?.curves?.x25519?.ecdh) {
|
|
582
|
+
throw new Error(
|
|
583
|
+
"Browser host crypto.x25519.sharedSecret requires a preloaded wasmWallet.",
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
return new Uint8Array(
|
|
587
|
+
wasmWallet.curves.x25519.ecdh(
|
|
588
|
+
toUint8Array(privateKey),
|
|
589
|
+
toUint8Array(publicKey),
|
|
321
590
|
),
|
|
322
591
|
);
|
|
323
592
|
},
|
|
593
|
+
ed25519: Object.freeze({
|
|
594
|
+
publicKeyFromSeed: (seed) => {
|
|
595
|
+
this.#assertCapability("crypto_sign", "crypto.ed25519.publicKeyFromSeed");
|
|
596
|
+
if (!wasmWallet?.curves?.ed25519?.publicKeyFromSeed) {
|
|
597
|
+
throw new Error(
|
|
598
|
+
"Browser host crypto.ed25519.publicKeyFromSeed requires a preloaded wasmWallet.",
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
return new Uint8Array(
|
|
602
|
+
wasmWallet.curves.ed25519.publicKeyFromSeed(toUint8Array(seed)),
|
|
603
|
+
);
|
|
604
|
+
},
|
|
605
|
+
sign: (message, seed) => {
|
|
606
|
+
this.#assertCapability("crypto_sign", "crypto.ed25519.sign");
|
|
607
|
+
if (!wasmWallet?.curves?.ed25519?.sign) {
|
|
608
|
+
throw new Error(
|
|
609
|
+
"Browser host crypto.ed25519.sign requires a preloaded wasmWallet.",
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
return new Uint8Array(
|
|
613
|
+
wasmWallet.curves.ed25519.sign(
|
|
614
|
+
toUint8Array(message),
|
|
615
|
+
toUint8Array(seed),
|
|
616
|
+
),
|
|
617
|
+
);
|
|
618
|
+
},
|
|
619
|
+
verify: (message, signature, publicKey) => {
|
|
620
|
+
this.#assertCapability("crypto_verify", "crypto.ed25519.verify");
|
|
621
|
+
if (!wasmWallet?.curves?.ed25519?.verify) {
|
|
622
|
+
throw new Error(
|
|
623
|
+
"Browser host crypto.ed25519.verify requires a preloaded wasmWallet.",
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
return wasmWallet.curves.ed25519.verify(
|
|
627
|
+
toUint8Array(message),
|
|
628
|
+
toUint8Array(signature),
|
|
629
|
+
toUint8Array(publicKey),
|
|
630
|
+
);
|
|
631
|
+
},
|
|
632
|
+
}),
|
|
324
633
|
});
|
|
325
634
|
|
|
326
635
|
this.filesystem = Object.freeze({
|
|
@@ -378,17 +687,154 @@ export class BrowserHost {
|
|
|
378
687
|
}
|
|
379
688
|
|
|
380
689
|
hasCapability(capability) {
|
|
381
|
-
|
|
690
|
+
const normalized = String(capability ?? "").trim();
|
|
691
|
+
return (
|
|
692
|
+
this._grantedCapabilities.has(normalized) ||
|
|
693
|
+
(this._grantedCapabilities.has("network") &&
|
|
694
|
+
["http", "websocket"].includes(normalized))
|
|
695
|
+
);
|
|
382
696
|
}
|
|
383
697
|
|
|
384
698
|
assertCapability(capability, operation) {
|
|
385
699
|
this.#assertCapability(capability, operation);
|
|
386
700
|
}
|
|
387
701
|
|
|
702
|
+
async invoke(operation, params = {}) {
|
|
703
|
+
const normalized = String(operation ?? "").trim();
|
|
704
|
+
switch (normalized) {
|
|
705
|
+
case "host.runtimeTarget":
|
|
706
|
+
return this.runtimeTarget;
|
|
707
|
+
case "host.listCapabilities":
|
|
708
|
+
return this.listCapabilities();
|
|
709
|
+
case "host.listSupportedCapabilities":
|
|
710
|
+
return this.listSupportedCapabilities();
|
|
711
|
+
case "host.listOperations":
|
|
712
|
+
return this.listOperations();
|
|
713
|
+
case "host.hasCapability":
|
|
714
|
+
return this.hasCapability(params.capability);
|
|
715
|
+
case "clock.now":
|
|
716
|
+
return this.clock.now();
|
|
717
|
+
case "clock.monotonicNow":
|
|
718
|
+
return this.clock.monotonicNow();
|
|
719
|
+
case "clock.nowIso":
|
|
720
|
+
return this.clock.nowIso();
|
|
721
|
+
case "random.bytes":
|
|
722
|
+
return this.random.bytes(params.length);
|
|
723
|
+
case "timers.delay":
|
|
724
|
+
return this.timers.delay(params.ms ?? params.delayMs ?? 0);
|
|
725
|
+
case "schedule.parse":
|
|
726
|
+
return this.schedule.parse(params.expression);
|
|
727
|
+
case "schedule.matches":
|
|
728
|
+
return this.schedule.matches(params.expression, params.date);
|
|
729
|
+
case "schedule.next":
|
|
730
|
+
return this.schedule.next(params.expression, params.from);
|
|
731
|
+
case "http.request":
|
|
732
|
+
return this.http.request(params);
|
|
733
|
+
case "websocket.exchange":
|
|
734
|
+
return this.websocket.exchange(params);
|
|
735
|
+
case "network.request":
|
|
736
|
+
return this.network.request(params);
|
|
737
|
+
case "filesystem.resolvePath":
|
|
738
|
+
return this.filesystem.resolvePath(params.path);
|
|
739
|
+
case "filesystem.readFile":
|
|
740
|
+
return this.filesystem.readFile(params.path, {
|
|
741
|
+
encoding: params.encoding,
|
|
742
|
+
});
|
|
743
|
+
case "filesystem.writeFile":
|
|
744
|
+
return this.filesystem.writeFile(params.path, params.value, {
|
|
745
|
+
encoding: params.encoding,
|
|
746
|
+
});
|
|
747
|
+
case "filesystem.appendFile":
|
|
748
|
+
return this.filesystem.appendFile(params.path, params.value, {
|
|
749
|
+
encoding: params.encoding,
|
|
750
|
+
});
|
|
751
|
+
case "filesystem.deleteFile":
|
|
752
|
+
return this.filesystem.deleteFile(params.path);
|
|
753
|
+
case "filesystem.mkdir":
|
|
754
|
+
return this.filesystem.mkdir(params.path, {
|
|
755
|
+
recursive: params.recursive,
|
|
756
|
+
});
|
|
757
|
+
case "filesystem.readdir":
|
|
758
|
+
return this.filesystem.readdir(params.path);
|
|
759
|
+
case "filesystem.stat":
|
|
760
|
+
return this.filesystem.stat(params.path);
|
|
761
|
+
case "filesystem.rename":
|
|
762
|
+
return this.filesystem.rename(params.fromPath, params.toPath);
|
|
763
|
+
case "ipfs.invoke":
|
|
764
|
+
return this.ipfs.invoke(params);
|
|
765
|
+
case "ipfs.add":
|
|
766
|
+
return this.ipfs.add(params);
|
|
767
|
+
case "ipfs.cat":
|
|
768
|
+
return this.ipfs.cat(params);
|
|
769
|
+
case "protocol_handle.register":
|
|
770
|
+
return this.protocolHandle.register(params);
|
|
771
|
+
case "protocol_handle.unregister":
|
|
772
|
+
return this.protocolHandle.unregister(params);
|
|
773
|
+
case "protocol_dial.dial":
|
|
774
|
+
return this.protocolDial.dial(params);
|
|
775
|
+
case "protocol.request":
|
|
776
|
+
return this.protocolDial.request(params);
|
|
777
|
+
case "keyslot.get":
|
|
778
|
+
return this.keyslot.get(params);
|
|
779
|
+
case "context.get":
|
|
780
|
+
return this.context.get(params.scope, params.key);
|
|
781
|
+
case "context.set":
|
|
782
|
+
return this.context.set(params.scope, params.key, params.value);
|
|
783
|
+
case "context.delete":
|
|
784
|
+
return this.context.delete(params.scope, params.key);
|
|
785
|
+
case "context.listKeys":
|
|
786
|
+
return this.context.listKeys(params.scope);
|
|
787
|
+
case "context.listScopes":
|
|
788
|
+
return this.context.listScopes();
|
|
789
|
+
case "crypto.sha256":
|
|
790
|
+
return this.crypto.sha256(params.value ?? params.bytes);
|
|
791
|
+
case "crypto.sha512":
|
|
792
|
+
return this.crypto.sha512(params.value ?? params.bytes);
|
|
793
|
+
case "crypto.hkdf":
|
|
794
|
+
return this.crypto.hkdf(params);
|
|
795
|
+
case "crypto.aesGcmEncrypt":
|
|
796
|
+
return this.crypto.aesGcmEncrypt(params);
|
|
797
|
+
case "crypto.aesGcmDecrypt":
|
|
798
|
+
return this.crypto.aesGcmDecrypt(params);
|
|
799
|
+
case "crypto.x25519.generateKeypair":
|
|
800
|
+
return this.crypto.generateX25519Keypair();
|
|
801
|
+
case "crypto.x25519.publicKey":
|
|
802
|
+
return this.crypto.x25519PublicKey(params.privateKey);
|
|
803
|
+
case "crypto.x25519.sharedSecret":
|
|
804
|
+
return this.crypto.x25519SharedSecret(
|
|
805
|
+
params.privateKey,
|
|
806
|
+
params.publicKey,
|
|
807
|
+
);
|
|
808
|
+
case "crypto.ed25519.publicKeyFromSeed":
|
|
809
|
+
return this.crypto.ed25519.publicKeyFromSeed(params.seed);
|
|
810
|
+
case "crypto.ed25519.sign":
|
|
811
|
+
return this.crypto.ed25519.sign(params.message, params.seed);
|
|
812
|
+
case "crypto.ed25519.verify":
|
|
813
|
+
return this.crypto.ed25519.verify(
|
|
814
|
+
params.message,
|
|
815
|
+
params.signature,
|
|
816
|
+
params.publicKey,
|
|
817
|
+
);
|
|
818
|
+
default:
|
|
819
|
+
throw new Error(`Unknown browser host operation "${normalized}".`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
388
823
|
// --- Private ---
|
|
389
824
|
|
|
390
825
|
#assertCapability(capability, operation) {
|
|
391
|
-
|
|
826
|
+
const normalized = String(capability ?? "").trim();
|
|
827
|
+
const networkBackedCapabilities = new Set([
|
|
828
|
+
"http",
|
|
829
|
+
"websocket",
|
|
830
|
+
]);
|
|
831
|
+
if (
|
|
832
|
+
!this._grantedCapabilities.has(normalized) &&
|
|
833
|
+
!(
|
|
834
|
+
this._grantedCapabilities.has("network") &&
|
|
835
|
+
networkBackedCapabilities.has(normalized)
|
|
836
|
+
)
|
|
837
|
+
) {
|
|
392
838
|
throw new BrowserHostCapabilityError(capability, operation);
|
|
393
839
|
}
|
|
394
840
|
}
|