@cef-ebsi/ebsi-uri 0.1.0 → 0.2.0-alpha.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/README.md +22 -0
- package/dist/index.d.ts +32 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -38
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +184 -106
package/README.md
CHANGED
|
@@ -44,6 +44,28 @@ returns
|
|
|
44
44
|
}
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
### Parse URL
|
|
48
|
+
|
|
49
|
+
The `parseUrl` function takes an URL (string) and returns the URI components `network`, `service`, and `resource`. The function accepts an optional object that contains the list of supported services with their version number, and will throw an error if the URL contains a service or service version that is not supported.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
parseUrl(
|
|
55
|
+
"https://api-test.ebsi.eu/did-registry/v4/identifiers/did:ebsi:zx23VhpbGYeqF2MRJk3HzPw",
|
|
56
|
+
);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
returns
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"network": "test",
|
|
64
|
+
"service": "did-registry",
|
|
65
|
+
"resource": "/identifiers/did:ebsi:zx23VhpbGYeqF2MRJk3HzPw"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
47
69
|
### Serialize
|
|
48
70
|
|
|
49
71
|
The `serialize` function takes an object containing the URI components `network`, `service`, and `resource`. It returns a string representation of the EBSI URI.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,31 +1,43 @@
|
|
|
1
1
|
export declare const SCHEME = "ebsi";
|
|
2
2
|
export declare const PRODUCTION_NETWORK = "production";
|
|
3
3
|
export declare const NETWORKS: readonly ["production", "preprod", "conformance", "pilot", "test"];
|
|
4
|
-
export declare const
|
|
5
|
-
readonly
|
|
6
|
-
readonly pilot:
|
|
7
|
-
readonly
|
|
8
|
-
readonly
|
|
9
|
-
readonly
|
|
4
|
+
export declare const NETWORKS_PATTERNS: {
|
|
5
|
+
readonly conformance: RegExp;
|
|
6
|
+
readonly pilot: RegExp;
|
|
7
|
+
readonly preprod: RegExp;
|
|
8
|
+
readonly production: RegExp;
|
|
9
|
+
readonly test: RegExp;
|
|
10
10
|
};
|
|
11
|
+
export interface EBSIURIComponents<T extends string = string> {
|
|
12
|
+
network: Network;
|
|
13
|
+
resource: string;
|
|
14
|
+
service: ServiceName<T>;
|
|
15
|
+
}
|
|
16
|
+
export interface IFromUrlOptions<T extends string = string> {
|
|
17
|
+
readonly services?: SupportedServices<T>;
|
|
18
|
+
}
|
|
19
|
+
export interface IParseUrlOptions<T extends string = string> {
|
|
20
|
+
readonly services?: SupportedServices<T>;
|
|
21
|
+
}
|
|
22
|
+
export interface IToUrlOptions<T extends string = string> {
|
|
23
|
+
readonly hosts: Partial<Record<Network, string>>;
|
|
24
|
+
readonly services: SupportedServices<T>;
|
|
25
|
+
}
|
|
11
26
|
export type Network = (typeof NETWORKS)[number];
|
|
12
|
-
export type
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
export type SupportedServices = Record<string, string>;
|
|
27
|
+
export type ServiceName<T extends string = string> = T;
|
|
28
|
+
export type ServiceVersion = `v${number}`;
|
|
29
|
+
export type SupportedServices<T extends string> = Record<ServiceName<T>, ServiceVersion>;
|
|
30
|
+
export declare function fromUrl<T extends string>(url: string, options?: IFromUrlOptions<T>): string;
|
|
18
31
|
export declare function parse(uriString: string): {
|
|
19
32
|
network: "production" | "preprod" | "conformance" | "pilot" | "test";
|
|
33
|
+
resource: string;
|
|
20
34
|
service: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function parseUrl<T extends string>(url: string, options?: IParseUrlOptions<T>): {
|
|
37
|
+
network: "production" | "preprod" | "conformance" | "pilot" | "test";
|
|
21
38
|
resource: string;
|
|
39
|
+
service: T;
|
|
22
40
|
};
|
|
23
|
-
export declare function serialize(components: EBSIURIComponents): string;
|
|
24
|
-
export declare function toUrl(uriString: string, options:
|
|
25
|
-
readonly services: SupportedServices;
|
|
26
|
-
readonly hosts: Partial<Record<Network, string>>;
|
|
27
|
-
}): string;
|
|
28
|
-
export declare function fromUrl(url: string, options?: {
|
|
29
|
-
services?: SupportedServices;
|
|
30
|
-
}): string;
|
|
41
|
+
export declare function serialize<T extends string>(components: EBSIURIComponents<T>): string;
|
|
42
|
+
export declare function toUrl<T extends string>(uriString: string, options: IToUrlOptions<T>): string;
|
|
31
43
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAE/C,eAAO,MAAM,QAAQ,oEAMX,CAAC;AAEX,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAE/C,eAAO,MAAM,QAAQ,oEAMX,CAAC;AAEX,eAAO,MAAM,iBAAiB;;;;;;CAMc,CAAC;AAQ7C,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAC1D,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;CACzB;AAGD,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAIxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;CAC1C;AAGD,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAIzD,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;CAC1C;AAGD,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAItD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAKjD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,CAAC,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,IAAI,MAAM,EAAE,CAAC;AAE1C,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,CACtD,WAAW,CAAC,CAAC,CAAC,EACd,cAAc,CACf,CAAC;AAaF,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EACtC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,UAK7B;AAmBD,wBAAgB,KAAK,CAAC,SAAS,EAAE,MAAM;;;;EAgCtC;AAQD,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;;;aA0DP,CAAC;EAExB;AAgBD,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAAC,UAO3E;AAgBD,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,MAAM,CAqBR"}
|
package/dist/index.js
CHANGED
|
@@ -7,20 +7,21 @@ export const NETWORKS = [
|
|
|
7
7
|
"pilot",
|
|
8
8
|
"test",
|
|
9
9
|
];
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
pilot:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
export const NETWORKS_PATTERNS = {
|
|
11
|
+
conformance: /^api-conformance\.ebsi\.eu$/,
|
|
12
|
+
pilot: /^api-pilot\.ebsi\./,
|
|
13
|
+
preprod: /^api-preprod\.ebsi\./,
|
|
14
|
+
production: /^api\.ebsi\./,
|
|
15
|
+
test: /^api-test\.ebsi\.eu$/,
|
|
16
16
|
};
|
|
17
|
-
function
|
|
18
|
-
|
|
17
|
+
export function fromUrl(url, options) {
|
|
18
|
+
const components = parseUrl(url, options);
|
|
19
|
+
return serialize(components);
|
|
19
20
|
}
|
|
20
21
|
export function parse(uriString) {
|
|
21
22
|
const [scheme, firstComponent, secondComponent, ...rest] = uriString.split(":");
|
|
22
23
|
if (scheme !== SCHEME) {
|
|
23
|
-
throw new Error(
|
|
24
|
+
throw new Error(`Invalid URI: scheme must be '${SCHEME}'`);
|
|
24
25
|
}
|
|
25
26
|
const network = isNetwork(firstComponent)
|
|
26
27
|
? firstComponent
|
|
@@ -35,28 +36,11 @@ export function parse(uriString) {
|
|
|
35
36
|
}
|
|
36
37
|
return {
|
|
37
38
|
network,
|
|
38
|
-
service,
|
|
39
39
|
resource,
|
|
40
|
+
service,
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
|
-
export function
|
|
43
|
-
const { network, service, resource } = components;
|
|
44
|
-
const networkName = network === PRODUCTION_NETWORK ? "" : `${network}:`;
|
|
45
|
-
return `${SCHEME}:${networkName}${service}:${resource}`;
|
|
46
|
-
}
|
|
47
|
-
export function toUrl(uriString, options) {
|
|
48
|
-
const { network, service, resource } = parse(uriString);
|
|
49
|
-
const host = options.hosts[network];
|
|
50
|
-
if (!host) {
|
|
51
|
-
throw new Error(`No host for network ${network}`);
|
|
52
|
-
}
|
|
53
|
-
const serviceVersion = options.services[service];
|
|
54
|
-
if (serviceVersion === undefined) {
|
|
55
|
-
throw new Error(`Unsupported service: ${service}`);
|
|
56
|
-
}
|
|
57
|
-
return `https://${host}/${service}/${serviceVersion}${resource}`;
|
|
58
|
-
}
|
|
59
|
-
export function fromUrl(url, options) {
|
|
43
|
+
export function parseUrl(url, options) {
|
|
60
44
|
let urlObject;
|
|
61
45
|
try {
|
|
62
46
|
urlObject = new URL(url);
|
|
@@ -64,34 +48,65 @@ export function fromUrl(url, options) {
|
|
|
64
48
|
catch {
|
|
65
49
|
throw new Error("Invalid URL");
|
|
66
50
|
}
|
|
67
|
-
const { host, pathname, search
|
|
68
|
-
const network =
|
|
51
|
+
const { hash, host, pathname, search } = urlObject;
|
|
52
|
+
const network = NETWORKS.find((n) => NETWORKS_PATTERNS[n].test(host));
|
|
69
53
|
if (!network) {
|
|
70
|
-
throw new Error(`
|
|
54
|
+
throw new Error(`Host ${host} doesn't match any EBSI network`);
|
|
71
55
|
}
|
|
72
56
|
const [service, serviceVersion, ...rest] = pathname
|
|
73
57
|
.split("/")
|
|
74
58
|
.filter(Boolean);
|
|
75
|
-
const resource = rest.join("/");
|
|
76
59
|
if (!service) {
|
|
77
60
|
throw new Error(`No service found in EBSI URI ${url}`);
|
|
78
61
|
}
|
|
79
62
|
if (!serviceVersion) {
|
|
80
63
|
throw new Error(`No service version found in EBSI URI ${url}`);
|
|
81
64
|
}
|
|
65
|
+
if (!isServiceVersion(serviceVersion)) {
|
|
66
|
+
throw new Error(`'${serviceVersion}' is not a valid service version`);
|
|
67
|
+
}
|
|
82
68
|
if (options?.services) {
|
|
83
|
-
const
|
|
84
|
-
if (!
|
|
69
|
+
const services = options.services;
|
|
70
|
+
if (!isSupportedService(service, services)) {
|
|
85
71
|
throw new Error(`Unsupported service '${service}'`);
|
|
86
72
|
}
|
|
73
|
+
const supportedServiceVersion = services[service];
|
|
87
74
|
if (serviceVersion !== supportedServiceVersion) {
|
|
88
75
|
throw new Error(`Unsupported version '${serviceVersion}' of service '${service}'`);
|
|
89
76
|
}
|
|
90
77
|
}
|
|
91
|
-
|
|
78
|
+
const resource = `/${rest.join("/")}${search}${hash}`;
|
|
79
|
+
return {
|
|
92
80
|
network,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
81
|
+
resource,
|
|
82
|
+
service: service,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function serialize(components) {
|
|
86
|
+
const { network, resource, service } = components;
|
|
87
|
+
const networkName = network === PRODUCTION_NETWORK ? "" : `${network}:`;
|
|
88
|
+
return `${SCHEME}:${networkName}${service}:${resource}`;
|
|
89
|
+
}
|
|
90
|
+
export function toUrl(uriString, options) {
|
|
91
|
+
const { network, resource, service } = parse(uriString);
|
|
92
|
+
const host = options.hosts[network];
|
|
93
|
+
if (!host) {
|
|
94
|
+
throw new Error(`No host for network ${network}`);
|
|
95
|
+
}
|
|
96
|
+
const services = options.services;
|
|
97
|
+
if (!isSupportedService(service, services)) {
|
|
98
|
+
throw new Error(`Unsupported service '${service}'`);
|
|
99
|
+
}
|
|
100
|
+
const serviceVersion = services[service];
|
|
101
|
+
return `https://${host}/${service}/${serviceVersion}${resource}`;
|
|
102
|
+
}
|
|
103
|
+
function isNetwork(network) {
|
|
104
|
+
return network !== undefined && NETWORKS.includes(network);
|
|
105
|
+
}
|
|
106
|
+
function isServiceVersion(serviceVersion) {
|
|
107
|
+
return typeof serviceVersion === "string" && /v\d*/.test(serviceVersion);
|
|
108
|
+
}
|
|
109
|
+
function isSupportedService(service, supportedServices) {
|
|
110
|
+
return service !== undefined && service in supportedServices;
|
|
96
111
|
}
|
|
97
112
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC;AAE7B,MAAM,CAAC,MAAM,kBAAkB,GAAG,YAAY,CAAC;AAE/C,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,kBAAkB;IAClB,SAAS;IACT,aAAa;IACb,OAAO;IACP,MAAM;CACE,CAAC;AAEX,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cef-ebsi/ebsi-uri",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-alpha.0",
|
|
4
4
|
"description": "EBSI URIs parser and serializer",
|
|
5
5
|
"license": "EUPL-1.2",
|
|
6
6
|
"publishConfig": {
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"test:e2e": "echo \"Info: there's no e2e tests for @cef-ebsi/ebsi-uri\" && exit 0",
|
|
31
31
|
"test:ci": "yarn test:unit --coverage --reporter=default"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "da5d07e18f31b1e569520c8c58c28d6601a24624"
|
|
34
34
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,26 +10,84 @@ export const NETWORKS = [
|
|
|
10
10
|
"test",
|
|
11
11
|
] as const;
|
|
12
12
|
|
|
13
|
-
export const
|
|
14
|
-
|
|
15
|
-
pilot:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} as const satisfies Record<Network,
|
|
13
|
+
export const NETWORKS_PATTERNS = {
|
|
14
|
+
conformance: /^api-conformance\.ebsi\.eu$/,
|
|
15
|
+
pilot: /^api-pilot\.ebsi\./,
|
|
16
|
+
preprod: /^api-preprod\.ebsi\./,
|
|
17
|
+
production: /^api\.ebsi\./,
|
|
18
|
+
test: /^api-test\.ebsi\.eu$/,
|
|
19
|
+
} as const satisfies Record<Network, RegExp>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Components of an EBSI URI:
|
|
23
|
+
* - a network, e.g. "pilot"
|
|
24
|
+
* - a service, e.g. "trusted-issuers-registry"
|
|
25
|
+
* - a resource path, e.g. "/issuers/did:ebsi:zZeKyEJfUTGwajhNyNX928z?x=y&z=w#abc
|
|
26
|
+
*/
|
|
27
|
+
export interface EBSIURIComponents<T extends string = string> {
|
|
28
|
+
network: Network;
|
|
29
|
+
resource: string;
|
|
30
|
+
service: ServiceName<T>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Options for the `fromUrl()` function */
|
|
34
|
+
export interface IFromUrlOptions<T extends string = string> {
|
|
35
|
+
/**
|
|
36
|
+
* Map of supported services and their versions
|
|
37
|
+
*/
|
|
38
|
+
readonly services?: SupportedServices<T>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Options for the `parseUrl()` function */
|
|
42
|
+
export interface IParseUrlOptions<T extends string = string> {
|
|
43
|
+
/**
|
|
44
|
+
* Map of supported services and their versions
|
|
45
|
+
*/
|
|
46
|
+
readonly services?: SupportedServices<T>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Options for the `toUrl()` function */
|
|
50
|
+
export interface IToUrlOptions<T extends string = string> {
|
|
51
|
+
/**
|
|
52
|
+
* Map of supported networks and their hosts
|
|
53
|
+
*/
|
|
54
|
+
readonly hosts: Partial<Record<Network, string>>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Map of supported services and their versions
|
|
58
|
+
*/
|
|
59
|
+
readonly services: SupportedServices<T>;
|
|
60
|
+
}
|
|
20
61
|
|
|
21
62
|
export type Network = (typeof NETWORKS)[number];
|
|
22
63
|
|
|
23
|
-
export type
|
|
24
|
-
network?: Network;
|
|
25
|
-
service?: string;
|
|
26
|
-
resource?: string;
|
|
27
|
-
};
|
|
64
|
+
export type ServiceName<T extends string = string> = T;
|
|
28
65
|
|
|
29
|
-
export type
|
|
66
|
+
export type ServiceVersion = `v${number}`;
|
|
30
67
|
|
|
31
|
-
|
|
32
|
-
|
|
68
|
+
export type SupportedServices<T extends string> = Record<
|
|
69
|
+
ServiceName<T>,
|
|
70
|
+
ServiceVersion
|
|
71
|
+
>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Transform a URL into an EBSI URI.
|
|
75
|
+
* @example
|
|
76
|
+
* ```js
|
|
77
|
+
* fromUrl("https://api-test.ebsi.eu/did-registry/v4/identifiers/did:ebsi:zx23VhpbGYeqF2MRJk3HzPw")
|
|
78
|
+
* ```
|
|
79
|
+
* returns `ebsi:test:did-registry:/identifiers/did:ebsi:zx23VhpbGYeqF2MRJk3HzPw`
|
|
80
|
+
* @param url - URL to transform
|
|
81
|
+
* @param options - Supported services
|
|
82
|
+
* @returns EBSI URI
|
|
83
|
+
*/
|
|
84
|
+
export function fromUrl<T extends string>(
|
|
85
|
+
url: string,
|
|
86
|
+
options?: IFromUrlOptions<T>,
|
|
87
|
+
) {
|
|
88
|
+
const components = parseUrl(url, options);
|
|
89
|
+
|
|
90
|
+
return serialize(components);
|
|
33
91
|
}
|
|
34
92
|
|
|
35
93
|
/**
|
|
@@ -54,10 +112,10 @@ export function parse(uriString: string) {
|
|
|
54
112
|
uriString.split(":");
|
|
55
113
|
|
|
56
114
|
if (scheme !== SCHEME) {
|
|
57
|
-
throw new Error(
|
|
115
|
+
throw new Error(`Invalid URI: scheme must be '${SCHEME}'`);
|
|
58
116
|
}
|
|
59
117
|
|
|
60
|
-
const network
|
|
118
|
+
const network = isNetwork(firstComponent)
|
|
61
119
|
? firstComponent
|
|
62
120
|
: PRODUCTION_NETWORK;
|
|
63
121
|
|
|
@@ -78,9 +136,79 @@ export function parse(uriString: string) {
|
|
|
78
136
|
// Return EBSI URI components
|
|
79
137
|
return {
|
|
80
138
|
network,
|
|
139
|
+
resource,
|
|
81
140
|
service,
|
|
141
|
+
} satisfies EBSIURIComponents<string>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Extract EBSI URI components from URL.
|
|
146
|
+
* @param url - The URL to parse.
|
|
147
|
+
* @param options - Supported services
|
|
148
|
+
* @returns EBSI URI components
|
|
149
|
+
*/
|
|
150
|
+
export function parseUrl<T extends string>(
|
|
151
|
+
url: string,
|
|
152
|
+
options?: IParseUrlOptions<T>,
|
|
153
|
+
) {
|
|
154
|
+
// Parse URL
|
|
155
|
+
let urlObject: URL;
|
|
156
|
+
try {
|
|
157
|
+
urlObject = new URL(url);
|
|
158
|
+
} catch {
|
|
159
|
+
throw new Error("Invalid URL");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const { hash, host, pathname, search } = urlObject;
|
|
163
|
+
|
|
164
|
+
// Identify network
|
|
165
|
+
const network = NETWORKS.find((n) => NETWORKS_PATTERNS[n].test(host));
|
|
166
|
+
|
|
167
|
+
if (!network) {
|
|
168
|
+
throw new Error(`Host ${host} doesn't match any EBSI network`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Extract service and resource from pathname: /<service>/<serviceVersion>/<resource>
|
|
172
|
+
const [service, serviceVersion, ...rest] = pathname
|
|
173
|
+
.split("/")
|
|
174
|
+
.filter(Boolean); // Remove empty path segments
|
|
175
|
+
|
|
176
|
+
if (!service) {
|
|
177
|
+
throw new Error(`No service found in EBSI URI ${url}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!serviceVersion) {
|
|
181
|
+
throw new Error(`No service version found in EBSI URI ${url}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!isServiceVersion(serviceVersion)) {
|
|
185
|
+
throw new Error(`'${serviceVersion}' is not a valid service version`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check if the service and its version are supported
|
|
189
|
+
if (options?.services) {
|
|
190
|
+
const services = options.services;
|
|
191
|
+
|
|
192
|
+
if (!isSupportedService(service, services)) {
|
|
193
|
+
throw new Error(`Unsupported service '${service}'`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const supportedServiceVersion = services[service];
|
|
197
|
+
|
|
198
|
+
if (serviceVersion !== supportedServiceVersion) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`Unsupported version '${serviceVersion}' of service '${service}'`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const resource = `/${rest.join("/")}${search}${hash}`;
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
network,
|
|
82
209
|
resource,
|
|
83
|
-
|
|
210
|
+
service: service as T,
|
|
211
|
+
} satisfies EBSIURIComponents<T>;
|
|
84
212
|
}
|
|
85
213
|
|
|
86
214
|
/**
|
|
@@ -97,8 +225,8 @@ export function parse(uriString: string) {
|
|
|
97
225
|
* @param components - EBSI URI components
|
|
98
226
|
* @returns EBSI URI
|
|
99
227
|
*/
|
|
100
|
-
export function serialize(components: EBSIURIComponents) {
|
|
101
|
-
const { network,
|
|
228
|
+
export function serialize<T extends string>(components: EBSIURIComponents<T>) {
|
|
229
|
+
const { network, resource, service } = components;
|
|
102
230
|
|
|
103
231
|
const networkName = network === PRODUCTION_NETWORK ? "" : `${network}:`;
|
|
104
232
|
|
|
@@ -120,21 +248,11 @@ export function serialize(components: EBSIURIComponents) {
|
|
|
120
248
|
* @param options - Supported services and hosts
|
|
121
249
|
* @returns URL
|
|
122
250
|
*/
|
|
123
|
-
export function toUrl(
|
|
251
|
+
export function toUrl<T extends string>(
|
|
124
252
|
uriString: string,
|
|
125
|
-
options:
|
|
126
|
-
/**
|
|
127
|
-
* Map of supported services and their versions
|
|
128
|
-
*/
|
|
129
|
-
readonly services: SupportedServices;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Map of supported networks and their hosts
|
|
133
|
-
*/
|
|
134
|
-
readonly hosts: Partial<Record<Network, string>>;
|
|
135
|
-
},
|
|
253
|
+
options: IToUrlOptions<T>,
|
|
136
254
|
): string {
|
|
137
|
-
const { network,
|
|
255
|
+
const { network, resource, service } = parse(uriString);
|
|
138
256
|
|
|
139
257
|
// Replace host
|
|
140
258
|
const host = options.hosts[network];
|
|
@@ -143,88 +261,48 @@ export function toUrl(
|
|
|
143
261
|
throw new Error(`No host for network ${network}`);
|
|
144
262
|
}
|
|
145
263
|
|
|
146
|
-
|
|
147
|
-
const serviceVersion = options.services[service];
|
|
264
|
+
const services = options.services;
|
|
148
265
|
|
|
149
|
-
if (
|
|
150
|
-
throw new Error(`Unsupported service
|
|
266
|
+
if (!isSupportedService(service, services)) {
|
|
267
|
+
throw new Error(`Unsupported service '${service}'`);
|
|
151
268
|
}
|
|
152
269
|
|
|
270
|
+
// Get service version
|
|
271
|
+
const serviceVersion = services[service];
|
|
272
|
+
|
|
153
273
|
// Return URL
|
|
154
274
|
return `https://${host}/${service}/${serviceVersion}${resource}`;
|
|
155
275
|
}
|
|
156
276
|
|
|
157
277
|
/**
|
|
158
|
-
*
|
|
159
|
-
* @
|
|
160
|
-
*
|
|
161
|
-
* fromUrl("https://api-test.ebsi.eu/did-registry/v4/identifiers/did:ebsi:zx23VhpbGYeqF2MRJk3HzPw")
|
|
162
|
-
* ```
|
|
163
|
-
* returns `ebsi:test:did-registry:/identifiers/did:ebsi:zx23VhpbGYeqF2MRJk3HzPw`
|
|
164
|
-
* @param url - URL to transform
|
|
165
|
-
* @param options - Supported services
|
|
166
|
-
* @returns EBSI URI
|
|
278
|
+
* Typeguard that checks if the given string is a supported network.
|
|
279
|
+
* @param network - String to check
|
|
280
|
+
* @returns True if network is a valid network name
|
|
167
281
|
*/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Map of supported services and their versions
|
|
173
|
-
*/
|
|
174
|
-
services?: SupportedServices;
|
|
175
|
-
},
|
|
176
|
-
) {
|
|
177
|
-
// Parse URL
|
|
178
|
-
let urlObject: URL;
|
|
179
|
-
try {
|
|
180
|
-
urlObject = new URL(url);
|
|
181
|
-
} catch {
|
|
182
|
-
throw new Error("Invalid URL");
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const { host, pathname, search, hash } = urlObject;
|
|
186
|
-
|
|
187
|
-
// Identify network
|
|
188
|
-
const network = (Object.keys(HOSTS) as (keyof typeof HOSTS)[]).find((n) =>
|
|
189
|
-
host.includes(HOSTS[n]),
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
if (!network) {
|
|
193
|
-
throw new Error(`No network configured for host ${host}`);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Extract service and resource from pathname: /<service>/<serviceVersion>/<resource>
|
|
197
|
-
const [service, serviceVersion, ...rest] = pathname
|
|
198
|
-
.split("/")
|
|
199
|
-
.filter(Boolean); // Remove empty path segments
|
|
200
|
-
const resource = rest.join("/");
|
|
201
|
-
|
|
202
|
-
if (!service) {
|
|
203
|
-
throw new Error(`No service found in EBSI URI ${url}`);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (!serviceVersion) {
|
|
207
|
-
throw new Error(`No service version found in EBSI URI ${url}`);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Check if the service and its version are supported
|
|
211
|
-
if (options?.services) {
|
|
212
|
-
const supportedServiceVersion = options.services[service];
|
|
213
|
-
|
|
214
|
-
if (!supportedServiceVersion) {
|
|
215
|
-
throw new Error(`Unsupported service '${service}'`);
|
|
216
|
-
}
|
|
282
|
+
function isNetwork(network: string | undefined): network is Network {
|
|
283
|
+
return network !== undefined && NETWORKS.includes(network as Network);
|
|
284
|
+
}
|
|
217
285
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Typeguard that checks if the given string is a supported service version.
|
|
288
|
+
* @param serviceVersion - String to check
|
|
289
|
+
* @returns True if serviceVersion is a valid service version
|
|
290
|
+
*/
|
|
291
|
+
function isServiceVersion(
|
|
292
|
+
serviceVersion: string | undefined,
|
|
293
|
+
): serviceVersion is ServiceVersion {
|
|
294
|
+
return typeof serviceVersion === "string" && /v\d*/.test(serviceVersion);
|
|
295
|
+
}
|
|
224
296
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Typeguard that checks if the given string is a supported service.
|
|
299
|
+
* @param service - String to check
|
|
300
|
+
* @param supportedServices - Supported services
|
|
301
|
+
* @returns True if service is a supported service
|
|
302
|
+
*/
|
|
303
|
+
function isSupportedService<T extends string>(
|
|
304
|
+
service: string | undefined,
|
|
305
|
+
supportedServices: SupportedServices<T>,
|
|
306
|
+
): service is T {
|
|
307
|
+
return service !== undefined && service in supportedServices;
|
|
230
308
|
}
|