@tinycloud/sdk-core 2.2.0-beta.0 → 2.2.0-beta.3
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/dist/index.cjs +388 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -5
- package/dist/index.d.ts +87 -5
- package/dist/index.js +375 -18
- package/dist/index.js.map +1 -1
- package/package.json +7 -1
package/dist/index.cjs
CHANGED
|
@@ -36,6 +36,7 @@ __export(index_exports, {
|
|
|
36
36
|
CapabilityKeyRegistry: () => CapabilityKeyRegistry,
|
|
37
37
|
CapabilityKeyRegistryErrorCodes: () => CapabilityKeyRegistryErrorCodes,
|
|
38
38
|
ClientSessionSchema: () => ClientSessionSchema,
|
|
39
|
+
CloudLocationResolutionError: () => CloudLocationResolutionError,
|
|
39
40
|
DEFAULT_DEFAULTS: () => DEFAULT_DEFAULTS,
|
|
40
41
|
DEFAULT_EXPIRY: () => DEFAULT_EXPIRY,
|
|
41
42
|
DEFAULT_MANIFEST_SPACE: () => DEFAULT_MANIFEST_SPACE,
|
|
@@ -51,6 +52,7 @@ __export(index_exports, {
|
|
|
51
52
|
ErrorCodes: () => import_sdk_services4.ErrorCodes,
|
|
52
53
|
HooksService: () => import_sdk_services4.HooksService,
|
|
53
54
|
KVService: () => import_sdk_services4.KVService,
|
|
55
|
+
LocationRecordValidationError: () => LocationRecordValidationError,
|
|
54
56
|
ManifestValidationError: () => ManifestValidationError,
|
|
55
57
|
PermissionNotInManifestError: () => PermissionNotInManifestError,
|
|
56
58
|
PrefixedKVService: () => import_sdk_services4.PrefixedKVService,
|
|
@@ -76,6 +78,7 @@ __export(index_exports, {
|
|
|
76
78
|
activateSessionWithHost: () => activateSessionWithHost,
|
|
77
79
|
applyPrefix: () => applyPrefix,
|
|
78
80
|
buildSpaceUri: () => buildSpaceUri,
|
|
81
|
+
canonicalLocationPayload: () => canonicalLocationPayload,
|
|
79
82
|
checkNodeInfo: () => checkNodeInfo,
|
|
80
83
|
composeManifestRequest: () => composeManifestRequest,
|
|
81
84
|
createCapabilityKeyRegistry: () => createCapabilityKeyRegistry,
|
|
@@ -87,24 +90,33 @@ __export(index_exports, {
|
|
|
87
90
|
defaultSpaceCreationHandler: () => defaultSpaceCreationHandler,
|
|
88
91
|
err: () => import_sdk_services4.err,
|
|
89
92
|
expandActionShortNames: () => expandActionShortNames,
|
|
93
|
+
fetchLocationRecord: () => fetchLocationRecord,
|
|
90
94
|
fetchPeerId: () => fetchPeerId,
|
|
95
|
+
httpUrlToMultiaddr: () => httpUrlToMultiaddr,
|
|
91
96
|
isCapabilitySubset: () => isCapabilitySubset,
|
|
92
97
|
loadManifest: () => loadManifest,
|
|
98
|
+
locationPayloadForRecord: () => locationPayloadForRecord,
|
|
93
99
|
makePublicSpaceId: () => makePublicSpaceId,
|
|
94
100
|
manifestAbilitiesUnion: () => manifestAbilitiesUnion,
|
|
101
|
+
multiaddrToHttpUrl: () => multiaddrToHttpUrl,
|
|
95
102
|
normalizeDefaults: () => normalizeDefaults,
|
|
96
103
|
ok: () => import_sdk_services4.ok,
|
|
97
104
|
parseExpiry: () => parseExpiry,
|
|
98
105
|
parseRecapCapabilities: () => parseRecapCapabilities,
|
|
99
106
|
parseSpaceUri: () => parseSpaceUri,
|
|
107
|
+
resolveCloudLocation: () => resolveCloudLocation,
|
|
100
108
|
resolveManifest: () => resolveManifest,
|
|
101
109
|
resourceCapabilitiesToAbilitiesMap: () => resourceCapabilitiesToAbilitiesMap,
|
|
102
110
|
resourceCapabilitiesToSpaceAbilitiesMap: () => resourceCapabilitiesToSpaceAbilitiesMap,
|
|
103
111
|
serviceError: () => import_sdk_services4.serviceError,
|
|
112
|
+
signLocationRecord: () => signLocationRecord,
|
|
104
113
|
submitHostDelegation: () => submitHostDelegation,
|
|
105
114
|
validateClientSession: () => validateClientSession,
|
|
115
|
+
validateLocationRecord: () => validateLocationRecord,
|
|
116
|
+
validateLocationRecordPayload: () => validateLocationRecordPayload,
|
|
106
117
|
validateManifest: () => validateManifest,
|
|
107
|
-
validatePersistedSessionData: () => validatePersistedSessionData
|
|
118
|
+
validatePersistedSessionData: () => validatePersistedSessionData,
|
|
119
|
+
verifyLocationRecord: () => verifyLocationRecord
|
|
108
120
|
});
|
|
109
121
|
module.exports = __toCommonJS(index_exports);
|
|
110
122
|
|
|
@@ -2834,9 +2846,7 @@ function parseExpiry(duration) {
|
|
|
2834
2846
|
`expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`
|
|
2835
2847
|
);
|
|
2836
2848
|
}
|
|
2837
|
-
const parsed = (0, import_ms.default)(
|
|
2838
|
-
duration
|
|
2839
|
-
);
|
|
2849
|
+
const parsed = (0, import_ms.default)(duration);
|
|
2840
2850
|
if (typeof parsed !== "number" || !Number.isFinite(parsed) || parsed <= 0) {
|
|
2841
2851
|
throw new ManifestValidationError(
|
|
2842
2852
|
`invalid expiry duration: ${JSON.stringify(duration)}`
|
|
@@ -2891,23 +2901,33 @@ function validateManifest(input) {
|
|
|
2891
2901
|
);
|
|
2892
2902
|
}
|
|
2893
2903
|
if (typeof m.app_id !== "string" || m.app_id.length === 0) {
|
|
2894
|
-
throw new ManifestValidationError(
|
|
2904
|
+
throw new ManifestValidationError(
|
|
2905
|
+
"manifest.app_id is required and must be a non-empty string"
|
|
2906
|
+
);
|
|
2895
2907
|
}
|
|
2896
2908
|
if (typeof m.name !== "string" || m.name.length === 0) {
|
|
2897
|
-
throw new ManifestValidationError(
|
|
2909
|
+
throw new ManifestValidationError(
|
|
2910
|
+
"manifest.name is required and must be a non-empty string"
|
|
2911
|
+
);
|
|
2898
2912
|
}
|
|
2899
2913
|
if (m.did !== void 0 && (typeof m.did !== "string" || m.did.length === 0)) {
|
|
2900
|
-
throw new ManifestValidationError(
|
|
2914
|
+
throw new ManifestValidationError(
|
|
2915
|
+
"manifest.did must be a non-empty DID string"
|
|
2916
|
+
);
|
|
2901
2917
|
}
|
|
2902
2918
|
if (m.space !== void 0 && (typeof m.space !== "string" || m.space.length === 0)) {
|
|
2903
|
-
throw new ManifestValidationError(
|
|
2919
|
+
throw new ManifestValidationError(
|
|
2920
|
+
"manifest.space must be a non-empty string"
|
|
2921
|
+
);
|
|
2904
2922
|
}
|
|
2905
2923
|
if (m.expiry !== void 0) {
|
|
2906
2924
|
parseExpiry(m.expiry);
|
|
2907
2925
|
}
|
|
2908
2926
|
if (m.permissions !== void 0) {
|
|
2909
2927
|
if (!Array.isArray(m.permissions)) {
|
|
2910
|
-
throw new ManifestValidationError(
|
|
2928
|
+
throw new ManifestValidationError(
|
|
2929
|
+
"manifest.permissions must be an array"
|
|
2930
|
+
);
|
|
2911
2931
|
}
|
|
2912
2932
|
m.permissions.forEach(
|
|
2913
2933
|
(p, i) => validatePermissionEntry(p, `permissions[${i}]`)
|
|
@@ -2924,7 +2944,9 @@ function validatePermissionEntry(p, path) {
|
|
|
2924
2944
|
throw new ManifestValidationError(`${path}.service is required`);
|
|
2925
2945
|
}
|
|
2926
2946
|
if (entry.space !== void 0 && (typeof entry.space !== "string" || entry.space.length === 0)) {
|
|
2927
|
-
throw new ManifestValidationError(
|
|
2947
|
+
throw new ManifestValidationError(
|
|
2948
|
+
`${path}.space must be a non-empty string`
|
|
2949
|
+
);
|
|
2928
2950
|
}
|
|
2929
2951
|
if (typeof entry.path !== "string") {
|
|
2930
2952
|
throw new ManifestValidationError(
|
|
@@ -3015,7 +3037,8 @@ function resolveEntry(entry, prefix, _inheritedExpiryMs, inheritedSpace) {
|
|
|
3015
3037
|
// Only populate `expiryMs` when the entry had its own expiry override.
|
|
3016
3038
|
// When absent, callers use the parent (delegation or manifest) expiry
|
|
3017
3039
|
// which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.
|
|
3018
|
-
...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {}
|
|
3040
|
+
...entryExpiryMs !== void 0 ? { expiryMs: entryExpiryMs } : {},
|
|
3041
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3019
3042
|
};
|
|
3020
3043
|
}
|
|
3021
3044
|
function cloneResourceCapability(entry) {
|
|
@@ -3024,7 +3047,8 @@ function cloneResourceCapability(entry) {
|
|
|
3024
3047
|
space: entry.space,
|
|
3025
3048
|
path: entry.path,
|
|
3026
3049
|
actions: [...entry.actions],
|
|
3027
|
-
...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {}
|
|
3050
|
+
...entry.expiryMs !== void 0 ? { expiryMs: entry.expiryMs } : {},
|
|
3051
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3028
3052
|
};
|
|
3029
3053
|
}
|
|
3030
3054
|
function clonePermissionEntry(entry) {
|
|
@@ -3034,7 +3058,8 @@ function clonePermissionEntry(entry) {
|
|
|
3034
3058
|
path: entry.path,
|
|
3035
3059
|
actions: [...entry.actions],
|
|
3036
3060
|
...entry.skipPrefix !== void 0 ? { skipPrefix: entry.skipPrefix } : {},
|
|
3037
|
-
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {}
|
|
3061
|
+
...entry.expiry !== void 0 ? { expiry: entry.expiry } : {},
|
|
3062
|
+
...entry.description !== void 0 ? { description: entry.description } : {}
|
|
3038
3063
|
};
|
|
3039
3064
|
}
|
|
3040
3065
|
function dedupeResources(resources) {
|
|
@@ -3053,6 +3078,9 @@ function dedupeResources(resources) {
|
|
|
3053
3078
|
seen.add(action);
|
|
3054
3079
|
}
|
|
3055
3080
|
}
|
|
3081
|
+
if (existing.description === void 0 && resource.description !== void 0) {
|
|
3082
|
+
existing.description = resource.description;
|
|
3083
|
+
}
|
|
3056
3084
|
}
|
|
3057
3085
|
return [...byKey.values()];
|
|
3058
3086
|
}
|
|
@@ -3061,11 +3089,7 @@ function accountRegistryPermission() {
|
|
|
3061
3089
|
service: "tinycloud.kv",
|
|
3062
3090
|
space: ACCOUNT_REGISTRY_SPACE,
|
|
3063
3091
|
path: ACCOUNT_REGISTRY_PATH,
|
|
3064
|
-
actions: [
|
|
3065
|
-
"tinycloud.kv/get",
|
|
3066
|
-
"tinycloud.kv/put",
|
|
3067
|
-
"tinycloud.kv/list"
|
|
3068
|
-
]
|
|
3092
|
+
actions: ["tinycloud.kv/get", "tinycloud.kv/put", "tinycloud.kv/list"]
|
|
3069
3093
|
};
|
|
3070
3094
|
}
|
|
3071
3095
|
function composeManifestRequest(inputs, options = {}) {
|
|
@@ -4318,6 +4342,339 @@ async function checkNodeInfo(host, sdkProtocol, fetchFn = globalThis.fetch.bind(
|
|
|
4318
4342
|
};
|
|
4319
4343
|
}
|
|
4320
4344
|
|
|
4345
|
+
// src/location.ts
|
|
4346
|
+
var import_multiaddr = require("@multiformats/multiaddr");
|
|
4347
|
+
var import_multiaddr_to_uri = require("@multiformats/multiaddr-to-uri");
|
|
4348
|
+
var import_uri_to_multiaddr = require("@multiformats/uri-to-multiaddr");
|
|
4349
|
+
var import_ed25519 = require("@noble/curves/ed25519");
|
|
4350
|
+
var import_basics = require("multiformats/basics");
|
|
4351
|
+
var import_viem = require("viem");
|
|
4352
|
+
var LocationRecordValidationError = class extends Error {
|
|
4353
|
+
constructor(message) {
|
|
4354
|
+
super(`Location record validation failed: ${message}`);
|
|
4355
|
+
this.name = "LocationRecordValidationError";
|
|
4356
|
+
}
|
|
4357
|
+
};
|
|
4358
|
+
var CloudLocationResolutionError = class extends Error {
|
|
4359
|
+
constructor(subject, attempts) {
|
|
4360
|
+
super(`Unable to resolve TinyCloud location for ${subject}`);
|
|
4361
|
+
this.name = "CloudLocationResolutionError";
|
|
4362
|
+
this.attempts = attempts;
|
|
4363
|
+
}
|
|
4364
|
+
};
|
|
4365
|
+
function locationPayloadForRecord(record) {
|
|
4366
|
+
return {
|
|
4367
|
+
version: record.version,
|
|
4368
|
+
subject: record.subject,
|
|
4369
|
+
multiaddrs: [...record.multiaddrs],
|
|
4370
|
+
updated_at: record.updated_at,
|
|
4371
|
+
sequence: record.sequence
|
|
4372
|
+
};
|
|
4373
|
+
}
|
|
4374
|
+
function canonicalLocationPayload(payload) {
|
|
4375
|
+
return JSON.stringify({
|
|
4376
|
+
version: payload.version,
|
|
4377
|
+
subject: payload.subject,
|
|
4378
|
+
multiaddrs: payload.multiaddrs,
|
|
4379
|
+
updated_at: payload.updated_at,
|
|
4380
|
+
sequence: payload.sequence
|
|
4381
|
+
});
|
|
4382
|
+
}
|
|
4383
|
+
async function signLocationRecord(payload, signer) {
|
|
4384
|
+
validateLocationRecordPayload(payload);
|
|
4385
|
+
const message = canonicalLocationPayload(payload);
|
|
4386
|
+
const signature = signer.type === "did:pkh" ? await signer.signMessage(message) : base64UrlEncode2(await signer.signBytes(new TextEncoder().encode(message)));
|
|
4387
|
+
return { ...payload, signature };
|
|
4388
|
+
}
|
|
4389
|
+
function validateLocationRecordPayload(input) {
|
|
4390
|
+
if (input === null || typeof input !== "object") {
|
|
4391
|
+
throw new LocationRecordValidationError("payload must be an object");
|
|
4392
|
+
}
|
|
4393
|
+
const payload = input;
|
|
4394
|
+
if (payload.version !== 1) {
|
|
4395
|
+
throw new LocationRecordValidationError("version must be 1");
|
|
4396
|
+
}
|
|
4397
|
+
validateSubject(payload.subject);
|
|
4398
|
+
validateMultiaddrs(payload.multiaddrs);
|
|
4399
|
+
if (typeof payload.updated_at !== "string" || Number.isNaN(Date.parse(payload.updated_at))) {
|
|
4400
|
+
throw new LocationRecordValidationError("updated_at must be an ISO timestamp");
|
|
4401
|
+
}
|
|
4402
|
+
if (typeof payload.sequence !== "number" || !Number.isSafeInteger(payload.sequence) || payload.sequence < 0) {
|
|
4403
|
+
throw new LocationRecordValidationError(
|
|
4404
|
+
"sequence must be a non-negative safe integer"
|
|
4405
|
+
);
|
|
4406
|
+
}
|
|
4407
|
+
return {
|
|
4408
|
+
version: 1,
|
|
4409
|
+
subject: payload.subject,
|
|
4410
|
+
multiaddrs: [...payload.multiaddrs],
|
|
4411
|
+
updated_at: payload.updated_at,
|
|
4412
|
+
sequence: payload.sequence
|
|
4413
|
+
};
|
|
4414
|
+
}
|
|
4415
|
+
function validateLocationRecord(input) {
|
|
4416
|
+
const payload = validateLocationRecordPayload(input);
|
|
4417
|
+
const signature = input.signature;
|
|
4418
|
+
if (typeof signature !== "string" || signature.length === 0) {
|
|
4419
|
+
throw new LocationRecordValidationError("signature must be a non-empty string");
|
|
4420
|
+
}
|
|
4421
|
+
return { ...payload, signature };
|
|
4422
|
+
}
|
|
4423
|
+
async function verifyLocationRecord(input) {
|
|
4424
|
+
const record = validateLocationRecord(input);
|
|
4425
|
+
const payload = canonicalLocationPayload(locationPayloadForRecord(record));
|
|
4426
|
+
if (record.subject.startsWith("did:pkh:")) {
|
|
4427
|
+
return verifyPkhSignature(record.subject, payload, record.signature);
|
|
4428
|
+
}
|
|
4429
|
+
if (record.subject.startsWith("did:key:")) {
|
|
4430
|
+
return verifyDidKeySignature(record.subject, payload, record.signature);
|
|
4431
|
+
}
|
|
4432
|
+
return false;
|
|
4433
|
+
}
|
|
4434
|
+
async function fetchLocationRecord(registryUrl, subject, fetchFn = globalThis.fetch) {
|
|
4435
|
+
const url = `${registryUrl.replace(/\/$/, "")}/v1/locations/${encodeURIComponent(subject)}`;
|
|
4436
|
+
const response = await fetchFn(url);
|
|
4437
|
+
if (response.status === 404) {
|
|
4438
|
+
return null;
|
|
4439
|
+
}
|
|
4440
|
+
if (!response.ok) {
|
|
4441
|
+
throw new Error(`location registry returned HTTP ${response.status}`);
|
|
4442
|
+
}
|
|
4443
|
+
const body = await response.json();
|
|
4444
|
+
if (body.record === void 0) {
|
|
4445
|
+
throw new LocationRecordValidationError("registry response missing record");
|
|
4446
|
+
}
|
|
4447
|
+
return validateLocationRecord(body.record);
|
|
4448
|
+
}
|
|
4449
|
+
async function resolveCloudLocation(subject, options = {}) {
|
|
4450
|
+
validateSubject(subject);
|
|
4451
|
+
const verifyRecords = options.verifyRecords ?? true;
|
|
4452
|
+
const attempts = await Promise.all([
|
|
4453
|
+
resolveExplicit(subject, options.explicitMultiaddrs),
|
|
4454
|
+
resolveBlockchain(subject, options.blockchain, verifyRecords),
|
|
4455
|
+
resolveCentralized(subject, options, verifyRecords),
|
|
4456
|
+
resolveFallback(subject, options.fallbackMultiaddrs)
|
|
4457
|
+
]);
|
|
4458
|
+
const winner = attempts.find((attempt) => attempt.candidate)?.candidate;
|
|
4459
|
+
if (!winner) {
|
|
4460
|
+
throw new CloudLocationResolutionError(subject, attempts);
|
|
4461
|
+
}
|
|
4462
|
+
return {
|
|
4463
|
+
subject,
|
|
4464
|
+
source: winner.source,
|
|
4465
|
+
multiaddrs: [...winner.multiaddrs],
|
|
4466
|
+
...winner.record ? { record: winner.record } : {},
|
|
4467
|
+
attempts,
|
|
4468
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4471
|
+
function multiaddrToHttpUrl(input) {
|
|
4472
|
+
const uri = (0, import_multiaddr_to_uri.multiaddrToUri)((0, import_multiaddr.multiaddr)(input));
|
|
4473
|
+
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
|
|
4474
|
+
throw new LocationRecordValidationError(
|
|
4475
|
+
`multiaddr does not resolve to http/https: ${input}`
|
|
4476
|
+
);
|
|
4477
|
+
}
|
|
4478
|
+
return uri;
|
|
4479
|
+
}
|
|
4480
|
+
function httpUrlToMultiaddr(input) {
|
|
4481
|
+
const url = new URL(input);
|
|
4482
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
4483
|
+
throw new LocationRecordValidationError("URL must use http or https");
|
|
4484
|
+
}
|
|
4485
|
+
return (0, import_uri_to_multiaddr.uriToMultiaddr)(url.toString()).toString();
|
|
4486
|
+
}
|
|
4487
|
+
async function resolveExplicit(subject, multiaddrs) {
|
|
4488
|
+
return resolveAttempt("explicit", async () => {
|
|
4489
|
+
if (multiaddrs === void 0 || multiaddrs.length === 0) {
|
|
4490
|
+
return null;
|
|
4491
|
+
}
|
|
4492
|
+
return toCandidate(subject, "explicit", multiaddrs, false);
|
|
4493
|
+
});
|
|
4494
|
+
}
|
|
4495
|
+
async function resolveBlockchain(subject, resolver, verifyRecords) {
|
|
4496
|
+
return resolveAttempt("blockchain", async () => {
|
|
4497
|
+
if (!resolver) {
|
|
4498
|
+
return null;
|
|
4499
|
+
}
|
|
4500
|
+
return toCandidate(subject, "blockchain", await resolver(subject), verifyRecords);
|
|
4501
|
+
});
|
|
4502
|
+
}
|
|
4503
|
+
async function resolveCentralized(subject, options, verifyRecords) {
|
|
4504
|
+
return resolveAttempt("centralized", async () => {
|
|
4505
|
+
if (!options.centralizedRegistryUrl) {
|
|
4506
|
+
return null;
|
|
4507
|
+
}
|
|
4508
|
+
const record = await fetchLocationRecord(
|
|
4509
|
+
options.centralizedRegistryUrl,
|
|
4510
|
+
subject,
|
|
4511
|
+
options.fetch
|
|
4512
|
+
);
|
|
4513
|
+
return toCandidate(subject, "centralized", record, verifyRecords);
|
|
4514
|
+
});
|
|
4515
|
+
}
|
|
4516
|
+
async function resolveFallback(subject, multiaddrs) {
|
|
4517
|
+
return resolveAttempt("fallback", async () => {
|
|
4518
|
+
if (multiaddrs === void 0 || multiaddrs.length === 0) {
|
|
4519
|
+
return null;
|
|
4520
|
+
}
|
|
4521
|
+
return toCandidate(subject, "fallback", multiaddrs, false);
|
|
4522
|
+
});
|
|
4523
|
+
}
|
|
4524
|
+
async function resolveAttempt(source, resolve) {
|
|
4525
|
+
try {
|
|
4526
|
+
const candidate = await resolve();
|
|
4527
|
+
return candidate ? { source, candidate } : { source };
|
|
4528
|
+
} catch (error) {
|
|
4529
|
+
return {
|
|
4530
|
+
source,
|
|
4531
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
async function toCandidate(subject, source, input, verifyRecord) {
|
|
4536
|
+
if (input === null || input === void 0) {
|
|
4537
|
+
return null;
|
|
4538
|
+
}
|
|
4539
|
+
if (Array.isArray(input)) {
|
|
4540
|
+
validateMultiaddrs(input);
|
|
4541
|
+
return { source, multiaddrs: [...input] };
|
|
4542
|
+
}
|
|
4543
|
+
const maybeRecord = input;
|
|
4544
|
+
if (maybeRecord.version === 1 && maybeRecord.signature !== void 0) {
|
|
4545
|
+
const record = validateLocationRecord(input);
|
|
4546
|
+
if (record.subject !== subject) {
|
|
4547
|
+
throw new LocationRecordValidationError(
|
|
4548
|
+
"location record subject does not match requested subject"
|
|
4549
|
+
);
|
|
4550
|
+
}
|
|
4551
|
+
if (verifyRecord && !await verifyLocationRecord(record)) {
|
|
4552
|
+
throw new LocationRecordValidationError("location record signature is invalid");
|
|
4553
|
+
}
|
|
4554
|
+
return { source, multiaddrs: [...record.multiaddrs], record };
|
|
4555
|
+
}
|
|
4556
|
+
const candidateInput = input;
|
|
4557
|
+
if (!Array.isArray(candidateInput.multiaddrs)) {
|
|
4558
|
+
throw new LocationRecordValidationError("candidate multiaddrs must be an array");
|
|
4559
|
+
}
|
|
4560
|
+
validateMultiaddrs(candidateInput.multiaddrs);
|
|
4561
|
+
if (candidateInput.record !== void 0) {
|
|
4562
|
+
const record = validateLocationRecord(candidateInput.record);
|
|
4563
|
+
if (record.subject !== subject) {
|
|
4564
|
+
throw new LocationRecordValidationError(
|
|
4565
|
+
"location record subject does not match requested subject"
|
|
4566
|
+
);
|
|
4567
|
+
}
|
|
4568
|
+
if (verifyRecord && !await verifyLocationRecord(record)) {
|
|
4569
|
+
throw new LocationRecordValidationError("location record signature is invalid");
|
|
4570
|
+
}
|
|
4571
|
+
return { source, multiaddrs: [...candidateInput.multiaddrs], record };
|
|
4572
|
+
}
|
|
4573
|
+
return { source, multiaddrs: [...candidateInput.multiaddrs] };
|
|
4574
|
+
}
|
|
4575
|
+
function validateSubject(subject) {
|
|
4576
|
+
if (typeof subject !== "string" || subject.length === 0) {
|
|
4577
|
+
throw new LocationRecordValidationError("subject must be a non-empty string");
|
|
4578
|
+
}
|
|
4579
|
+
if (!subject.startsWith("did:pkh:") && !subject.startsWith("did:key:")) {
|
|
4580
|
+
throw new LocationRecordValidationError("subject must be did:pkh or did:key");
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
function validateMultiaddrs(input) {
|
|
4584
|
+
if (!Array.isArray(input)) {
|
|
4585
|
+
throw new LocationRecordValidationError("multiaddrs must be an array");
|
|
4586
|
+
}
|
|
4587
|
+
for (const addr of input) {
|
|
4588
|
+
if (typeof addr !== "string" || addr.length === 0) {
|
|
4589
|
+
throw new LocationRecordValidationError(
|
|
4590
|
+
"multiaddr entries must be non-empty strings"
|
|
4591
|
+
);
|
|
4592
|
+
}
|
|
4593
|
+
try {
|
|
4594
|
+
(0, import_multiaddr.multiaddr)(addr);
|
|
4595
|
+
} catch {
|
|
4596
|
+
throw new LocationRecordValidationError(`invalid multiaddr: ${addr}`);
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
async function verifyPkhSignature(did, payload, signature) {
|
|
4601
|
+
const address = did.split(":").at(-1);
|
|
4602
|
+
if (!address || !/^0x[a-fA-F0-9]{40}$/.test(address)) {
|
|
4603
|
+
throw new LocationRecordValidationError(
|
|
4604
|
+
"did:pkh subject must end with an EVM address"
|
|
4605
|
+
);
|
|
4606
|
+
}
|
|
4607
|
+
if (!/^0x[0-9a-fA-F]+$/.test(signature)) {
|
|
4608
|
+
throw new LocationRecordValidationError("did:pkh signature must be hex");
|
|
4609
|
+
}
|
|
4610
|
+
return (0, import_viem.verifyMessage)({
|
|
4611
|
+
address,
|
|
4612
|
+
message: payload,
|
|
4613
|
+
signature
|
|
4614
|
+
});
|
|
4615
|
+
}
|
|
4616
|
+
function verifyDidKeySignature(did, payload, signature) {
|
|
4617
|
+
const publicKey = ed25519PublicKeyFromDidKey(did);
|
|
4618
|
+
const signatureBytes = decodeBase64Url(signature);
|
|
4619
|
+
if (signatureBytes.length !== 64) {
|
|
4620
|
+
throw new LocationRecordValidationError(
|
|
4621
|
+
"did:key signature must be a base64url Ed25519 signature"
|
|
4622
|
+
);
|
|
4623
|
+
}
|
|
4624
|
+
return import_ed25519.ed25519.verify(signatureBytes, new TextEncoder().encode(payload), publicKey);
|
|
4625
|
+
}
|
|
4626
|
+
function ed25519PublicKeyFromDidKey(did) {
|
|
4627
|
+
const identifier = did.slice("did:key:".length);
|
|
4628
|
+
if (!identifier.startsWith("z")) {
|
|
4629
|
+
throw new LocationRecordValidationError("did:key must use base58btc multibase");
|
|
4630
|
+
}
|
|
4631
|
+
const bytes = import_basics.bases.base58btc.decode(identifier);
|
|
4632
|
+
if (bytes.length !== 34 || bytes[0] !== 237 || bytes[1] !== 1) {
|
|
4633
|
+
throw new LocationRecordValidationError("did:key must be an Ed25519 public key");
|
|
4634
|
+
}
|
|
4635
|
+
return bytes.slice(2);
|
|
4636
|
+
}
|
|
4637
|
+
function base64UrlEncode2(bytes) {
|
|
4638
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
4639
|
+
let output = "";
|
|
4640
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
4641
|
+
const a = bytes[i];
|
|
4642
|
+
const b = bytes[i + 1];
|
|
4643
|
+
const c = bytes[i + 2];
|
|
4644
|
+
const triplet = a << 16 | (b ?? 0) << 8 | (c ?? 0);
|
|
4645
|
+
output += alphabet[triplet >> 18 & 63];
|
|
4646
|
+
output += alphabet[triplet >> 12 & 63];
|
|
4647
|
+
if (i + 1 < bytes.length) {
|
|
4648
|
+
output += alphabet[triplet >> 6 & 63];
|
|
4649
|
+
}
|
|
4650
|
+
if (i + 2 < bytes.length) {
|
|
4651
|
+
output += alphabet[triplet & 63];
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
return output;
|
|
4655
|
+
}
|
|
4656
|
+
function decodeBase64Url(value) {
|
|
4657
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
4658
|
+
const bytes = [];
|
|
4659
|
+
let buffer = 0;
|
|
4660
|
+
let bits = 0;
|
|
4661
|
+
for (const char of value) {
|
|
4662
|
+
const index = alphabet.indexOf(char);
|
|
4663
|
+
if (index < 0) {
|
|
4664
|
+
throw new LocationRecordValidationError(
|
|
4665
|
+
"did:key signature must be base64url"
|
|
4666
|
+
);
|
|
4667
|
+
}
|
|
4668
|
+
buffer = buffer << 6 | index;
|
|
4669
|
+
bits += 6;
|
|
4670
|
+
if (bits >= 8) {
|
|
4671
|
+
bits -= 8;
|
|
4672
|
+
bytes.push(buffer >> bits & 255);
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
return Uint8Array.from(bytes);
|
|
4676
|
+
}
|
|
4677
|
+
|
|
4321
4678
|
// src/capabilities.ts
|
|
4322
4679
|
var PermissionNotInManifestError = class extends Error {
|
|
4323
4680
|
constructor(missing, granted) {
|
|
@@ -4439,6 +4796,7 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4439
4796
|
CapabilityKeyRegistry,
|
|
4440
4797
|
CapabilityKeyRegistryErrorCodes,
|
|
4441
4798
|
ClientSessionSchema,
|
|
4799
|
+
CloudLocationResolutionError,
|
|
4442
4800
|
DEFAULT_DEFAULTS,
|
|
4443
4801
|
DEFAULT_EXPIRY,
|
|
4444
4802
|
DEFAULT_MANIFEST_SPACE,
|
|
@@ -4454,6 +4812,7 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4454
4812
|
ErrorCodes,
|
|
4455
4813
|
HooksService,
|
|
4456
4814
|
KVService,
|
|
4815
|
+
LocationRecordValidationError,
|
|
4457
4816
|
ManifestValidationError,
|
|
4458
4817
|
PermissionNotInManifestError,
|
|
4459
4818
|
PrefixedKVService,
|
|
@@ -4479,6 +4838,7 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4479
4838
|
activateSessionWithHost,
|
|
4480
4839
|
applyPrefix,
|
|
4481
4840
|
buildSpaceUri,
|
|
4841
|
+
canonicalLocationPayload,
|
|
4482
4842
|
checkNodeInfo,
|
|
4483
4843
|
composeManifestRequest,
|
|
4484
4844
|
createCapabilityKeyRegistry,
|
|
@@ -4490,23 +4850,32 @@ function parseRecapCapabilities(parseWasm, siwe) {
|
|
|
4490
4850
|
defaultSpaceCreationHandler,
|
|
4491
4851
|
err,
|
|
4492
4852
|
expandActionShortNames,
|
|
4853
|
+
fetchLocationRecord,
|
|
4493
4854
|
fetchPeerId,
|
|
4855
|
+
httpUrlToMultiaddr,
|
|
4494
4856
|
isCapabilitySubset,
|
|
4495
4857
|
loadManifest,
|
|
4858
|
+
locationPayloadForRecord,
|
|
4496
4859
|
makePublicSpaceId,
|
|
4497
4860
|
manifestAbilitiesUnion,
|
|
4861
|
+
multiaddrToHttpUrl,
|
|
4498
4862
|
normalizeDefaults,
|
|
4499
4863
|
ok,
|
|
4500
4864
|
parseExpiry,
|
|
4501
4865
|
parseRecapCapabilities,
|
|
4502
4866
|
parseSpaceUri,
|
|
4867
|
+
resolveCloudLocation,
|
|
4503
4868
|
resolveManifest,
|
|
4504
4869
|
resourceCapabilitiesToAbilitiesMap,
|
|
4505
4870
|
resourceCapabilitiesToSpaceAbilitiesMap,
|
|
4506
4871
|
serviceError,
|
|
4872
|
+
signLocationRecord,
|
|
4507
4873
|
submitHostDelegation,
|
|
4508
4874
|
validateClientSession,
|
|
4875
|
+
validateLocationRecord,
|
|
4876
|
+
validateLocationRecordPayload,
|
|
4509
4877
|
validateManifest,
|
|
4510
|
-
validatePersistedSessionData
|
|
4878
|
+
validatePersistedSessionData,
|
|
4879
|
+
verifyLocationRecord
|
|
4511
4880
|
});
|
|
4512
4881
|
//# sourceMappingURL=index.cjs.map
|