@coffre/client 0.1.5 → 0.1.7
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-BJZEgxE8.d.ts +1762 -0
- package/dist/index.d.ts +2 -1722
- package/dist/index.js +3 -1
- package/dist/routes.d.ts +41 -0
- package/dist/routes.js +102 -0
- package/package.json +6 -1
- package/src/api.ts +21 -0
- package/src/index.ts +2 -0
- package/src/routes.ts +115 -0
package/dist/index.js
CHANGED
|
@@ -216,7 +216,9 @@ set: (member, access) => call("PATCH /access/:member", { member }, access) },
|
|
|
216
216
|
},
|
|
217
217
|
audit: {
|
|
218
218
|
list: (query = {}) => call("GET /audit", {}, query),
|
|
219
|
-
verify: () => call("GET /audit/verification", {})
|
|
219
|
+
verify: () => call("GET /audit/verification", {}),
|
|
220
|
+
/** What an escrowed key is checked against: public material, owners and root admins only. */
|
|
221
|
+
keys: () => call("GET /audit/keys", {})
|
|
220
222
|
}
|
|
221
223
|
};
|
|
222
224
|
}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { a as Params, o as RouteInput, s as RouteKey } from "./index-BJZEgxE8.js";
|
|
2
|
+
//#region src/routes.d.ts
|
|
3
|
+
export type GetKey = Extract<RouteKey, `GET ${string}`>;
|
|
4
|
+
/** Each GET route, with what to ask it. */
|
|
5
|
+
export type GetCalls = { [K in GetKey]: {
|
|
6
|
+
params: Params<K>;
|
|
7
|
+
input?: RouteInput<K>;
|
|
8
|
+
}[]; };
|
|
9
|
+
/** What the GET routes are asked about: places, secrets and members to name. */
|
|
10
|
+
export type Subjects = {
|
|
11
|
+
places: {
|
|
12
|
+
project: string;
|
|
13
|
+
environment: string;
|
|
14
|
+
}[];
|
|
15
|
+
/** Secrets whose history to ask for. */
|
|
16
|
+
secrets: {
|
|
17
|
+
project: string;
|
|
18
|
+
environment: string;
|
|
19
|
+
key: string;
|
|
20
|
+
}[];
|
|
21
|
+
/** `user:…` and `token:…`. */
|
|
22
|
+
members: string[];
|
|
23
|
+
/** Tokens whose credentials to list. */
|
|
24
|
+
services: string[];
|
|
25
|
+
};
|
|
26
|
+
export declare function getCalls({ places, secrets, members, services }: Subjects): GetCalls;
|
|
27
|
+
/** Each GET call as a URL, with its parameters and query filled in. */
|
|
28
|
+
export declare function getUrls(origin: string, calls: GetCalls): {
|
|
29
|
+
key: GetKey;
|
|
30
|
+
url: string;
|
|
31
|
+
}[];
|
|
32
|
+
/**
|
|
33
|
+
* Every route as a method and a URL, each parameter a made-up value: what
|
|
34
|
+
* a caller with no right to any of them would send.
|
|
35
|
+
*/
|
|
36
|
+
export declare function everyRoute(origin: string): {
|
|
37
|
+
key: RouteKey;
|
|
38
|
+
method: string;
|
|
39
|
+
url: string;
|
|
40
|
+
}[];
|
|
41
|
+
//#endregion
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//#region src/routes.ts
|
|
2
|
+
function getCalls({ places, secrets, members, services }) {
|
|
3
|
+
const first = places[0];
|
|
4
|
+
return {
|
|
5
|
+
"GET /me": [{ params: {} }],
|
|
6
|
+
"GET /projects": [{ params: {} }],
|
|
7
|
+
"GET /secrets/:project/:environment": places.map((params) => ({ params })),
|
|
8
|
+
"GET /secrets/:project/:environment/:key/versions": secrets.map((params) => ({ params })),
|
|
9
|
+
"GET /members": [{ params: {} }, ...first === void 0 ? [] : [{
|
|
10
|
+
params: {},
|
|
11
|
+
input: { path: `${first.project}/${first.environment}` }
|
|
12
|
+
}]],
|
|
13
|
+
"GET /members/:member": members.map((member) => ({ params: { member } })),
|
|
14
|
+
"GET /members/:member/tokens": services.map((member) => ({ params: { member } })),
|
|
15
|
+
"GET /sessions": [{ params: {} }],
|
|
16
|
+
"GET /identities": [{ params: {} }],
|
|
17
|
+
"GET /device-logins/:code": [{ params: { code: "BCDF-GHJK" } }],
|
|
18
|
+
"GET /syncs/providers": [{ params: {} }],
|
|
19
|
+
"GET /syncs/:project/:environment": places.map((params) => ({ params })),
|
|
20
|
+
"GET /audit": [{
|
|
21
|
+
params: {},
|
|
22
|
+
input: { limit: 500 }
|
|
23
|
+
}, {
|
|
24
|
+
params: {},
|
|
25
|
+
input: {
|
|
26
|
+
limit: 500,
|
|
27
|
+
detail: "1"
|
|
28
|
+
}
|
|
29
|
+
}],
|
|
30
|
+
"GET /audit/verification": [{ params: {} }],
|
|
31
|
+
"GET /audit/keys": [{ params: {} }]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Each GET call as a URL, with its parameters and query filled in. */
|
|
35
|
+
function getUrls(origin, calls) {
|
|
36
|
+
const urls = [];
|
|
37
|
+
for (const [key, asks] of Object.entries(calls)) for (const { params, input } of asks) urls.push({
|
|
38
|
+
key,
|
|
39
|
+
url: address(origin, key, params, input)
|
|
40
|
+
});
|
|
41
|
+
return urls;
|
|
42
|
+
}
|
|
43
|
+
/** Every route, whatever its method. */
|
|
44
|
+
const EVERY_ROUTE = {
|
|
45
|
+
"GET /me": true,
|
|
46
|
+
"GET /projects": true,
|
|
47
|
+
"PUT /projects/:project": true,
|
|
48
|
+
"PATCH /projects/:project": true,
|
|
49
|
+
"PUT /projects/:project/:environment": true,
|
|
50
|
+
"PATCH /projects/:project/:environment": true,
|
|
51
|
+
"GET /secrets/:project/:environment": true,
|
|
52
|
+
"PATCH /secrets/:project/:environment": true,
|
|
53
|
+
"PATCH /secrets/:project/:environment/:key": true,
|
|
54
|
+
"GET /secrets/:project/:environment/:key/versions": true,
|
|
55
|
+
"POST /secrets/:project/:environment/:key/restore": true,
|
|
56
|
+
"POST /reveals": true,
|
|
57
|
+
"GET /members": true,
|
|
58
|
+
"GET /members/:member": true,
|
|
59
|
+
"PUT /members/:member": true,
|
|
60
|
+
"DELETE /members/:member": true,
|
|
61
|
+
"GET /members/:member/tokens": true,
|
|
62
|
+
"POST /members/:member/tokens": true,
|
|
63
|
+
"DELETE /members/:member/tokens/:id": true,
|
|
64
|
+
"PATCH /access/:member": true,
|
|
65
|
+
"GET /sessions": true,
|
|
66
|
+
"DELETE /sessions/:id": true,
|
|
67
|
+
"GET /identities": true,
|
|
68
|
+
"DELETE /identities/:id": true,
|
|
69
|
+
"GET /device-logins/:code": true,
|
|
70
|
+
"POST /device-logins/:code": true,
|
|
71
|
+
"GET /syncs/providers": true,
|
|
72
|
+
"GET /syncs/:project/:environment": true,
|
|
73
|
+
"POST /syncs/:project/:environment": true,
|
|
74
|
+
"PATCH /syncs/by-id/:id": true,
|
|
75
|
+
"DELETE /syncs/by-id/:id": true,
|
|
76
|
+
"POST /syncs/by-id/:id/runs": true,
|
|
77
|
+
"GET /audit": true,
|
|
78
|
+
"GET /audit/verification": true,
|
|
79
|
+
"GET /audit/keys": true
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Every route as a method and a URL, each parameter a made-up value: what
|
|
83
|
+
* a caller with no right to any of them would send.
|
|
84
|
+
*/
|
|
85
|
+
function everyRoute(origin) {
|
|
86
|
+
return Object.keys(EVERY_ROUTE).map((key) => {
|
|
87
|
+
const [method, pattern] = key.split(" ");
|
|
88
|
+
return {
|
|
89
|
+
key,
|
|
90
|
+
method,
|
|
91
|
+
url: `${origin}/api${pattern.replace(/:(\w+)/g, (_, name) => encodeURIComponent(name === "member" ? "token:conformance-nobody" : "conformance-nobody"))}`
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function address(origin, key, params, input) {
|
|
96
|
+
const path = key.slice(4).replace(/:(\w+)/g, (_, name) => encodeURIComponent(params[name]));
|
|
97
|
+
const url = new URL(`${origin}/api${path}`);
|
|
98
|
+
for (const [name, value] of Object.entries(input ?? {})) url.searchParams.set(name, String(value));
|
|
99
|
+
return url.href;
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { everyRoute, getCalls, getUrls };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffre/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "The coffre API as typed function calls.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"default": "./dist/index.js"
|
|
24
24
|
},
|
|
25
|
+
"./routes": {
|
|
26
|
+
"coffre:source": "./src/routes.ts",
|
|
27
|
+
"types": "./dist/routes.d.ts",
|
|
28
|
+
"default": "./dist/routes.js"
|
|
29
|
+
},
|
|
25
30
|
"./package.json": "./package.json"
|
|
26
31
|
},
|
|
27
32
|
"devDependencies": {
|
package/src/api.ts
CHANGED
|
@@ -726,6 +726,27 @@ export type Api = {
|
|
|
726
726
|
reason: string;
|
|
727
727
|
};
|
|
728
728
|
};
|
|
729
|
+
"GET /audit/keys": {
|
|
730
|
+
input: undefined;
|
|
731
|
+
output: {
|
|
732
|
+
app: {
|
|
733
|
+
keyId: string;
|
|
734
|
+
};
|
|
735
|
+
vault: {
|
|
736
|
+
current: {
|
|
737
|
+
vaultId: string;
|
|
738
|
+
provider: string;
|
|
739
|
+
};
|
|
740
|
+
checks: {
|
|
741
|
+
seq: number;
|
|
742
|
+
vaultId: string;
|
|
743
|
+
provider: string;
|
|
744
|
+
version: string;
|
|
745
|
+
wrapped: string;
|
|
746
|
+
}[];
|
|
747
|
+
};
|
|
748
|
+
};
|
|
749
|
+
};
|
|
729
750
|
};
|
|
730
751
|
|
|
731
752
|
export type AccessValue = "access-manager" | "auditor" | "developer" | "maintainer" | "owner" | "viewer" | null | {
|
package/src/index.ts
CHANGED
|
@@ -260,6 +260,8 @@ export function createClient(options: ClientOptions) {
|
|
|
260
260
|
audit: {
|
|
261
261
|
list: (query: RouteInput<'GET /audit'> = {}) => call('GET /audit', {}, query),
|
|
262
262
|
verify: () => call('GET /audit/verification', {}),
|
|
263
|
+
/** What an escrowed key is checked against: public material, owners and root admins only. */
|
|
264
|
+
keys: () => call('GET /audit/keys', {}),
|
|
263
265
|
},
|
|
264
266
|
};
|
|
265
267
|
}
|
package/src/routes.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// The API's routes, as checks call them: `coffre verify instance` and the
|
|
2
|
+
// local conformance run. Both tables are typed over the route map, so a
|
|
3
|
+
// route added to the server fails the typecheck here until it is listed,
|
|
4
|
+
// and from then on every check that walks a table covers it: the anonymous
|
|
5
|
+
// refusal, the canary scans, the cross-site one.
|
|
6
|
+
import type { Params, RouteInput, RouteKey } from './index.ts';
|
|
7
|
+
|
|
8
|
+
export type GetKey = Extract<RouteKey, `GET ${string}`>;
|
|
9
|
+
|
|
10
|
+
/** Each GET route, with what to ask it. */
|
|
11
|
+
export type GetCalls = { [K in GetKey]: { params: Params<K>; input?: RouteInput<K> }[] };
|
|
12
|
+
|
|
13
|
+
/** What the GET routes are asked about: places, secrets and members to name. */
|
|
14
|
+
export type Subjects = {
|
|
15
|
+
places: { project: string; environment: string }[];
|
|
16
|
+
/** Secrets whose history to ask for. */
|
|
17
|
+
secrets: { project: string; environment: string; key: string }[];
|
|
18
|
+
/** `user:…` and `token:…`. */
|
|
19
|
+
members: string[];
|
|
20
|
+
/** Tokens whose credentials to list. */
|
|
21
|
+
services: string[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function getCalls({ places, secrets, members, services }: Subjects): GetCalls {
|
|
25
|
+
const first = places[0];
|
|
26
|
+
return {
|
|
27
|
+
'GET /me': [{ params: {} }],
|
|
28
|
+
'GET /projects': [{ params: {} }],
|
|
29
|
+
'GET /secrets/:project/:environment': places.map((params) => ({ params })),
|
|
30
|
+
'GET /secrets/:project/:environment/:key/versions': secrets.map((params) => ({ params })),
|
|
31
|
+
'GET /members': [
|
|
32
|
+
{ params: {} },
|
|
33
|
+
...(first === undefined ? [] : [{ params: {}, input: { path: `${first.project}/${first.environment}` } }]),
|
|
34
|
+
],
|
|
35
|
+
'GET /members/:member': members.map((member) => ({ params: { member } })),
|
|
36
|
+
'GET /members/:member/tokens': services.map((member) => ({ params: { member } })),
|
|
37
|
+
'GET /sessions': [{ params: {} }],
|
|
38
|
+
'GET /identities': [{ params: {} }],
|
|
39
|
+
'GET /device-logins/:code': [{ params: { code: 'BCDF-GHJK' } }],
|
|
40
|
+
'GET /syncs/providers': [{ params: {} }],
|
|
41
|
+
'GET /syncs/:project/:environment': places.map((params) => ({ params })),
|
|
42
|
+
'GET /audit': [{ params: {}, input: { limit: 500 } }, { params: {}, input: { limit: 500, detail: '1' } }],
|
|
43
|
+
'GET /audit/verification': [{ params: {} }],
|
|
44
|
+
'GET /audit/keys': [{ params: {} }],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Each GET call as a URL, with its parameters and query filled in. */
|
|
49
|
+
export function getUrls(origin: string, calls: GetCalls): { key: GetKey; url: string }[] {
|
|
50
|
+
const urls: { key: GetKey; url: string }[] = [];
|
|
51
|
+
for (const [key, asks] of Object.entries(calls) as [GetKey, { params: Record<string, string>; input?: object }[]][]) {
|
|
52
|
+
for (const { params, input } of asks) urls.push({ key, url: address(origin, key, params, input) });
|
|
53
|
+
}
|
|
54
|
+
return urls;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Every route, whatever its method. */
|
|
58
|
+
const EVERY_ROUTE: { [K in RouteKey]: true } = {
|
|
59
|
+
'GET /me': true,
|
|
60
|
+
'GET /projects': true,
|
|
61
|
+
'PUT /projects/:project': true,
|
|
62
|
+
'PATCH /projects/:project': true,
|
|
63
|
+
'PUT /projects/:project/:environment': true,
|
|
64
|
+
'PATCH /projects/:project/:environment': true,
|
|
65
|
+
'GET /secrets/:project/:environment': true,
|
|
66
|
+
'PATCH /secrets/:project/:environment': true,
|
|
67
|
+
'PATCH /secrets/:project/:environment/:key': true,
|
|
68
|
+
'GET /secrets/:project/:environment/:key/versions': true,
|
|
69
|
+
'POST /secrets/:project/:environment/:key/restore': true,
|
|
70
|
+
'POST /reveals': true,
|
|
71
|
+
'GET /members': true,
|
|
72
|
+
'GET /members/:member': true,
|
|
73
|
+
'PUT /members/:member': true,
|
|
74
|
+
'DELETE /members/:member': true,
|
|
75
|
+
'GET /members/:member/tokens': true,
|
|
76
|
+
'POST /members/:member/tokens': true,
|
|
77
|
+
'DELETE /members/:member/tokens/:id': true,
|
|
78
|
+
'PATCH /access/:member': true,
|
|
79
|
+
'GET /sessions': true,
|
|
80
|
+
'DELETE /sessions/:id': true,
|
|
81
|
+
'GET /identities': true,
|
|
82
|
+
'DELETE /identities/:id': true,
|
|
83
|
+
'GET /device-logins/:code': true,
|
|
84
|
+
'POST /device-logins/:code': true,
|
|
85
|
+
'GET /syncs/providers': true,
|
|
86
|
+
'GET /syncs/:project/:environment': true,
|
|
87
|
+
'POST /syncs/:project/:environment': true,
|
|
88
|
+
'PATCH /syncs/by-id/:id': true,
|
|
89
|
+
'DELETE /syncs/by-id/:id': true,
|
|
90
|
+
'POST /syncs/by-id/:id/runs': true,
|
|
91
|
+
'GET /audit': true,
|
|
92
|
+
'GET /audit/verification': true,
|
|
93
|
+
'GET /audit/keys': true,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Every route as a method and a URL, each parameter a made-up value: what
|
|
98
|
+
* a caller with no right to any of them would send.
|
|
99
|
+
*/
|
|
100
|
+
export function everyRoute(origin: string): { key: RouteKey; method: string; url: string }[] {
|
|
101
|
+
return (Object.keys(EVERY_ROUTE) as RouteKey[]).map((key) => {
|
|
102
|
+
const [method, pattern] = key.split(' ') as [string, string];
|
|
103
|
+
const path = pattern.replace(/:(\w+)/g, (_, name: string) =>
|
|
104
|
+
encodeURIComponent(name === 'member' ? 'token:conformance-nobody' : 'conformance-nobody'),
|
|
105
|
+
);
|
|
106
|
+
return { key, method, url: `${origin}/api${path}` };
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function address(origin: string, key: GetKey, params: Record<string, string>, input: object | undefined): string {
|
|
111
|
+
const path = key.slice('GET '.length).replace(/:(\w+)/g, (_, name: string) => encodeURIComponent(params[name]!));
|
|
112
|
+
const url = new URL(`${origin}/api${path}`);
|
|
113
|
+
for (const [name, value] of Object.entries(input ?? {})) url.searchParams.set(name, String(value));
|
|
114
|
+
return url.href;
|
|
115
|
+
}
|