@cef-ebsi/ebsi-uri 0.1.1-alpha.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 +28 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -37
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +153 -80
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,33 +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 conformance:
|
|
6
|
-
readonly pilot:
|
|
7
|
-
readonly preprod:
|
|
8
|
-
readonly production:
|
|
9
|
-
readonly test:
|
|
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 {
|
|
12
|
-
network
|
|
13
|
-
resource
|
|
14
|
-
service
|
|
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>;
|
|
15
18
|
}
|
|
16
|
-
export interface
|
|
17
|
-
services?: SupportedServices
|
|
19
|
+
export interface IParseUrlOptions<T extends string = string> {
|
|
20
|
+
readonly services?: SupportedServices<T>;
|
|
18
21
|
}
|
|
19
|
-
export interface IToUrlOptions {
|
|
22
|
+
export interface IToUrlOptions<T extends string = string> {
|
|
20
23
|
readonly hosts: Partial<Record<Network, string>>;
|
|
21
|
-
readonly services: SupportedServices
|
|
24
|
+
readonly services: SupportedServices<T>;
|
|
22
25
|
}
|
|
23
26
|
export type Network = (typeof NETWORKS)[number];
|
|
24
|
-
export type
|
|
25
|
-
export
|
|
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;
|
|
26
31
|
export declare function parse(uriString: string): {
|
|
27
32
|
network: "production" | "preprod" | "conformance" | "pilot" | "test";
|
|
28
33
|
resource: string;
|
|
29
34
|
service: string;
|
|
30
35
|
};
|
|
31
|
-
export declare function
|
|
32
|
-
|
|
36
|
+
export declare function parseUrl<T extends string>(url: string, options?: IParseUrlOptions<T>): {
|
|
37
|
+
network: "production" | "preprod" | "conformance" | "pilot" | "test";
|
|
38
|
+
resource: string;
|
|
39
|
+
service: T;
|
|
40
|
+
};
|
|
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;
|
|
33
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,14 +7,40 @@ export const NETWORKS = [
|
|
|
7
7
|
"pilot",
|
|
8
8
|
"test",
|
|
9
9
|
];
|
|
10
|
-
export const
|
|
11
|
-
conformance:
|
|
12
|
-
pilot:
|
|
13
|
-
preprod:
|
|
14
|
-
production:
|
|
15
|
-
test:
|
|
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
17
|
export function fromUrl(url, options) {
|
|
18
|
+
const components = parseUrl(url, options);
|
|
19
|
+
return serialize(components);
|
|
20
|
+
}
|
|
21
|
+
export function parse(uriString) {
|
|
22
|
+
const [scheme, firstComponent, secondComponent, ...rest] = uriString.split(":");
|
|
23
|
+
if (scheme !== SCHEME) {
|
|
24
|
+
throw new Error(`Invalid URI: scheme must be '${SCHEME}'`);
|
|
25
|
+
}
|
|
26
|
+
const network = isNetwork(firstComponent)
|
|
27
|
+
? firstComponent
|
|
28
|
+
: PRODUCTION_NETWORK;
|
|
29
|
+
const service = isNetwork(firstComponent) ? secondComponent : firstComponent;
|
|
30
|
+
if (!service || service.startsWith("/")) {
|
|
31
|
+
throw new Error(`No service found in EBSI URI '${uriString}'`);
|
|
32
|
+
}
|
|
33
|
+
const resource = (isNetwork(firstComponent) ? rest : [secondComponent, ...rest]).join(":");
|
|
34
|
+
if (!resource) {
|
|
35
|
+
throw new Error(`No resource found in EBSI URI '${uriString}'`);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
network,
|
|
39
|
+
resource,
|
|
40
|
+
service,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function parseUrl(url, options) {
|
|
18
44
|
let urlObject;
|
|
19
45
|
try {
|
|
20
46
|
urlObject = new URL(url);
|
|
@@ -23,55 +49,37 @@ export function fromUrl(url, options) {
|
|
|
23
49
|
throw new Error("Invalid URL");
|
|
24
50
|
}
|
|
25
51
|
const { hash, host, pathname, search } = urlObject;
|
|
26
|
-
const network =
|
|
52
|
+
const network = NETWORKS.find((n) => NETWORKS_PATTERNS[n].test(host));
|
|
27
53
|
if (!network) {
|
|
28
|
-
throw new Error(`
|
|
54
|
+
throw new Error(`Host ${host} doesn't match any EBSI network`);
|
|
29
55
|
}
|
|
30
56
|
const [service, serviceVersion, ...rest] = pathname
|
|
31
57
|
.split("/")
|
|
32
58
|
.filter(Boolean);
|
|
33
|
-
const resource = rest.join("/");
|
|
34
59
|
if (!service) {
|
|
35
60
|
throw new Error(`No service found in EBSI URI ${url}`);
|
|
36
61
|
}
|
|
37
62
|
if (!serviceVersion) {
|
|
38
63
|
throw new Error(`No service version found in EBSI URI ${url}`);
|
|
39
64
|
}
|
|
65
|
+
if (!isServiceVersion(serviceVersion)) {
|
|
66
|
+
throw new Error(`'${serviceVersion}' is not a valid service version`);
|
|
67
|
+
}
|
|
40
68
|
if (options?.services) {
|
|
41
|
-
const
|
|
42
|
-
if (!
|
|
69
|
+
const services = options.services;
|
|
70
|
+
if (!isSupportedService(service, services)) {
|
|
43
71
|
throw new Error(`Unsupported service '${service}'`);
|
|
44
72
|
}
|
|
73
|
+
const supportedServiceVersion = services[service];
|
|
45
74
|
if (serviceVersion !== supportedServiceVersion) {
|
|
46
75
|
throw new Error(`Unsupported version '${serviceVersion}' of service '${service}'`);
|
|
47
76
|
}
|
|
48
77
|
}
|
|
49
|
-
|
|
50
|
-
network,
|
|
51
|
-
resource: `/${resource}${search}${hash}`,
|
|
52
|
-
service,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
export function parse(uriString) {
|
|
56
|
-
const [scheme, firstComponent, secondComponent, ...rest] = uriString.split(":");
|
|
57
|
-
if (scheme !== SCHEME) {
|
|
58
|
-
throw new Error("Invalid URI: scheme must be 'ebsi'");
|
|
59
|
-
}
|
|
60
|
-
const network = isNetwork(firstComponent)
|
|
61
|
-
? firstComponent
|
|
62
|
-
: PRODUCTION_NETWORK;
|
|
63
|
-
const service = isNetwork(firstComponent) ? secondComponent : firstComponent;
|
|
64
|
-
if (!service || service.startsWith("/")) {
|
|
65
|
-
throw new Error(`No service found in EBSI URI '${uriString}'`);
|
|
66
|
-
}
|
|
67
|
-
const resource = (isNetwork(firstComponent) ? rest : [secondComponent, ...rest]).join(":");
|
|
68
|
-
if (!resource) {
|
|
69
|
-
throw new Error(`No resource found in EBSI URI '${uriString}'`);
|
|
70
|
-
}
|
|
78
|
+
const resource = `/${rest.join("/")}${search}${hash}`;
|
|
71
79
|
return {
|
|
72
80
|
network,
|
|
73
81
|
resource,
|
|
74
|
-
service,
|
|
82
|
+
service: service,
|
|
75
83
|
};
|
|
76
84
|
}
|
|
77
85
|
export function serialize(components) {
|
|
@@ -85,13 +93,20 @@ export function toUrl(uriString, options) {
|
|
|
85
93
|
if (!host) {
|
|
86
94
|
throw new Error(`No host for network ${network}`);
|
|
87
95
|
}
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
throw new Error(`Unsupported service
|
|
96
|
+
const services = options.services;
|
|
97
|
+
if (!isSupportedService(service, services)) {
|
|
98
|
+
throw new Error(`Unsupported service '${service}'`);
|
|
91
99
|
}
|
|
100
|
+
const serviceVersion = services[service];
|
|
92
101
|
return `https://${host}/${service}/${serviceVersion}${resource}`;
|
|
93
102
|
}
|
|
94
103
|
function isNetwork(network) {
|
|
95
104
|
return network !== undefined && NETWORKS.includes(network);
|
|
96
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;
|
|
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,30 +10,44 @@ export const NETWORKS = [
|
|
|
10
10
|
"test",
|
|
11
11
|
] as const;
|
|
12
12
|
|
|
13
|
-
export const
|
|
14
|
-
conformance:
|
|
15
|
-
pilot:
|
|
16
|
-
preprod:
|
|
17
|
-
production:
|
|
18
|
-
test:
|
|
19
|
-
} as const satisfies Record<Network,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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>;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
/** Options for the `fromUrl()` function */
|
|
28
|
-
export interface IFromUrlOptions {
|
|
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> {
|
|
29
43
|
/**
|
|
30
44
|
* Map of supported services and their versions
|
|
31
45
|
*/
|
|
32
|
-
services?: SupportedServices
|
|
46
|
+
readonly services?: SupportedServices<T>;
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
/** Options for the `toUrl()` function */
|
|
36
|
-
export interface IToUrlOptions {
|
|
50
|
+
export interface IToUrlOptions<T extends string = string> {
|
|
37
51
|
/**
|
|
38
52
|
* Map of supported networks and their hosts
|
|
39
53
|
*/
|
|
@@ -42,12 +56,19 @@ export interface IToUrlOptions {
|
|
|
42
56
|
/**
|
|
43
57
|
* Map of supported services and their versions
|
|
44
58
|
*/
|
|
45
|
-
readonly services: SupportedServices
|
|
59
|
+
readonly services: SupportedServices<T>;
|
|
46
60
|
}
|
|
47
61
|
|
|
48
62
|
export type Network = (typeof NETWORKS)[number];
|
|
49
63
|
|
|
50
|
-
export type
|
|
64
|
+
export type ServiceName<T extends string = string> = T;
|
|
65
|
+
|
|
66
|
+
export type ServiceVersion = `v${number}`;
|
|
67
|
+
|
|
68
|
+
export type SupportedServices<T extends string> = Record<
|
|
69
|
+
ServiceName<T>,
|
|
70
|
+
ServiceVersion
|
|
71
|
+
>;
|
|
51
72
|
|
|
52
73
|
/**
|
|
53
74
|
* Transform a URL into an EBSI URI.
|
|
@@ -60,60 +81,13 @@ export type SupportedServices = Record<string, string>;
|
|
|
60
81
|
* @param options - Supported services
|
|
61
82
|
* @returns EBSI URI
|
|
62
83
|
*/
|
|
63
|
-
export function fromUrl
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
} catch {
|
|
69
|
-
throw new Error("Invalid URL");
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const { hash, host, pathname, search } = urlObject;
|
|
84
|
+
export function fromUrl<T extends string>(
|
|
85
|
+
url: string,
|
|
86
|
+
options?: IFromUrlOptions<T>,
|
|
87
|
+
) {
|
|
88
|
+
const components = parseUrl(url, options);
|
|
73
89
|
|
|
74
|
-
|
|
75
|
-
const network = (Object.keys(HOSTS) as (keyof typeof HOSTS)[]).find((n) =>
|
|
76
|
-
host.includes(HOSTS[n]),
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
if (!network) {
|
|
80
|
-
throw new Error(`No network configured for host ${host}`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Extract service and resource from pathname: /<service>/<serviceVersion>/<resource>
|
|
84
|
-
const [service, serviceVersion, ...rest] = pathname
|
|
85
|
-
.split("/")
|
|
86
|
-
.filter(Boolean); // Remove empty path segments
|
|
87
|
-
const resource = rest.join("/");
|
|
88
|
-
|
|
89
|
-
if (!service) {
|
|
90
|
-
throw new Error(`No service found in EBSI URI ${url}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (!serviceVersion) {
|
|
94
|
-
throw new Error(`No service version found in EBSI URI ${url}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check if the service and its version are supported
|
|
98
|
-
if (options?.services) {
|
|
99
|
-
const supportedServiceVersion = options.services[service];
|
|
100
|
-
|
|
101
|
-
if (!supportedServiceVersion) {
|
|
102
|
-
throw new Error(`Unsupported service '${service}'`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (serviceVersion !== supportedServiceVersion) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
`Unsupported version '${serviceVersion}' of service '${service}'`,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return serialize({
|
|
113
|
-
network,
|
|
114
|
-
resource: `/${resource}${search}${hash}`,
|
|
115
|
-
service,
|
|
116
|
-
});
|
|
90
|
+
return serialize(components);
|
|
117
91
|
}
|
|
118
92
|
|
|
119
93
|
/**
|
|
@@ -138,10 +112,10 @@ export function parse(uriString: string) {
|
|
|
138
112
|
uriString.split(":");
|
|
139
113
|
|
|
140
114
|
if (scheme !== SCHEME) {
|
|
141
|
-
throw new Error(
|
|
115
|
+
throw new Error(`Invalid URI: scheme must be '${SCHEME}'`);
|
|
142
116
|
}
|
|
143
117
|
|
|
144
|
-
const network
|
|
118
|
+
const network = isNetwork(firstComponent)
|
|
145
119
|
? firstComponent
|
|
146
120
|
: PRODUCTION_NETWORK;
|
|
147
121
|
|
|
@@ -164,7 +138,77 @@ export function parse(uriString: string) {
|
|
|
164
138
|
network,
|
|
165
139
|
resource,
|
|
166
140
|
service,
|
|
167
|
-
} satisfies EBSIURIComponents
|
|
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,
|
|
209
|
+
resource,
|
|
210
|
+
service: service as T,
|
|
211
|
+
} satisfies EBSIURIComponents<T>;
|
|
168
212
|
}
|
|
169
213
|
|
|
170
214
|
/**
|
|
@@ -181,7 +225,7 @@ export function parse(uriString: string) {
|
|
|
181
225
|
* @param components - EBSI URI components
|
|
182
226
|
* @returns EBSI URI
|
|
183
227
|
*/
|
|
184
|
-
export function serialize(components: EBSIURIComponents) {
|
|
228
|
+
export function serialize<T extends string>(components: EBSIURIComponents<T>) {
|
|
185
229
|
const { network, resource, service } = components;
|
|
186
230
|
|
|
187
231
|
const networkName = network === PRODUCTION_NETWORK ? "" : `${network}:`;
|
|
@@ -204,7 +248,10 @@ export function serialize(components: EBSIURIComponents) {
|
|
|
204
248
|
* @param options - Supported services and hosts
|
|
205
249
|
* @returns URL
|
|
206
250
|
*/
|
|
207
|
-
export function toUrl
|
|
251
|
+
export function toUrl<T extends string>(
|
|
252
|
+
uriString: string,
|
|
253
|
+
options: IToUrlOptions<T>,
|
|
254
|
+
): string {
|
|
208
255
|
const { network, resource, service } = parse(uriString);
|
|
209
256
|
|
|
210
257
|
// Replace host
|
|
@@ -214,13 +261,15 @@ export function toUrl(uriString: string, options: IToUrlOptions): string {
|
|
|
214
261
|
throw new Error(`No host for network ${network}`);
|
|
215
262
|
}
|
|
216
263
|
|
|
217
|
-
|
|
218
|
-
const serviceVersion = options.services[service];
|
|
264
|
+
const services = options.services;
|
|
219
265
|
|
|
220
|
-
if (
|
|
221
|
-
throw new Error(`Unsupported service
|
|
266
|
+
if (!isSupportedService(service, services)) {
|
|
267
|
+
throw new Error(`Unsupported service '${service}'`);
|
|
222
268
|
}
|
|
223
269
|
|
|
270
|
+
// Get service version
|
|
271
|
+
const serviceVersion = services[service];
|
|
272
|
+
|
|
224
273
|
// Return URL
|
|
225
274
|
return `https://${host}/${service}/${serviceVersion}${resource}`;
|
|
226
275
|
}
|
|
@@ -228,8 +277,32 @@ export function toUrl(uriString: string, options: IToUrlOptions): string {
|
|
|
228
277
|
/**
|
|
229
278
|
* Typeguard that checks if the given string is a supported network.
|
|
230
279
|
* @param network - String to check
|
|
231
|
-
* @returns
|
|
280
|
+
* @returns True if network is a valid network name
|
|
232
281
|
*/
|
|
233
282
|
function isNetwork(network: string | undefined): network is Network {
|
|
234
283
|
return network !== undefined && NETWORKS.includes(network as Network);
|
|
235
284
|
}
|
|
285
|
+
|
|
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
|
+
}
|
|
296
|
+
|
|
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;
|
|
308
|
+
}
|