@nattyjs/client 0.0.1-beta.67
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.cjs +166 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.mjs +163 -0
- package/package.json +20 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class NattyClientError extends Error {
|
|
4
|
+
constructor(message, status, body, responseHeaders = {}) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "NattyClientError";
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.body = body;
|
|
9
|
+
this.responseHeaders = responseHeaders;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function isEndpoint(value) {
|
|
13
|
+
return !!value && typeof value === "object" && typeof value.method === "string" && typeof value.path === "string";
|
|
14
|
+
}
|
|
15
|
+
function trimSlashes(value) {
|
|
16
|
+
return String(value || "").replace(/^\/+|\/+$/g, "");
|
|
17
|
+
}
|
|
18
|
+
function joinUrl(baseUrl, pathName) {
|
|
19
|
+
const normalizedPath = trimSlashes(pathName);
|
|
20
|
+
if (!baseUrl) {
|
|
21
|
+
return normalizedPath ? `/${normalizedPath}` : "/";
|
|
22
|
+
}
|
|
23
|
+
const normalizedBase = trimSlashes(baseUrl);
|
|
24
|
+
if (!normalizedBase) {
|
|
25
|
+
return normalizedPath ? `/${normalizedPath}` : "/";
|
|
26
|
+
}
|
|
27
|
+
return normalizedPath ? `/${normalizedBase}/${normalizedPath}` : `/${normalizedBase}`;
|
|
28
|
+
}
|
|
29
|
+
function applyPathParams(pathName, params) {
|
|
30
|
+
return pathName.replace(/:([A-Za-z0-9_]+)/g, (_, key) => {
|
|
31
|
+
if (!params || params[key] === void 0 || params[key] === null) {
|
|
32
|
+
throw new Error(`Missing path param "${key}" for ${pathName}`);
|
|
33
|
+
}
|
|
34
|
+
return encodeURIComponent(String(params[key]));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function appendQueryParams(url, query) {
|
|
38
|
+
if (!query || Object.keys(query).length === 0) {
|
|
39
|
+
return url;
|
|
40
|
+
}
|
|
41
|
+
const searchParams = new URLSearchParams();
|
|
42
|
+
for (const [key, value] of Object.entries(query)) {
|
|
43
|
+
if (value === void 0 || value === null) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
for (const item of value) {
|
|
48
|
+
searchParams.append(key, String(item));
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
searchParams.set(key, String(value));
|
|
53
|
+
}
|
|
54
|
+
const queryString = searchParams.toString();
|
|
55
|
+
if (!queryString) {
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
return `${url}${url.includes("?") ? "&" : "?"}${queryString}`;
|
|
59
|
+
}
|
|
60
|
+
function isBodyPassthrough(value) {
|
|
61
|
+
if (value == null) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (typeof value === "string") {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (typeof FormData !== "undefined" && value instanceof FormData) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
async function resolveDefaultHeaders(options) {
|
|
82
|
+
if (!options?.headers) {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
if (typeof options.headers === "function") {
|
|
86
|
+
return await options.headers() || {};
|
|
87
|
+
}
|
|
88
|
+
return options.headers;
|
|
89
|
+
}
|
|
90
|
+
function readResponseHeaders(response) {
|
|
91
|
+
const headers = {};
|
|
92
|
+
response.headers?.forEach?.((value, key) => {
|
|
93
|
+
headers[key] = value;
|
|
94
|
+
});
|
|
95
|
+
return headers;
|
|
96
|
+
}
|
|
97
|
+
async function readResponseBody(response) {
|
|
98
|
+
const text = await response.text();
|
|
99
|
+
if (!text) {
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
const contentType = response.headers?.get?.("content-type") || "";
|
|
103
|
+
if (contentType.includes("application/json")) {
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(text);
|
|
106
|
+
} catch {
|
|
107
|
+
return text;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return text;
|
|
111
|
+
}
|
|
112
|
+
async function invokeEndpoint(endpoint, input, options) {
|
|
113
|
+
const fetcher = options.fetch || globalThis.fetch;
|
|
114
|
+
if (!fetcher) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
"No fetch implementation is available. Pass options.fetch to createNattyClient."
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const pathWithParams = applyPathParams(endpoint.path, input?.params);
|
|
120
|
+
const url = appendQueryParams(joinUrl(options.baseUrl, pathWithParams), input?.query);
|
|
121
|
+
const defaultHeaders = await resolveDefaultHeaders(options);
|
|
122
|
+
const headers = {
|
|
123
|
+
...defaultHeaders,
|
|
124
|
+
...input?.headers || {}
|
|
125
|
+
};
|
|
126
|
+
let body = input?.body;
|
|
127
|
+
if (!isBodyPassthrough(body) && body !== void 0) {
|
|
128
|
+
body = JSON.stringify(body);
|
|
129
|
+
if (!headers["Content-Type"]) {
|
|
130
|
+
headers["Content-Type"] = "application/json";
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const response = await fetcher(url, {
|
|
134
|
+
method: endpoint.method,
|
|
135
|
+
headers,
|
|
136
|
+
body,
|
|
137
|
+
signal: input?.signal
|
|
138
|
+
});
|
|
139
|
+
const responseBody = await readResponseBody(response);
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
throw new NattyClientError(
|
|
142
|
+
`Natty client request failed with status ${response.status}`,
|
|
143
|
+
response.status,
|
|
144
|
+
responseBody,
|
|
145
|
+
readResponseHeaders(response)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
return responseBody;
|
|
149
|
+
}
|
|
150
|
+
function createContractNode(node, options) {
|
|
151
|
+
const clientNode = {};
|
|
152
|
+
for (const [key, value] of Object.entries(node)) {
|
|
153
|
+
if (isEndpoint(value)) {
|
|
154
|
+
clientNode[key] = (input) => invokeEndpoint(value, input, options);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
clientNode[key] = createContractNode(value, options);
|
|
158
|
+
}
|
|
159
|
+
return clientNode;
|
|
160
|
+
}
|
|
161
|
+
function createNattyClient(contract, options = {}) {
|
|
162
|
+
return createContractNode(contract, options);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
exports.NattyClientError = NattyClientError;
|
|
166
|
+
exports.createNattyClient = createNattyClient;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
interface NattyFetchRequestInit {
|
|
2
|
+
method?: string;
|
|
3
|
+
headers?: Record<string, string>;
|
|
4
|
+
body?: unknown;
|
|
5
|
+
signal?: unknown;
|
|
6
|
+
}
|
|
7
|
+
interface NattyFetchResponse {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
status: number;
|
|
10
|
+
headers?: {
|
|
11
|
+
get?(name: string): string | null;
|
|
12
|
+
forEach?(callback: (value: string, key: string) => void): void;
|
|
13
|
+
};
|
|
14
|
+
text(): Promise<string>;
|
|
15
|
+
}
|
|
16
|
+
type NattyFetch = (input: string, init?: NattyFetchRequestInit) => Promise<NattyFetchResponse>;
|
|
17
|
+
interface NattyClientOptions {
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
fetch?: NattyFetch;
|
|
20
|
+
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
21
|
+
}
|
|
22
|
+
interface NattyRequestOptions {
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
signal?: unknown;
|
|
25
|
+
params?: Record<string, unknown>;
|
|
26
|
+
query?: Record<string, unknown>;
|
|
27
|
+
body?: unknown;
|
|
28
|
+
}
|
|
29
|
+
interface NattyClientEndpoint {
|
|
30
|
+
method: string;
|
|
31
|
+
path: string;
|
|
32
|
+
}
|
|
33
|
+
type NattyClientContract = {
|
|
34
|
+
[key: string]: NattyClientContract | NattyClientEndpoint;
|
|
35
|
+
};
|
|
36
|
+
declare class NattyClientError extends Error {
|
|
37
|
+
status: number;
|
|
38
|
+
body: unknown;
|
|
39
|
+
responseHeaders: Record<string, string>;
|
|
40
|
+
constructor(message: string, status: number, body: unknown, responseHeaders?: Record<string, string>);
|
|
41
|
+
}
|
|
42
|
+
declare function createNattyClient<TClient = any>(contract: NattyClientContract, options?: NattyClientOptions): TClient;
|
|
43
|
+
|
|
44
|
+
export { NattyClientContract, NattyClientEndpoint, NattyClientError, NattyClientOptions, NattyFetch, NattyFetchRequestInit, NattyFetchResponse, NattyRequestOptions, createNattyClient };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
class NattyClientError extends Error {
|
|
2
|
+
constructor(message, status, body, responseHeaders = {}) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "NattyClientError";
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.body = body;
|
|
7
|
+
this.responseHeaders = responseHeaders;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function isEndpoint(value) {
|
|
11
|
+
return !!value && typeof value === "object" && typeof value.method === "string" && typeof value.path === "string";
|
|
12
|
+
}
|
|
13
|
+
function trimSlashes(value) {
|
|
14
|
+
return String(value || "").replace(/^\/+|\/+$/g, "");
|
|
15
|
+
}
|
|
16
|
+
function joinUrl(baseUrl, pathName) {
|
|
17
|
+
const normalizedPath = trimSlashes(pathName);
|
|
18
|
+
if (!baseUrl) {
|
|
19
|
+
return normalizedPath ? `/${normalizedPath}` : "/";
|
|
20
|
+
}
|
|
21
|
+
const normalizedBase = trimSlashes(baseUrl);
|
|
22
|
+
if (!normalizedBase) {
|
|
23
|
+
return normalizedPath ? `/${normalizedPath}` : "/";
|
|
24
|
+
}
|
|
25
|
+
return normalizedPath ? `/${normalizedBase}/${normalizedPath}` : `/${normalizedBase}`;
|
|
26
|
+
}
|
|
27
|
+
function applyPathParams(pathName, params) {
|
|
28
|
+
return pathName.replace(/:([A-Za-z0-9_]+)/g, (_, key) => {
|
|
29
|
+
if (!params || params[key] === void 0 || params[key] === null) {
|
|
30
|
+
throw new Error(`Missing path param "${key}" for ${pathName}`);
|
|
31
|
+
}
|
|
32
|
+
return encodeURIComponent(String(params[key]));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function appendQueryParams(url, query) {
|
|
36
|
+
if (!query || Object.keys(query).length === 0) {
|
|
37
|
+
return url;
|
|
38
|
+
}
|
|
39
|
+
const searchParams = new URLSearchParams();
|
|
40
|
+
for (const [key, value] of Object.entries(query)) {
|
|
41
|
+
if (value === void 0 || value === null) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(value)) {
|
|
45
|
+
for (const item of value) {
|
|
46
|
+
searchParams.append(key, String(item));
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
searchParams.set(key, String(value));
|
|
51
|
+
}
|
|
52
|
+
const queryString = searchParams.toString();
|
|
53
|
+
if (!queryString) {
|
|
54
|
+
return url;
|
|
55
|
+
}
|
|
56
|
+
return `${url}${url.includes("?") ? "&" : "?"}${queryString}`;
|
|
57
|
+
}
|
|
58
|
+
function isBodyPassthrough(value) {
|
|
59
|
+
if (value == null) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (typeof value === "string") {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
if (typeof FormData !== "undefined" && value instanceof FormData) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
async function resolveDefaultHeaders(options) {
|
|
80
|
+
if (!options?.headers) {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
if (typeof options.headers === "function") {
|
|
84
|
+
return await options.headers() || {};
|
|
85
|
+
}
|
|
86
|
+
return options.headers;
|
|
87
|
+
}
|
|
88
|
+
function readResponseHeaders(response) {
|
|
89
|
+
const headers = {};
|
|
90
|
+
response.headers?.forEach?.((value, key) => {
|
|
91
|
+
headers[key] = value;
|
|
92
|
+
});
|
|
93
|
+
return headers;
|
|
94
|
+
}
|
|
95
|
+
async function readResponseBody(response) {
|
|
96
|
+
const text = await response.text();
|
|
97
|
+
if (!text) {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
const contentType = response.headers?.get?.("content-type") || "";
|
|
101
|
+
if (contentType.includes("application/json")) {
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(text);
|
|
104
|
+
} catch {
|
|
105
|
+
return text;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return text;
|
|
109
|
+
}
|
|
110
|
+
async function invokeEndpoint(endpoint, input, options) {
|
|
111
|
+
const fetcher = options.fetch || globalThis.fetch;
|
|
112
|
+
if (!fetcher) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
"No fetch implementation is available. Pass options.fetch to createNattyClient."
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const pathWithParams = applyPathParams(endpoint.path, input?.params);
|
|
118
|
+
const url = appendQueryParams(joinUrl(options.baseUrl, pathWithParams), input?.query);
|
|
119
|
+
const defaultHeaders = await resolveDefaultHeaders(options);
|
|
120
|
+
const headers = {
|
|
121
|
+
...defaultHeaders,
|
|
122
|
+
...input?.headers || {}
|
|
123
|
+
};
|
|
124
|
+
let body = input?.body;
|
|
125
|
+
if (!isBodyPassthrough(body) && body !== void 0) {
|
|
126
|
+
body = JSON.stringify(body);
|
|
127
|
+
if (!headers["Content-Type"]) {
|
|
128
|
+
headers["Content-Type"] = "application/json";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const response = await fetcher(url, {
|
|
132
|
+
method: endpoint.method,
|
|
133
|
+
headers,
|
|
134
|
+
body,
|
|
135
|
+
signal: input?.signal
|
|
136
|
+
});
|
|
137
|
+
const responseBody = await readResponseBody(response);
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
throw new NattyClientError(
|
|
140
|
+
`Natty client request failed with status ${response.status}`,
|
|
141
|
+
response.status,
|
|
142
|
+
responseBody,
|
|
143
|
+
readResponseHeaders(response)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return responseBody;
|
|
147
|
+
}
|
|
148
|
+
function createContractNode(node, options) {
|
|
149
|
+
const clientNode = {};
|
|
150
|
+
for (const [key, value] of Object.entries(node)) {
|
|
151
|
+
if (isEndpoint(value)) {
|
|
152
|
+
clientNode[key] = (input) => invokeEndpoint(value, input, options);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
clientNode[key] = createContractNode(value, options);
|
|
156
|
+
}
|
|
157
|
+
return clientNode;
|
|
158
|
+
}
|
|
159
|
+
function createNattyClient(contract, options = {}) {
|
|
160
|
+
return createContractNode(contract, options);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export { NattyClientError, createNattyClient };
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nattyjs/client",
|
|
3
|
+
"version": "0.0.1-beta.67",
|
|
4
|
+
"description": "Generic runtime client for NattyJS generated SDKs",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"author": "ajayojha <ojhaajay@outlook.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "unbuild"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"unbuild": "1.2.1"
|
|
19
|
+
}
|
|
20
|
+
}
|