@coffre/client 0.1.6 → 0.1.8

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.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
  }
@@ -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.6",
3
+ "version": "0.1.8",
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
+ }