@red-isbe/isbe-service-did-registry 1.0.0
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/LICENSE +202 -0
- package/README.md +129 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/src/ServiceDidRegistryContext.d.ts +40 -0
- package/dist/src/ServiceDidRegistryContext.d.ts.map +1 -0
- package/dist/src/ServiceDidRegistryContext.js +42 -0
- package/dist/src/ServiceDidRegistryContext.js.map +1 -0
- package/dist/src/ServiceDidRegistryQueries.d.ts +127 -0
- package/dist/src/ServiceDidRegistryQueries.d.ts.map +1 -0
- package/dist/src/ServiceDidRegistryQueries.js +226 -0
- package/dist/src/ServiceDidRegistryQueries.js.map +1 -0
- package/dist/src/ServiceDidRegistryTransactions.d.ts +90 -0
- package/dist/src/ServiceDidRegistryTransactions.d.ts.map +1 -0
- package/dist/src/ServiceDidRegistryTransactions.js +134 -0
- package/dist/src/ServiceDidRegistryTransactions.js.map +1 -0
- package/dist/src/ServiceDidTransactionExecutor.d.ts +31 -0
- package/dist/src/ServiceDidTransactionExecutor.d.ts.map +1 -0
- package/dist/src/ServiceDidTransactionExecutor.js +37 -0
- package/dist/src/ServiceDidTransactionExecutor.js.map +1 -0
- package/dist/src/abi/IServiceDidRegistry.abi.d.ts +578 -0
- package/dist/src/abi/IServiceDidRegistry.abi.d.ts.map +1 -0
- package/dist/src/abi/IServiceDidRegistry.abi.js +741 -0
- package/dist/src/abi/IServiceDidRegistry.abi.js.map +1 -0
- package/dist/src/serviceDid.types.d.ts +78 -0
- package/dist/src/serviceDid.types.d.ts.map +1 -0
- package/dist/src/serviceDid.types.js +16 -0
- package/dist/src/serviceDid.types.js.map +1 -0
- package/dist/src/types.d.ts +35 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +16 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/blockchain.utils.d.ts +49 -0
- package/dist/src/utils/blockchain.utils.d.ts.map +1 -0
- package/dist/src/utils/blockchain.utils.js +102 -0
- package/dist/src/utils/blockchain.utils.js.map +1 -0
- package/dist/src/utils/did.utils.d.ts +20 -0
- package/dist/src/utils/did.utils.d.ts.map +1 -0
- package/dist/src/utils/did.utils.js +57 -0
- package/dist/src/utils/did.utils.js.map +1 -0
- package/dist/src/utils/serviceDid.utils.d.ts +71 -0
- package/dist/src/utils/serviceDid.utils.d.ts.map +1 -0
- package/dist/src/utils/serviceDid.utils.js +142 -0
- package/dist/src/utils/serviceDid.utils.js.map +1 -0
- package/dist/src/utils/serviceDocument.utils.d.ts +61 -0
- package/dist/src/utils/serviceDocument.utils.d.ts.map +1 -0
- package/dist/src/utils/serviceDocument.utils.js +101 -0
- package/dist/src/utils/serviceDocument.utils.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Comunidad de Madrid & Alastria
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
*
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { AbiCoder, getAddress, keccak256 } from "ethers";
|
|
17
|
+
import { didToHex } from "./did.utils.js";
|
|
18
|
+
const SERVICE_PREFIX = "did:isbe:svc:";
|
|
19
|
+
const ORGANISATIONAL_PREFIX = "did:isbe:";
|
|
20
|
+
/**
|
|
21
|
+
* Derives the on-chain identifier of a service identity.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors `ServiceDidRegistryInternal._computeServiceDid` exactly. The encoding is
|
|
24
|
+
* `abi.encode`, NOT `solidityPacked`: `abi.encode` pads the nonce to 32 bytes, and a
|
|
25
|
+
* packed encoding produces a different digest. Getting this wrong does not break any
|
|
26
|
+
* transaction — it breaks resolution, silently, which is far worse.
|
|
27
|
+
*
|
|
28
|
+
* @param controllerDidHex Parent DID as bytes32 hex
|
|
29
|
+
* @param nonce Monotonic counter of the controller, starting at 1
|
|
30
|
+
*/
|
|
31
|
+
export function computeServiceDidHex(controllerDidHex, nonce) {
|
|
32
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(controllerDidHex)) {
|
|
33
|
+
throw new Error("The 'controllerDidHex' parameter must be a 32-byte hexadecimal string");
|
|
34
|
+
}
|
|
35
|
+
const nonceValue = BigInt(nonce);
|
|
36
|
+
if (nonceValue <= 0n) {
|
|
37
|
+
throw new Error("The 'nonce' parameter must be greater than zero");
|
|
38
|
+
}
|
|
39
|
+
return keccak256(AbiCoder.defaultAbiCoder().encode(["bytes32", "uint64"], [controllerDidHex, nonceValue]));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Builds the readable identifier of a service identity.
|
|
43
|
+
*
|
|
44
|
+
* The parent's own segment is carried verbatim, which is what lets a reader recover
|
|
45
|
+
* the controlling DID from the service DID alone, with no lookup. A digest of the
|
|
46
|
+
* parent would not be reversible.
|
|
47
|
+
*
|
|
48
|
+
* @param controllerDid Parent DID, as did:isbe:<network>:<segment>
|
|
49
|
+
* @param nonce Monotonic counter of the controller
|
|
50
|
+
*/
|
|
51
|
+
export function buildServiceDid(controllerDid, nonce) {
|
|
52
|
+
const { network, segment } = splitOrganisationalDid(controllerDid);
|
|
53
|
+
return `${SERVICE_PREFIX}${network}:${segment}:${BigInt(nonce).toString()}`;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Recovers the parent DID and the nonce from a service DID string.
|
|
57
|
+
*
|
|
58
|
+
* Entirely local: no network call. This is the direction that matters for resolution,
|
|
59
|
+
* and the reason the identifier carries the parent's segment rather than a digest.
|
|
60
|
+
*
|
|
61
|
+
* Note the asymmetry — the reverse is not possible. A service identifier is a full
|
|
62
|
+
* keccak digest, so the DID string cannot be recovered from it without reading the
|
|
63
|
+
* record from the chain to learn its controller and nonce.
|
|
64
|
+
*/
|
|
65
|
+
export function parseServiceDid(serviceDid) {
|
|
66
|
+
if (!serviceDid || !serviceDid.startsWith(SERVICE_PREFIX)) {
|
|
67
|
+
throw new Error(`The 'serviceDid' parameter must start with '${SERVICE_PREFIX}'`);
|
|
68
|
+
}
|
|
69
|
+
const rest = serviceDid.slice(SERVICE_PREFIX.length).split("#")[0].split("?")[0];
|
|
70
|
+
const parts = rest.split(":");
|
|
71
|
+
if (parts.length !== 3) {
|
|
72
|
+
throw new Error("The 'serviceDid' parameter must have the form did:isbe:svc:<network>:<segment>:<nonce>");
|
|
73
|
+
}
|
|
74
|
+
const [network, segment, nonceText] = parts;
|
|
75
|
+
if (!network || !segment) {
|
|
76
|
+
throw new Error("The service DID carries an empty network or segment");
|
|
77
|
+
}
|
|
78
|
+
if (!/^[0-9]+$/.test(nonceText)) {
|
|
79
|
+
throw new Error("The nonce of a service DID must be a decimal integer");
|
|
80
|
+
}
|
|
81
|
+
const nonce = BigInt(nonceText);
|
|
82
|
+
if (nonce <= 0n) {
|
|
83
|
+
throw new Error("The nonce of a service DID starts at one");
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
network,
|
|
87
|
+
controllerDid: `${ORGANISATIONAL_PREFIX}${network}:${segment}`,
|
|
88
|
+
nonce,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Resolves a service DID string to the identifier the contract stores.
|
|
93
|
+
*
|
|
94
|
+
* Local: parses the string, converts the parent DID to bytes32 and derives the digest.
|
|
95
|
+
*/
|
|
96
|
+
export function serviceDidToHex(serviceDid) {
|
|
97
|
+
const { controllerDid, nonce } = parseServiceDid(serviceDid);
|
|
98
|
+
return computeServiceDidHex(didToHex(controllerDid), nonce);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Derives the address of a signing key from its coordinates.
|
|
102
|
+
*
|
|
103
|
+
* Reproduces the derivation the organisational registry applies to its own keys —
|
|
104
|
+
* keccak256 of the 64 coordinate bytes, truncated to 20 — so both registries agree on
|
|
105
|
+
* the address of a given key.
|
|
106
|
+
*
|
|
107
|
+
* Returned checksummed, so it compares equal to what ethers hands back from a contract
|
|
108
|
+
* call or from a wallet. A lowercase form would be the same address and still fail a
|
|
109
|
+
* naive equality check.
|
|
110
|
+
*/
|
|
111
|
+
export function signingKeyAddress(pubKeyX, pubKeyY) {
|
|
112
|
+
const x = stripHex(pubKeyX, "pubKeyX");
|
|
113
|
+
const y = stripHex(pubKeyY, "pubKeyY");
|
|
114
|
+
const digest = keccak256(`0x${x}${y}`);
|
|
115
|
+
return getAddress(`0x${digest.slice(-40)}`);
|
|
116
|
+
}
|
|
117
|
+
/** Reassembles the two coordinates into the 65-byte uncompressed encoding. */
|
|
118
|
+
export function toUncompressedPublicKey(pubKeyX, pubKeyY) {
|
|
119
|
+
return `0x04${stripHex(pubKeyX, "pubKeyX")}${stripHex(pubKeyY, "pubKeyY")}`;
|
|
120
|
+
}
|
|
121
|
+
/** Splits an organisational DID into its network discriminator and its segment. */
|
|
122
|
+
function splitOrganisationalDid(controllerDid) {
|
|
123
|
+
if (!controllerDid || !controllerDid.startsWith(ORGANISATIONAL_PREFIX)) {
|
|
124
|
+
throw new Error(`The 'controllerDid' parameter must start with '${ORGANISATIONAL_PREFIX}'`);
|
|
125
|
+
}
|
|
126
|
+
const parts = controllerDid
|
|
127
|
+
.slice(ORGANISATIONAL_PREFIX.length)
|
|
128
|
+
.split("#")[0]
|
|
129
|
+
.split("?")[0]
|
|
130
|
+
.split(":");
|
|
131
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
132
|
+
throw new Error("The 'controllerDid' parameter must have the form did:isbe:<network>:<segment>");
|
|
133
|
+
}
|
|
134
|
+
return { network: parts[0], segment: parts[1] };
|
|
135
|
+
}
|
|
136
|
+
function stripHex(value, name) {
|
|
137
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(value)) {
|
|
138
|
+
throw new Error(`The '${name}' parameter must be a 32-byte hexadecimal string`);
|
|
139
|
+
}
|
|
140
|
+
return value.slice(2);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=serviceDid.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceDid.utils.js","sourceRoot":"","sources":["../../../src/utils/serviceDid.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;EAcE;AAEF,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,MAAM,cAAc,GAAG,eAAe,CAAC;AACvC,MAAM,qBAAqB,GAAG,WAAW,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,gBAAwB,EACxB,KAAsB;IAEtB,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACb,uEAAuE,CACxE,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,UAAU,IAAI,EAAE,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,SAAS,CACd,QAAQ,CAAC,eAAe,EAAE,CAAC,MAAM,CAC/B,CAAC,SAAS,EAAE,QAAQ,CAAC,EACrB,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAC/B,CACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,aAAqB,EACrB,KAAsB;IAEtB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;IACnE,OAAO,GAAG,cAAc,GAAG,OAAO,IAAI,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC9E,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CACb,+CAA+C,cAAc,GAAG,CACjE,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACnF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,wFAAwF,CACzF,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,KAAiC,CAAC;IACxE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAChC,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO;QACL,OAAO;QACP,aAAa,EAAE,GAAG,qBAAqB,GAAG,OAAO,IAAI,OAAO,EAAE;QAC9D,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC7D,OAAO,oBAAoB,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,OAAe;IAChE,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACvC,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACvC,OAAO,UAAU,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,uBAAuB,CACrC,OAAe,EACf,OAAe;IAEf,OAAO,OAAO,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,mFAAmF;AACnF,SAAS,sBAAsB,CAAC,aAAqB;IAInD,IAAI,CAAC,aAAa,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,KAAK,CACb,kDAAkD,qBAAqB,GAAG,CAC3E,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,aAAa;SACxB,KAAK,CAAC,qBAAqB,CAAC,MAAM,CAAC;SACnC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE;SACd,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE;SACd,KAAK,CAAC,GAAG,CAAC,CAAC;IAEd,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,IAAY;IAC3C,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CACb,QAAQ,IAAI,kDAAkD,CAC/D,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Comunidad de Madrid & Alastria
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
*
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The free-form part of a service identity.
|
|
18
|
+
*
|
|
19
|
+
* A Service DID has a fixed minimum — controller, key, curve, label, expiry — and on
|
|
20
|
+
* top of it each organisation may add whatever fields it needs: a service endpoint, a
|
|
21
|
+
* company name, a description. Those extra fields travel as one JSON object, stored
|
|
22
|
+
* on-chain next to the record, and a resolver adds them to the DID Document.
|
|
23
|
+
*
|
|
24
|
+
* The registry treats the document as an opaque string. Everything here is the
|
|
25
|
+
* contract this library puts around it: it must be a JSON object, it cannot redefine
|
|
26
|
+
* what the registry itself asserts, and it has to fit the on-chain size limit.
|
|
27
|
+
*/
|
|
28
|
+
/** Members of a service document. Values are any JSON. */
|
|
29
|
+
export type ServiceDocument = Record<string, unknown>;
|
|
30
|
+
/** Largest document the registry accepts, in UTF-8 bytes. Mirrors the contract. */
|
|
31
|
+
export declare const MAX_SERVICE_DOCUMENT_BYTES = 4096;
|
|
32
|
+
/**
|
|
33
|
+
* Members a service document may not set, because the resolver derives them from the
|
|
34
|
+
* registry. Letting a document define its own `id`, `controller` or keys would let it
|
|
35
|
+
* claim an identity, or a key, the registry never bound to it.
|
|
36
|
+
*/
|
|
37
|
+
export declare const RESERVED_DOCUMENT_KEYS: readonly string[];
|
|
38
|
+
/** Raised when a service document cannot be stored as given. */
|
|
39
|
+
export declare class ServiceDocumentError extends Error {
|
|
40
|
+
constructor(message: string);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Turns a service document into the string the registry stores.
|
|
44
|
+
*
|
|
45
|
+
* `null`, `undefined` and `{}` all become the empty string, which is what clears a
|
|
46
|
+
* document on-chain.
|
|
47
|
+
*
|
|
48
|
+
* @throws ServiceDocumentError when the document is not a JSON object, sets a reserved
|
|
49
|
+
* member, or exceeds `MAX_SERVICE_DOCUMENT_BYTES` once serialised
|
|
50
|
+
*/
|
|
51
|
+
export declare function serializeServiceDocument(document: ServiceDocument | null | undefined): string;
|
|
52
|
+
/**
|
|
53
|
+
* Reads a stored service document back.
|
|
54
|
+
*
|
|
55
|
+
* Tolerant by design: the contract accepts any string, so a document written without
|
|
56
|
+
* this library may not be JSON, or may set reserved members. Resolution must not
|
|
57
|
+
* break because of it, so anything unreadable yields `null` and reserved members are
|
|
58
|
+
* dropped.
|
|
59
|
+
*/
|
|
60
|
+
export declare function parseServiceDocument(raw: string): ServiceDocument | null;
|
|
61
|
+
//# sourceMappingURL=serviceDocument.utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceDocument.utils.d.ts","sourceRoot":"","sources":["../../../src/utils/serviceDocument.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;EAcE;AAEF;;;;;;;;;;;GAWG;AAEH,0DAA0D;AAC1D,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,mFAAmF;AACnF,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAYnD,CAAC;AAEF,gEAAgE;AAChE,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAMD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,GAC3C,MAAM,CA2BR;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAgBxE"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Comunidad de Madrid & Alastria
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
*
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/** Largest document the registry accepts, in UTF-8 bytes. Mirrors the contract. */
|
|
17
|
+
export const MAX_SERVICE_DOCUMENT_BYTES = 4096;
|
|
18
|
+
/**
|
|
19
|
+
* Members a service document may not set, because the resolver derives them from the
|
|
20
|
+
* registry. Letting a document define its own `id`, `controller` or keys would let it
|
|
21
|
+
* claim an identity, or a key, the registry never bound to it.
|
|
22
|
+
*/
|
|
23
|
+
export const RESERVED_DOCUMENT_KEYS = [
|
|
24
|
+
"@context",
|
|
25
|
+
"id",
|
|
26
|
+
"controller",
|
|
27
|
+
"verificationMethod",
|
|
28
|
+
"authentication",
|
|
29
|
+
"assertionMethod",
|
|
30
|
+
"capabilityInvocation",
|
|
31
|
+
"capabilityDelegation",
|
|
32
|
+
"keyAgreement",
|
|
33
|
+
"deactivated",
|
|
34
|
+
"reason",
|
|
35
|
+
];
|
|
36
|
+
/** Raised when a service document cannot be stored as given. */
|
|
37
|
+
export class ServiceDocumentError extends Error {
|
|
38
|
+
constructor(message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = "ServiceDocumentError";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function isPlainObject(value) {
|
|
44
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Turns a service document into the string the registry stores.
|
|
48
|
+
*
|
|
49
|
+
* `null`, `undefined` and `{}` all become the empty string, which is what clears a
|
|
50
|
+
* document on-chain.
|
|
51
|
+
*
|
|
52
|
+
* @throws ServiceDocumentError when the document is not a JSON object, sets a reserved
|
|
53
|
+
* member, or exceeds `MAX_SERVICE_DOCUMENT_BYTES` once serialised
|
|
54
|
+
*/
|
|
55
|
+
export function serializeServiceDocument(document) {
|
|
56
|
+
if (document === null || document === undefined)
|
|
57
|
+
return "";
|
|
58
|
+
if (!isPlainObject(document)) {
|
|
59
|
+
throw new ServiceDocumentError("The service document must be a JSON object.");
|
|
60
|
+
}
|
|
61
|
+
const reserved = Object.keys(document).filter((key) => RESERVED_DOCUMENT_KEYS.includes(key));
|
|
62
|
+
if (reserved.length > 0) {
|
|
63
|
+
throw new ServiceDocumentError(`The service document cannot set ${reserved.join(", ")}: the registry derives ${reserved.length === 1 ? "it" : "them"}.`);
|
|
64
|
+
}
|
|
65
|
+
if (Object.keys(document).length === 0)
|
|
66
|
+
return "";
|
|
67
|
+
const serialized = JSON.stringify(document);
|
|
68
|
+
const bytes = new TextEncoder().encode(serialized).length;
|
|
69
|
+
if (bytes > MAX_SERVICE_DOCUMENT_BYTES) {
|
|
70
|
+
throw new ServiceDocumentError(`The service document is ${bytes} bytes; the registry accepts at most ${MAX_SERVICE_DOCUMENT_BYTES}.`);
|
|
71
|
+
}
|
|
72
|
+
return serialized;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Reads a stored service document back.
|
|
76
|
+
*
|
|
77
|
+
* Tolerant by design: the contract accepts any string, so a document written without
|
|
78
|
+
* this library may not be JSON, or may set reserved members. Resolution must not
|
|
79
|
+
* break because of it, so anything unreadable yields `null` and reserved members are
|
|
80
|
+
* dropped.
|
|
81
|
+
*/
|
|
82
|
+
export function parseServiceDocument(raw) {
|
|
83
|
+
if (!raw)
|
|
84
|
+
return null;
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = JSON.parse(raw);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (!isPlainObject(parsed))
|
|
93
|
+
return null;
|
|
94
|
+
const clean = {};
|
|
95
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
96
|
+
if (!RESERVED_DOCUMENT_KEYS.includes(key))
|
|
97
|
+
clean[key] = value;
|
|
98
|
+
}
|
|
99
|
+
return Object.keys(clean).length > 0 ? clean : null;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=serviceDocument.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceDocument.utils.js","sourceRoot":"","sources":["../../../src/utils/serviceDocument.utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@red-isbe/isbe-service-did-registry",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A library for interacting with the ISBE Service DID Registry: operational identities under did:isbe:svc:",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "npm run test:unit && npm run test:esm",
|
|
16
|
+
"test:unit": "jest --config jest.config.ts",
|
|
17
|
+
"test:esm": "NODE_OPTIONS=--experimental-vm-modules jest -c jest.esm.config.ts",
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"prepublishOnly": "npm run build && npm test"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"did",
|
|
23
|
+
"did:isbe",
|
|
24
|
+
"did:isbe:svc",
|
|
25
|
+
"service-did"
|
|
26
|
+
],
|
|
27
|
+
"author": "ISBE",
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/red-isbe/isbe-service-did-lib.git"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@red-isbe/did-isbe-registry": "^2.0.1",
|
|
35
|
+
"@types/jest": "^30.0.0",
|
|
36
|
+
"@types/node": "^24.9.1",
|
|
37
|
+
"jest": "^30.1.3",
|
|
38
|
+
"ts-jest": "^29.4.4",
|
|
39
|
+
"typescript": "^5.9.3"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"ethers": "^6.15.0",
|
|
43
|
+
"multiformats": "^13.4.2"
|
|
44
|
+
}
|
|
45
|
+
}
|