restix 0.0.1 → 0.0.2
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/api.js +3 -4
- package/api.ts +3 -5
- package/package.json +1 -1
- package/test.test.js +104 -4
- package/test.test.ts +164 -6
- package/types.d.ts +1 -1
- package/types.ts +1 -1
package/api.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export class NexAPI {
|
|
2
2
|
#base = 'http://localhost:5000';
|
|
3
3
|
#api_maps;
|
|
4
|
-
#defaults;
|
|
4
|
+
#defaults = { headers: { 'X-Client-Signature': 'restix/0.0.2 (by SignorP)' } };
|
|
5
5
|
constructor(base, api_maps, defaults) {
|
|
6
6
|
this.#base = base;
|
|
7
7
|
this.#api_maps = api_maps;
|
|
8
|
-
this.#defaults = defaults ?? {};
|
|
8
|
+
this.#defaults = { ...defaults, headers: { ...defaults?.headers ?? {}, ...this.#defaults.headers } };
|
|
9
9
|
}
|
|
10
10
|
async request(prefix, data) {
|
|
11
11
|
const api = this.#api_maps[prefix];
|
|
@@ -36,12 +36,11 @@ export class NexAPI {
|
|
|
36
36
|
fetchOptoin.body = JSON.stringify(this.#injectData(api.map.body, data));
|
|
37
37
|
if (api.map.headers.length > 0)
|
|
38
38
|
fetchOptoin.headers = this.#injectData(api.map.headers, data);
|
|
39
|
-
console.log(url, method, { ...fetchOptoin });
|
|
40
39
|
const res = await fetch(url, fetchOptoin);
|
|
41
40
|
if (res.status === api.status.safe)
|
|
42
41
|
return await res[api.res]();
|
|
43
42
|
else if (api.status && api.status[res.status])
|
|
44
|
-
return api.status[res.status]?.();
|
|
43
|
+
return await api.status[res.status]?.(res);
|
|
45
44
|
return undefined;
|
|
46
45
|
}
|
|
47
46
|
#injectUrlData(url, params, data) {
|
package/api.ts
CHANGED
|
@@ -2,14 +2,13 @@ import type { API_MAPS, Exact } from './types'
|
|
|
2
2
|
|
|
3
3
|
export class NexAPI<API_INTERFACE extends { [k: string]: any }> {
|
|
4
4
|
#base = 'http://localhost:5000'
|
|
5
|
-
|
|
6
5
|
#api_maps: API_MAPS<API_INTERFACE>
|
|
7
6
|
#defaults: {
|
|
8
7
|
body?: Record<string, string>
|
|
9
8
|
params?: Record<string, string>
|
|
10
9
|
query?: Record<string, string>
|
|
11
10
|
headers?: Record<string, string>
|
|
12
|
-
}
|
|
11
|
+
} = { headers: { 'X-Client-Signature': 'restix/0.0.2 (by SignorP)' } }
|
|
13
12
|
|
|
14
13
|
constructor(base: string, api_maps: API_MAPS<API_INTERFACE>, defaults?: {
|
|
15
14
|
body?: Record<string, string>
|
|
@@ -19,7 +18,7 @@ export class NexAPI<API_INTERFACE extends { [k: string]: any }> {
|
|
|
19
18
|
}) {
|
|
20
19
|
this.#base = base
|
|
21
20
|
this.#api_maps = api_maps
|
|
22
|
-
this.#defaults = defaults ?? {}
|
|
21
|
+
this.#defaults = { ...defaults, headers: { ...defaults?.headers ?? {}, ...this.#defaults.headers } }
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
async request<URL extends (keyof API_INTERFACE) & string>(prefix: URL, data: Exact<API_INTERFACE[URL], API_INTERFACE[URL]>) {
|
|
@@ -46,10 +45,9 @@ export class NexAPI<API_INTERFACE extends { [k: string]: any }> {
|
|
|
46
45
|
if (api.map.query.length > 0) url = this.#injectQuery(url, api.map.query, data)
|
|
47
46
|
if (api.map.body.length > 0) fetchOptoin.body = JSON.stringify(this.#injectData(api.map.body, data))
|
|
48
47
|
if (api.map.headers.length > 0) fetchOptoin.headers = this.#injectData(api.map.headers, data)
|
|
49
|
-
console.log(url, method, { ...fetchOptoin })
|
|
50
48
|
const res = await fetch(url, fetchOptoin)
|
|
51
49
|
if (res.status === api.status.safe) return await res[api.res]()
|
|
52
|
-
else if (api.status && api.status[res.status]) return api.status[res.status]?.()
|
|
50
|
+
else if (api.status && api.status[res.status]) return await api.status[res.status]?.(res)
|
|
53
51
|
return undefined
|
|
54
52
|
}
|
|
55
53
|
|
package/package.json
CHANGED
package/test.test.js
CHANGED
|
@@ -1,13 +1,113 @@
|
|
|
1
1
|
import { NexAPI } from './api';
|
|
2
2
|
import { apiMap } from './utils';
|
|
3
|
+
/* -------------------------------------------------------
|
|
4
|
+
API MAPS
|
|
5
|
+
------------------------------------------------------- */
|
|
3
6
|
const api_maps = {
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
// PARAM INJECTION
|
|
8
|
+
'GET /packages/cli/name/{name}': {
|
|
9
|
+
map: apiMap({
|
|
10
|
+
params: ['name']
|
|
11
|
+
}),
|
|
12
|
+
res: 'json',
|
|
13
|
+
status: {
|
|
14
|
+
safe: 200,
|
|
15
|
+
404: () => 'PACKAGE NOT FOUND'
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
// MULTIPLE PARAMS
|
|
19
|
+
'GET /packages/cli/name/{name}/{version}': {
|
|
20
|
+
map: apiMap({
|
|
21
|
+
params: ['*']
|
|
22
|
+
}),
|
|
23
|
+
res: 'json',
|
|
24
|
+
status: {
|
|
25
|
+
safe: 200,
|
|
26
|
+
404: () => 'VERSION NOT FOUND'
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
// BODY INJECTION
|
|
30
|
+
'POST /packages/cli/create': {
|
|
31
|
+
map: apiMap({
|
|
32
|
+
body: ['*']
|
|
33
|
+
}),
|
|
34
|
+
res: 'json',
|
|
35
|
+
status: {
|
|
36
|
+
safe: 201,
|
|
37
|
+
400: () => 'BAD REQUEST'
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
// QUERY + HEADER INJECTION
|
|
41
|
+
'GET /packages/cli/search': {
|
|
42
|
+
map: apiMap({
|
|
43
|
+
query: ['q'],
|
|
44
|
+
headers: ['Authorization']
|
|
45
|
+
}),
|
|
6
46
|
res: 'json',
|
|
7
47
|
status: {
|
|
8
48
|
safe: 200
|
|
9
49
|
}
|
|
10
50
|
}
|
|
11
51
|
};
|
|
12
|
-
|
|
13
|
-
|
|
52
|
+
/* -------------------------------------------------------
|
|
53
|
+
API INSTANCE
|
|
54
|
+
------------------------------------------------------- */
|
|
55
|
+
const api = new NexAPI('http://localhost:5000', api_maps, {
|
|
56
|
+
headers: {
|
|
57
|
+
'User-Agent': 'restix-cli'
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
/* -------------------------------------------------------
|
|
61
|
+
FEATURE SHOWCASE
|
|
62
|
+
------------------------------------------------------- */
|
|
63
|
+
// -----------------------------------------
|
|
64
|
+
// SIMPLE PARAM REQUEST
|
|
65
|
+
// GET /packages/cli/name/xnex
|
|
66
|
+
// -----------------------------------------
|
|
67
|
+
await api.request('GET /packages/cli/name/{name}', {
|
|
68
|
+
name: 'xnex'
|
|
69
|
+
});
|
|
70
|
+
// -----------------------------------------
|
|
71
|
+
// MULTI PARAM REQUEST
|
|
72
|
+
// GET /packages/cli/name/xnex/0.0.1
|
|
73
|
+
// -----------------------------------------
|
|
74
|
+
await api.request('GET /packages/cli/name/{name}/{version}', {
|
|
75
|
+
name: 'xnex',
|
|
76
|
+
version: '0.0.1'
|
|
77
|
+
});
|
|
78
|
+
// -----------------------------------------
|
|
79
|
+
// BODY PAYLOAD
|
|
80
|
+
// POST /packages/cli/create
|
|
81
|
+
// -----------------------------------------
|
|
82
|
+
await api.request('POST /packages/cli/create', {
|
|
83
|
+
name: 'xnex',
|
|
84
|
+
version: '0.0.1',
|
|
85
|
+
developer: 'Signor P'
|
|
86
|
+
});
|
|
87
|
+
// -----------------------------------------
|
|
88
|
+
// QUERY + HEADER
|
|
89
|
+
// GET /packages/cli/search?query=nex
|
|
90
|
+
// Header: token
|
|
91
|
+
// -----------------------------------------
|
|
92
|
+
await api.request('GET /packages/cli/search', {
|
|
93
|
+
q: 'nex',
|
|
94
|
+
Authorization: 'Bearer AUTH_TOKEN'
|
|
95
|
+
});
|
|
96
|
+
// -----------------------------------------
|
|
97
|
+
// TYPE SAFETY DEMO (INTENTIONAL ERRORS)
|
|
98
|
+
// -----------------------------------------
|
|
99
|
+
// ❌ extra field not allowed
|
|
100
|
+
// await api.request(
|
|
101
|
+
// 'GET /packages/cli/name/{name}',
|
|
102
|
+
// {
|
|
103
|
+
// name: 'xnex',
|
|
104
|
+
// extra: 'nope'
|
|
105
|
+
// }
|
|
106
|
+
// )
|
|
107
|
+
// ❌ missing field
|
|
108
|
+
// await api.request(
|
|
109
|
+
// 'POST /packages/cli/create',
|
|
110
|
+
// {
|
|
111
|
+
// name: 'xnex'
|
|
112
|
+
// }
|
|
113
|
+
// )
|
package/test.test.ts
CHANGED
|
@@ -1,13 +1,81 @@
|
|
|
1
1
|
import { NexAPI } from './api'
|
|
2
|
-
import type { API_MAPS } from './types'
|
|
2
|
+
import type { API_MAPS, GET_API } from './types'
|
|
3
3
|
import { apiMap } from './utils'
|
|
4
4
|
|
|
5
|
+
/* -------------------------------------------------------
|
|
6
|
+
API CONTRACT
|
|
7
|
+
------------------------------------------------------- */
|
|
8
|
+
|
|
5
9
|
interface API_INTERFACE {
|
|
6
|
-
'GET /
|
|
10
|
+
'GET /packages/cli/name/{name}': {
|
|
11
|
+
name: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
'GET /packages/cli/name/{name}/{version}': {
|
|
15
|
+
name: string
|
|
16
|
+
version: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
'POST /packages/cli/create': {
|
|
20
|
+
name: string
|
|
21
|
+
version: string
|
|
22
|
+
developer: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
'GET /packages/cli/search': {
|
|
26
|
+
q: string
|
|
27
|
+
Authorization: string
|
|
28
|
+
}
|
|
7
29
|
}
|
|
30
|
+
|
|
31
|
+
/* -------------------------------------------------------
|
|
32
|
+
API MAPS
|
|
33
|
+
------------------------------------------------------- */
|
|
34
|
+
|
|
8
35
|
const api_maps: API_MAPS<API_INTERFACE> = {
|
|
9
|
-
|
|
10
|
-
|
|
36
|
+
|
|
37
|
+
// PARAM INJECTION
|
|
38
|
+
'GET /packages/cli/name/{name}': {
|
|
39
|
+
map: apiMap<GET_API<API_INTERFACE, 'GET /packages/cli/name/{name}'>>({
|
|
40
|
+
params: ['name']
|
|
41
|
+
}),
|
|
42
|
+
res: 'json',
|
|
43
|
+
status: {
|
|
44
|
+
safe: 200,
|
|
45
|
+
404: () => 'PACKAGE NOT FOUND'
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// MULTIPLE PARAMS
|
|
50
|
+
'GET /packages/cli/name/{name}/{version}': {
|
|
51
|
+
map: apiMap<GET_API<API_INTERFACE, 'GET /packages/cli/name/{name}/{version}'>>({
|
|
52
|
+
params: ['*']
|
|
53
|
+
}),
|
|
54
|
+
res: 'json',
|
|
55
|
+
status: {
|
|
56
|
+
safe: 200,
|
|
57
|
+
404: () => 'VERSION NOT FOUND'
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// BODY INJECTION
|
|
62
|
+
'POST /packages/cli/create': {
|
|
63
|
+
map: apiMap<GET_API<API_INTERFACE, 'POST /packages/cli/create'>>({
|
|
64
|
+
body: ['*']
|
|
65
|
+
}),
|
|
66
|
+
res: 'json',
|
|
67
|
+
status: {
|
|
68
|
+
safe: 201,
|
|
69
|
+
400: () => 'BAD REQUEST'
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// QUERY + HEADER INJECTION
|
|
74
|
+
'GET /packages/cli/search': {
|
|
75
|
+
map: apiMap<GET_API<API_INTERFACE, 'GET /packages/cli/search'>>({
|
|
76
|
+
query: ['q'],
|
|
77
|
+
headers: ['Authorization']
|
|
78
|
+
}),
|
|
11
79
|
res: 'json',
|
|
12
80
|
status: {
|
|
13
81
|
safe: 200
|
|
@@ -15,5 +83,95 @@ const api_maps: API_MAPS<API_INTERFACE> = {
|
|
|
15
83
|
}
|
|
16
84
|
}
|
|
17
85
|
|
|
18
|
-
|
|
19
|
-
|
|
86
|
+
/* -------------------------------------------------------
|
|
87
|
+
API INSTANCE
|
|
88
|
+
------------------------------------------------------- */
|
|
89
|
+
|
|
90
|
+
const api = new NexAPI<API_INTERFACE>(
|
|
91
|
+
'http://localhost:5000',
|
|
92
|
+
api_maps,
|
|
93
|
+
{
|
|
94
|
+
headers: {
|
|
95
|
+
'User-Agent': 'restix-cli'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
/* -------------------------------------------------------
|
|
101
|
+
FEATURE SHOWCASE
|
|
102
|
+
------------------------------------------------------- */
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
// -----------------------------------------
|
|
106
|
+
// SIMPLE PARAM REQUEST
|
|
107
|
+
// GET /packages/cli/name/xnex
|
|
108
|
+
// -----------------------------------------
|
|
109
|
+
await api.request(
|
|
110
|
+
'GET /packages/cli/name/{name}',
|
|
111
|
+
{
|
|
112
|
+
name: 'xnex'
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// -----------------------------------------
|
|
118
|
+
// MULTI PARAM REQUEST
|
|
119
|
+
// GET /packages/cli/name/xnex/0.0.1
|
|
120
|
+
// -----------------------------------------
|
|
121
|
+
await api.request(
|
|
122
|
+
'GET /packages/cli/name/{name}/{version}',
|
|
123
|
+
{
|
|
124
|
+
name: 'xnex',
|
|
125
|
+
version: '0.0.1'
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
// -----------------------------------------
|
|
131
|
+
// BODY PAYLOAD
|
|
132
|
+
// POST /packages/cli/create
|
|
133
|
+
// -----------------------------------------
|
|
134
|
+
await api.request(
|
|
135
|
+
'POST /packages/cli/create',
|
|
136
|
+
{
|
|
137
|
+
name: 'xnex',
|
|
138
|
+
version: '0.0.1',
|
|
139
|
+
developer: 'Signor P'
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
// -----------------------------------------
|
|
145
|
+
// QUERY + HEADER
|
|
146
|
+
// GET /packages/cli/search?query=nex
|
|
147
|
+
// Header: token
|
|
148
|
+
// -----------------------------------------
|
|
149
|
+
await api.request(
|
|
150
|
+
'GET /packages/cli/search',
|
|
151
|
+
{
|
|
152
|
+
q: 'nex',
|
|
153
|
+
Authorization: 'Bearer AUTH_TOKEN'
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
// -----------------------------------------
|
|
159
|
+
// TYPE SAFETY DEMO (INTENTIONAL ERRORS)
|
|
160
|
+
// -----------------------------------------
|
|
161
|
+
|
|
162
|
+
// ❌ extra field not allowed
|
|
163
|
+
// await api.request(
|
|
164
|
+
// 'GET /packages/cli/name/{name}',
|
|
165
|
+
// {
|
|
166
|
+
// name: 'xnex',
|
|
167
|
+
// extra: 'nope'
|
|
168
|
+
// }
|
|
169
|
+
// )
|
|
170
|
+
|
|
171
|
+
// ❌ missing field
|
|
172
|
+
// await api.request(
|
|
173
|
+
// 'POST /packages/cli/create',
|
|
174
|
+
// {
|
|
175
|
+
// name: 'xnex'
|
|
176
|
+
// }
|
|
177
|
+
// )
|
package/types.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface API_MAP_TYPE<K extends string> {
|
|
|
10
10
|
res: keyof Pick<Response, 'json' | 'text' | 'blob'>;
|
|
11
11
|
status: {
|
|
12
12
|
safe: number;
|
|
13
|
-
[k: number]: () => any;
|
|
13
|
+
[k: number]: (res: Response) => any;
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
export type API_MAPS<T extends Record<string, any>> = {
|