@themoltnet/sdk 0.0.0-seed
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/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/credentials.d.ts +23 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1117 -0
- package/dist/register.d.ts +29 -0
- package/dist/register.d.ts.map +1 -0
- package/package.json +37 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,wBAAsB,cAAc,CAClC,SAAS,EAAE,SAAS,EACpB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RegisterResult } from './register.js';
|
|
2
|
+
export interface CredentialsFile {
|
|
3
|
+
identity_id: string;
|
|
4
|
+
oauth2: {
|
|
5
|
+
client_id: string;
|
|
6
|
+
client_secret: string;
|
|
7
|
+
};
|
|
8
|
+
keys: {
|
|
9
|
+
public_key: string;
|
|
10
|
+
private_key: string;
|
|
11
|
+
fingerprint: string;
|
|
12
|
+
};
|
|
13
|
+
endpoints: {
|
|
14
|
+
api: string;
|
|
15
|
+
mcp: string;
|
|
16
|
+
};
|
|
17
|
+
registered_at: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function getConfigDir(): string;
|
|
20
|
+
export declare function getCredentialsPath(): string;
|
|
21
|
+
export declare function writeCredentials(result: RegisterResult): Promise<string>;
|
|
22
|
+
export declare function readCredentials(): Promise<CredentialsFile | null>;
|
|
23
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE;QACN,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,IAAI,EAAE;QACJ,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAOvE"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ProblemDetails } from '@moltnet/api-client';
|
|
2
|
+
export declare class MoltNetError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
readonly statusCode?: number;
|
|
5
|
+
readonly detail?: string;
|
|
6
|
+
constructor(message: string, options: {
|
|
7
|
+
code: string;
|
|
8
|
+
statusCode?: number;
|
|
9
|
+
detail?: string;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export declare class RegistrationError extends MoltNetError {
|
|
13
|
+
constructor(message: string, options: {
|
|
14
|
+
code: string;
|
|
15
|
+
statusCode: number;
|
|
16
|
+
detail?: string;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export declare class NetworkError extends MoltNetError {
|
|
20
|
+
constructor(message: string, options?: {
|
|
21
|
+
detail?: string;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export declare function problemToError(problem: ProblemDetails, statusCode: number): RegistrationError;
|
|
25
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAGvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAQlE;AAED,qBAAa,iBAAkB,SAAQ,YAAY;gBAE/C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAKjE;AAED,qBAAa,YAAa,SAAQ,YAAY;gBAChC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAO3D;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,GACjB,iBAAiB,CAMnB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { writeMcpConfig } from './config.js';
|
|
2
|
+
export { type CredentialsFile, getConfigDir, getCredentialsPath, readCredentials, writeCredentials, } from './credentials.js';
|
|
3
|
+
export { MoltNetError, NetworkError, problemToError, RegistrationError, } from './errors.js';
|
|
4
|
+
export { buildMcpConfig, type McpConfig, register, type RegisterOptions, type RegisterResult, } from './register.js';
|
|
5
|
+
import { register } from './register.js';
|
|
6
|
+
export declare const MoltNet: {
|
|
7
|
+
readonly register: typeof register;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,KAAK,eAAe,EACpB,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,KAAK,SAAS,EACd,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,eAAO,MAAM,OAAO;;CAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,1117 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { readFile, writeFile, mkdir, chmod } from "node:fs/promises";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import * as ed from "@noble/ed25519";
|
|
8
|
+
import { createHash, randomBytes } from "crypto";
|
|
9
|
+
async function writeMcpConfig(mcpConfig, dir) {
|
|
10
|
+
const targetDir = dir ?? process.cwd();
|
|
11
|
+
const filePath = join(targetDir, ".mcp.json");
|
|
12
|
+
let existing = {};
|
|
13
|
+
try {
|
|
14
|
+
const content = await readFile(filePath, "utf-8");
|
|
15
|
+
existing = JSON.parse(content);
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
const merged = {
|
|
19
|
+
...existing,
|
|
20
|
+
mcpServers: {
|
|
21
|
+
...existing.mcpServers ?? {},
|
|
22
|
+
...mcpConfig.mcpServers
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
await writeFile(filePath, JSON.stringify(merged, null, 2) + "\n");
|
|
26
|
+
return filePath;
|
|
27
|
+
}
|
|
28
|
+
function getConfigDir() {
|
|
29
|
+
return join(homedir(), ".config", "moltnet");
|
|
30
|
+
}
|
|
31
|
+
function getCredentialsPath() {
|
|
32
|
+
return join(getConfigDir(), "credentials.json");
|
|
33
|
+
}
|
|
34
|
+
async function writeCredentials(result) {
|
|
35
|
+
const configDir = getConfigDir();
|
|
36
|
+
await mkdir(configDir, { recursive: true });
|
|
37
|
+
const credentials = {
|
|
38
|
+
identity_id: result.identity.identityId,
|
|
39
|
+
oauth2: {
|
|
40
|
+
client_id: result.credentials.clientId,
|
|
41
|
+
client_secret: result.credentials.clientSecret
|
|
42
|
+
},
|
|
43
|
+
keys: {
|
|
44
|
+
public_key: result.identity.publicKey,
|
|
45
|
+
private_key: result.identity.privateKey,
|
|
46
|
+
fingerprint: result.identity.fingerprint
|
|
47
|
+
},
|
|
48
|
+
endpoints: {
|
|
49
|
+
api: result.apiUrl,
|
|
50
|
+
mcp: `${result.apiUrl}/mcp`
|
|
51
|
+
},
|
|
52
|
+
registered_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
53
|
+
};
|
|
54
|
+
const filePath = getCredentialsPath();
|
|
55
|
+
await writeFile(filePath, JSON.stringify(credentials, null, 2) + "\n", {
|
|
56
|
+
mode: 384
|
|
57
|
+
});
|
|
58
|
+
await chmod(filePath, 384);
|
|
59
|
+
return filePath;
|
|
60
|
+
}
|
|
61
|
+
async function readCredentials() {
|
|
62
|
+
try {
|
|
63
|
+
const content = await readFile(getCredentialsPath(), "utf-8");
|
|
64
|
+
return JSON.parse(content);
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
class MoltNetError extends Error {
|
|
70
|
+
constructor(message, options) {
|
|
71
|
+
super(message);
|
|
72
|
+
__publicField(this, "code");
|
|
73
|
+
__publicField(this, "statusCode");
|
|
74
|
+
__publicField(this, "detail");
|
|
75
|
+
this.name = "MoltNetError";
|
|
76
|
+
this.code = options.code;
|
|
77
|
+
this.statusCode = options.statusCode;
|
|
78
|
+
this.detail = options.detail;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
class RegistrationError extends MoltNetError {
|
|
82
|
+
constructor(message, options) {
|
|
83
|
+
super(message, options);
|
|
84
|
+
this.name = "RegistrationError";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
class NetworkError extends MoltNetError {
|
|
88
|
+
constructor(message, options) {
|
|
89
|
+
super(message, {
|
|
90
|
+
code: "NETWORK_ERROR",
|
|
91
|
+
detail: options == null ? void 0 : options.detail
|
|
92
|
+
});
|
|
93
|
+
this.name = "NetworkError";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function problemToError(problem, statusCode) {
|
|
97
|
+
return new RegistrationError(problem.title ?? "Registration failed", {
|
|
98
|
+
code: problem.type ?? "UNKNOWN",
|
|
99
|
+
statusCode,
|
|
100
|
+
detail: problem.detail
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const jsonBodySerializer = {
|
|
104
|
+
bodySerializer: (body) => JSON.stringify(
|
|
105
|
+
body,
|
|
106
|
+
(_key, value) => typeof value === "bigint" ? value.toString() : value
|
|
107
|
+
)
|
|
108
|
+
};
|
|
109
|
+
const createSseClient = ({
|
|
110
|
+
onRequest,
|
|
111
|
+
onSseError,
|
|
112
|
+
onSseEvent,
|
|
113
|
+
responseTransformer,
|
|
114
|
+
responseValidator,
|
|
115
|
+
sseDefaultRetryDelay,
|
|
116
|
+
sseMaxRetryAttempts,
|
|
117
|
+
sseMaxRetryDelay,
|
|
118
|
+
sseSleepFn,
|
|
119
|
+
url,
|
|
120
|
+
...options
|
|
121
|
+
}) => {
|
|
122
|
+
let lastEventId;
|
|
123
|
+
const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
124
|
+
const createStream = async function* () {
|
|
125
|
+
let retryDelay = sseDefaultRetryDelay ?? 3e3;
|
|
126
|
+
let attempt = 0;
|
|
127
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
128
|
+
while (true) {
|
|
129
|
+
if (signal.aborted) break;
|
|
130
|
+
attempt++;
|
|
131
|
+
const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
|
|
132
|
+
if (lastEventId !== void 0) {
|
|
133
|
+
headers.set("Last-Event-ID", lastEventId);
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const requestInit = {
|
|
137
|
+
redirect: "follow",
|
|
138
|
+
...options,
|
|
139
|
+
body: options.serializedBody,
|
|
140
|
+
headers,
|
|
141
|
+
signal
|
|
142
|
+
};
|
|
143
|
+
let request = new Request(url, requestInit);
|
|
144
|
+
if (onRequest) {
|
|
145
|
+
request = await onRequest(url, requestInit);
|
|
146
|
+
}
|
|
147
|
+
const _fetch = options.fetch ?? globalThis.fetch;
|
|
148
|
+
const response = await _fetch(request);
|
|
149
|
+
if (!response.ok)
|
|
150
|
+
throw new Error(
|
|
151
|
+
`SSE failed: ${response.status} ${response.statusText}`
|
|
152
|
+
);
|
|
153
|
+
if (!response.body) throw new Error("No body in SSE response");
|
|
154
|
+
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
155
|
+
let buffer = "";
|
|
156
|
+
const abortHandler = () => {
|
|
157
|
+
try {
|
|
158
|
+
reader.cancel();
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
signal.addEventListener("abort", abortHandler);
|
|
163
|
+
try {
|
|
164
|
+
while (true) {
|
|
165
|
+
const { done, value } = await reader.read();
|
|
166
|
+
if (done) break;
|
|
167
|
+
buffer += value;
|
|
168
|
+
buffer = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
169
|
+
const chunks = buffer.split("\n\n");
|
|
170
|
+
buffer = chunks.pop() ?? "";
|
|
171
|
+
for (const chunk of chunks) {
|
|
172
|
+
const lines = chunk.split("\n");
|
|
173
|
+
const dataLines = [];
|
|
174
|
+
let eventName;
|
|
175
|
+
for (const line of lines) {
|
|
176
|
+
if (line.startsWith("data:")) {
|
|
177
|
+
dataLines.push(line.replace(/^data:\s*/, ""));
|
|
178
|
+
} else if (line.startsWith("event:")) {
|
|
179
|
+
eventName = line.replace(/^event:\s*/, "");
|
|
180
|
+
} else if (line.startsWith("id:")) {
|
|
181
|
+
lastEventId = line.replace(/^id:\s*/, "");
|
|
182
|
+
} else if (line.startsWith("retry:")) {
|
|
183
|
+
const parsed = Number.parseInt(
|
|
184
|
+
line.replace(/^retry:\s*/, ""),
|
|
185
|
+
10
|
|
186
|
+
);
|
|
187
|
+
if (!Number.isNaN(parsed)) {
|
|
188
|
+
retryDelay = parsed;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
let data;
|
|
193
|
+
let parsedJson = false;
|
|
194
|
+
if (dataLines.length) {
|
|
195
|
+
const rawData = dataLines.join("\n");
|
|
196
|
+
try {
|
|
197
|
+
data = JSON.parse(rawData);
|
|
198
|
+
parsedJson = true;
|
|
199
|
+
} catch {
|
|
200
|
+
data = rawData;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (parsedJson) {
|
|
204
|
+
if (responseValidator) {
|
|
205
|
+
await responseValidator(data);
|
|
206
|
+
}
|
|
207
|
+
if (responseTransformer) {
|
|
208
|
+
data = await responseTransformer(data);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
onSseEvent == null ? void 0 : onSseEvent({
|
|
212
|
+
data,
|
|
213
|
+
event: eventName,
|
|
214
|
+
id: lastEventId,
|
|
215
|
+
retry: retryDelay
|
|
216
|
+
});
|
|
217
|
+
if (dataLines.length) {
|
|
218
|
+
yield data;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} finally {
|
|
223
|
+
signal.removeEventListener("abort", abortHandler);
|
|
224
|
+
reader.releaseLock();
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
onSseError == null ? void 0 : onSseError(error);
|
|
229
|
+
if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
const backoff = Math.min(
|
|
233
|
+
retryDelay * 2 ** (attempt - 1),
|
|
234
|
+
sseMaxRetryDelay ?? 3e4
|
|
235
|
+
);
|
|
236
|
+
await sleep(backoff);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const stream = createStream();
|
|
241
|
+
return { stream };
|
|
242
|
+
};
|
|
243
|
+
const separatorArrayExplode = (style) => {
|
|
244
|
+
switch (style) {
|
|
245
|
+
case "label":
|
|
246
|
+
return ".";
|
|
247
|
+
case "matrix":
|
|
248
|
+
return ";";
|
|
249
|
+
case "simple":
|
|
250
|
+
return ",";
|
|
251
|
+
default:
|
|
252
|
+
return "&";
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
const separatorArrayNoExplode = (style) => {
|
|
256
|
+
switch (style) {
|
|
257
|
+
case "form":
|
|
258
|
+
return ",";
|
|
259
|
+
case "pipeDelimited":
|
|
260
|
+
return "|";
|
|
261
|
+
case "spaceDelimited":
|
|
262
|
+
return "%20";
|
|
263
|
+
default:
|
|
264
|
+
return ",";
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
const separatorObjectExplode = (style) => {
|
|
268
|
+
switch (style) {
|
|
269
|
+
case "label":
|
|
270
|
+
return ".";
|
|
271
|
+
case "matrix":
|
|
272
|
+
return ";";
|
|
273
|
+
case "simple":
|
|
274
|
+
return ",";
|
|
275
|
+
default:
|
|
276
|
+
return "&";
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const serializeArrayParam = ({
|
|
280
|
+
allowReserved,
|
|
281
|
+
explode,
|
|
282
|
+
name,
|
|
283
|
+
style,
|
|
284
|
+
value
|
|
285
|
+
}) => {
|
|
286
|
+
if (!explode) {
|
|
287
|
+
const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
|
|
288
|
+
switch (style) {
|
|
289
|
+
case "label":
|
|
290
|
+
return `.${joinedValues2}`;
|
|
291
|
+
case "matrix":
|
|
292
|
+
return `;${name}=${joinedValues2}`;
|
|
293
|
+
case "simple":
|
|
294
|
+
return joinedValues2;
|
|
295
|
+
default:
|
|
296
|
+
return `${name}=${joinedValues2}`;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const separator = separatorArrayExplode(style);
|
|
300
|
+
const joinedValues = value.map((v) => {
|
|
301
|
+
if (style === "label" || style === "simple") {
|
|
302
|
+
return allowReserved ? v : encodeURIComponent(v);
|
|
303
|
+
}
|
|
304
|
+
return serializePrimitiveParam({
|
|
305
|
+
allowReserved,
|
|
306
|
+
name,
|
|
307
|
+
value: v
|
|
308
|
+
});
|
|
309
|
+
}).join(separator);
|
|
310
|
+
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
311
|
+
};
|
|
312
|
+
const serializePrimitiveParam = ({
|
|
313
|
+
allowReserved,
|
|
314
|
+
name,
|
|
315
|
+
value
|
|
316
|
+
}) => {
|
|
317
|
+
if (value === void 0 || value === null) {
|
|
318
|
+
return "";
|
|
319
|
+
}
|
|
320
|
+
if (typeof value === "object") {
|
|
321
|
+
throw new Error(
|
|
322
|
+
"Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these."
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
|
|
326
|
+
};
|
|
327
|
+
const serializeObjectParam = ({
|
|
328
|
+
allowReserved,
|
|
329
|
+
explode,
|
|
330
|
+
name,
|
|
331
|
+
style,
|
|
332
|
+
value,
|
|
333
|
+
valueOnly
|
|
334
|
+
}) => {
|
|
335
|
+
if (value instanceof Date) {
|
|
336
|
+
return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
|
|
337
|
+
}
|
|
338
|
+
if (style !== "deepObject" && !explode) {
|
|
339
|
+
let values = [];
|
|
340
|
+
Object.entries(value).forEach(([key, v]) => {
|
|
341
|
+
values = [
|
|
342
|
+
...values,
|
|
343
|
+
key,
|
|
344
|
+
allowReserved ? v : encodeURIComponent(v)
|
|
345
|
+
];
|
|
346
|
+
});
|
|
347
|
+
const joinedValues2 = values.join(",");
|
|
348
|
+
switch (style) {
|
|
349
|
+
case "form":
|
|
350
|
+
return `${name}=${joinedValues2}`;
|
|
351
|
+
case "label":
|
|
352
|
+
return `.${joinedValues2}`;
|
|
353
|
+
case "matrix":
|
|
354
|
+
return `;${name}=${joinedValues2}`;
|
|
355
|
+
default:
|
|
356
|
+
return joinedValues2;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const separator = separatorObjectExplode(style);
|
|
360
|
+
const joinedValues = Object.entries(value).map(
|
|
361
|
+
([key, v]) => serializePrimitiveParam({
|
|
362
|
+
allowReserved,
|
|
363
|
+
name: style === "deepObject" ? `${name}[${key}]` : key,
|
|
364
|
+
value: v
|
|
365
|
+
})
|
|
366
|
+
).join(separator);
|
|
367
|
+
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
368
|
+
};
|
|
369
|
+
const PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
370
|
+
const defaultPathSerializer = ({ path, url: _url }) => {
|
|
371
|
+
let url = _url;
|
|
372
|
+
const matches = _url.match(PATH_PARAM_RE);
|
|
373
|
+
if (matches) {
|
|
374
|
+
for (const match of matches) {
|
|
375
|
+
let explode = false;
|
|
376
|
+
let name = match.substring(1, match.length - 1);
|
|
377
|
+
let style = "simple";
|
|
378
|
+
if (name.endsWith("*")) {
|
|
379
|
+
explode = true;
|
|
380
|
+
name = name.substring(0, name.length - 1);
|
|
381
|
+
}
|
|
382
|
+
if (name.startsWith(".")) {
|
|
383
|
+
name = name.substring(1);
|
|
384
|
+
style = "label";
|
|
385
|
+
} else if (name.startsWith(";")) {
|
|
386
|
+
name = name.substring(1);
|
|
387
|
+
style = "matrix";
|
|
388
|
+
}
|
|
389
|
+
const value = path[name];
|
|
390
|
+
if (value === void 0 || value === null) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (Array.isArray(value)) {
|
|
394
|
+
url = url.replace(
|
|
395
|
+
match,
|
|
396
|
+
serializeArrayParam({ explode, name, style, value })
|
|
397
|
+
);
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (typeof value === "object") {
|
|
401
|
+
url = url.replace(
|
|
402
|
+
match,
|
|
403
|
+
serializeObjectParam({
|
|
404
|
+
explode,
|
|
405
|
+
name,
|
|
406
|
+
style,
|
|
407
|
+
value,
|
|
408
|
+
valueOnly: true
|
|
409
|
+
})
|
|
410
|
+
);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
if (style === "matrix") {
|
|
414
|
+
url = url.replace(
|
|
415
|
+
match,
|
|
416
|
+
`;${serializePrimitiveParam({
|
|
417
|
+
name,
|
|
418
|
+
value
|
|
419
|
+
})}`
|
|
420
|
+
);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const replaceValue = encodeURIComponent(
|
|
424
|
+
style === "label" ? `.${value}` : value
|
|
425
|
+
);
|
|
426
|
+
url = url.replace(match, replaceValue);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return url;
|
|
430
|
+
};
|
|
431
|
+
const getUrl = ({
|
|
432
|
+
baseUrl,
|
|
433
|
+
path,
|
|
434
|
+
query,
|
|
435
|
+
querySerializer,
|
|
436
|
+
url: _url
|
|
437
|
+
}) => {
|
|
438
|
+
const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
|
|
439
|
+
let url = (baseUrl ?? "") + pathUrl;
|
|
440
|
+
if (path) {
|
|
441
|
+
url = defaultPathSerializer({ path, url });
|
|
442
|
+
}
|
|
443
|
+
let search = query ? querySerializer(query) : "";
|
|
444
|
+
if (search.startsWith("?")) {
|
|
445
|
+
search = search.substring(1);
|
|
446
|
+
}
|
|
447
|
+
if (search) {
|
|
448
|
+
url += `?${search}`;
|
|
449
|
+
}
|
|
450
|
+
return url;
|
|
451
|
+
};
|
|
452
|
+
function getValidRequestBody(options) {
|
|
453
|
+
const hasBody = options.body !== void 0;
|
|
454
|
+
const isSerializedBody = hasBody && options.bodySerializer;
|
|
455
|
+
if (isSerializedBody) {
|
|
456
|
+
if ("serializedBody" in options) {
|
|
457
|
+
const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
|
|
458
|
+
return hasSerializedBody ? options.serializedBody : null;
|
|
459
|
+
}
|
|
460
|
+
return options.body !== "" ? options.body : null;
|
|
461
|
+
}
|
|
462
|
+
if (hasBody) {
|
|
463
|
+
return options.body;
|
|
464
|
+
}
|
|
465
|
+
return void 0;
|
|
466
|
+
}
|
|
467
|
+
const getAuthToken = async (auth, callback) => {
|
|
468
|
+
const token = typeof callback === "function" ? await callback(auth) : callback;
|
|
469
|
+
if (!token) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (auth.scheme === "bearer") {
|
|
473
|
+
return `Bearer ${token}`;
|
|
474
|
+
}
|
|
475
|
+
if (auth.scheme === "basic") {
|
|
476
|
+
return `Basic ${btoa(token)}`;
|
|
477
|
+
}
|
|
478
|
+
return token;
|
|
479
|
+
};
|
|
480
|
+
const createQuerySerializer = ({
|
|
481
|
+
parameters = {},
|
|
482
|
+
...args
|
|
483
|
+
} = {}) => {
|
|
484
|
+
const querySerializer = (queryParams) => {
|
|
485
|
+
const search = [];
|
|
486
|
+
if (queryParams && typeof queryParams === "object") {
|
|
487
|
+
for (const name in queryParams) {
|
|
488
|
+
const value = queryParams[name];
|
|
489
|
+
if (value === void 0 || value === null) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
const options = parameters[name] || args;
|
|
493
|
+
if (Array.isArray(value)) {
|
|
494
|
+
const serializedArray = serializeArrayParam({
|
|
495
|
+
allowReserved: options.allowReserved,
|
|
496
|
+
explode: true,
|
|
497
|
+
name,
|
|
498
|
+
style: "form",
|
|
499
|
+
value,
|
|
500
|
+
...options.array
|
|
501
|
+
});
|
|
502
|
+
if (serializedArray) search.push(serializedArray);
|
|
503
|
+
} else if (typeof value === "object") {
|
|
504
|
+
const serializedObject = serializeObjectParam({
|
|
505
|
+
allowReserved: options.allowReserved,
|
|
506
|
+
explode: true,
|
|
507
|
+
name,
|
|
508
|
+
style: "deepObject",
|
|
509
|
+
value,
|
|
510
|
+
...options.object
|
|
511
|
+
});
|
|
512
|
+
if (serializedObject) search.push(serializedObject);
|
|
513
|
+
} else {
|
|
514
|
+
const serializedPrimitive = serializePrimitiveParam({
|
|
515
|
+
allowReserved: options.allowReserved,
|
|
516
|
+
name,
|
|
517
|
+
value
|
|
518
|
+
});
|
|
519
|
+
if (serializedPrimitive) search.push(serializedPrimitive);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return search.join("&");
|
|
524
|
+
};
|
|
525
|
+
return querySerializer;
|
|
526
|
+
};
|
|
527
|
+
const getParseAs = (contentType) => {
|
|
528
|
+
var _a;
|
|
529
|
+
if (!contentType) {
|
|
530
|
+
return "stream";
|
|
531
|
+
}
|
|
532
|
+
const cleanContent = (_a = contentType.split(";")[0]) == null ? void 0 : _a.trim();
|
|
533
|
+
if (!cleanContent) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
|
|
537
|
+
return "json";
|
|
538
|
+
}
|
|
539
|
+
if (cleanContent === "multipart/form-data") {
|
|
540
|
+
return "formData";
|
|
541
|
+
}
|
|
542
|
+
if (["application/", "audio/", "image/", "video/"].some(
|
|
543
|
+
(type) => cleanContent.startsWith(type)
|
|
544
|
+
)) {
|
|
545
|
+
return "blob";
|
|
546
|
+
}
|
|
547
|
+
if (cleanContent.startsWith("text/")) {
|
|
548
|
+
return "text";
|
|
549
|
+
}
|
|
550
|
+
return;
|
|
551
|
+
};
|
|
552
|
+
const checkForExistence = (options, name) => {
|
|
553
|
+
var _a, _b;
|
|
554
|
+
if (!name) {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
if (options.headers.has(name) || ((_a = options.query) == null ? void 0 : _a[name]) || ((_b = options.headers.get("Cookie")) == null ? void 0 : _b.includes(`${name}=`))) {
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
return false;
|
|
561
|
+
};
|
|
562
|
+
const setAuthParams = async ({
|
|
563
|
+
security,
|
|
564
|
+
...options
|
|
565
|
+
}) => {
|
|
566
|
+
for (const auth of security) {
|
|
567
|
+
if (checkForExistence(options, auth.name)) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
const token = await getAuthToken(auth, options.auth);
|
|
571
|
+
if (!token) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const name = auth.name ?? "Authorization";
|
|
575
|
+
switch (auth.in) {
|
|
576
|
+
case "query":
|
|
577
|
+
if (!options.query) {
|
|
578
|
+
options.query = {};
|
|
579
|
+
}
|
|
580
|
+
options.query[name] = token;
|
|
581
|
+
break;
|
|
582
|
+
case "cookie":
|
|
583
|
+
options.headers.append("Cookie", `${name}=${token}`);
|
|
584
|
+
break;
|
|
585
|
+
case "header":
|
|
586
|
+
default:
|
|
587
|
+
options.headers.set(name, token);
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
const buildUrl = (options) => getUrl({
|
|
593
|
+
baseUrl: options.baseUrl,
|
|
594
|
+
path: options.path,
|
|
595
|
+
query: options.query,
|
|
596
|
+
querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
|
|
597
|
+
url: options.url
|
|
598
|
+
});
|
|
599
|
+
const mergeConfigs = (a, b) => {
|
|
600
|
+
var _a;
|
|
601
|
+
const config = { ...a, ...b };
|
|
602
|
+
if ((_a = config.baseUrl) == null ? void 0 : _a.endsWith("/")) {
|
|
603
|
+
config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
|
|
604
|
+
}
|
|
605
|
+
config.headers = mergeHeaders(a.headers, b.headers);
|
|
606
|
+
return config;
|
|
607
|
+
};
|
|
608
|
+
const headersEntries = (headers) => {
|
|
609
|
+
const entries = [];
|
|
610
|
+
headers.forEach((value, key) => {
|
|
611
|
+
entries.push([key, value]);
|
|
612
|
+
});
|
|
613
|
+
return entries;
|
|
614
|
+
};
|
|
615
|
+
const mergeHeaders = (...headers) => {
|
|
616
|
+
const mergedHeaders = new Headers();
|
|
617
|
+
for (const header of headers) {
|
|
618
|
+
if (!header) {
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
|
|
622
|
+
for (const [key, value] of iterator) {
|
|
623
|
+
if (value === null) {
|
|
624
|
+
mergedHeaders.delete(key);
|
|
625
|
+
} else if (Array.isArray(value)) {
|
|
626
|
+
for (const v of value) {
|
|
627
|
+
mergedHeaders.append(key, v);
|
|
628
|
+
}
|
|
629
|
+
} else if (value !== void 0) {
|
|
630
|
+
mergedHeaders.set(
|
|
631
|
+
key,
|
|
632
|
+
typeof value === "object" ? JSON.stringify(value) : value
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return mergedHeaders;
|
|
638
|
+
};
|
|
639
|
+
class Interceptors {
|
|
640
|
+
constructor() {
|
|
641
|
+
__publicField(this, "fns", []);
|
|
642
|
+
}
|
|
643
|
+
clear() {
|
|
644
|
+
this.fns = [];
|
|
645
|
+
}
|
|
646
|
+
eject(id) {
|
|
647
|
+
const index = this.getInterceptorIndex(id);
|
|
648
|
+
if (this.fns[index]) {
|
|
649
|
+
this.fns[index] = null;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
exists(id) {
|
|
653
|
+
const index = this.getInterceptorIndex(id);
|
|
654
|
+
return Boolean(this.fns[index]);
|
|
655
|
+
}
|
|
656
|
+
getInterceptorIndex(id) {
|
|
657
|
+
if (typeof id === "number") {
|
|
658
|
+
return this.fns[id] ? id : -1;
|
|
659
|
+
}
|
|
660
|
+
return this.fns.indexOf(id);
|
|
661
|
+
}
|
|
662
|
+
update(id, fn) {
|
|
663
|
+
const index = this.getInterceptorIndex(id);
|
|
664
|
+
if (this.fns[index]) {
|
|
665
|
+
this.fns[index] = fn;
|
|
666
|
+
return id;
|
|
667
|
+
}
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
use(fn) {
|
|
671
|
+
this.fns.push(fn);
|
|
672
|
+
return this.fns.length - 1;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
const createInterceptors = () => ({
|
|
676
|
+
error: new Interceptors(),
|
|
677
|
+
request: new Interceptors(),
|
|
678
|
+
response: new Interceptors()
|
|
679
|
+
});
|
|
680
|
+
const defaultQuerySerializer = createQuerySerializer({
|
|
681
|
+
allowReserved: false,
|
|
682
|
+
array: {
|
|
683
|
+
explode: true,
|
|
684
|
+
style: "form"
|
|
685
|
+
},
|
|
686
|
+
object: {
|
|
687
|
+
explode: true,
|
|
688
|
+
style: "deepObject"
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
const defaultHeaders = {
|
|
692
|
+
"Content-Type": "application/json"
|
|
693
|
+
};
|
|
694
|
+
const createConfig = (override = {}) => ({
|
|
695
|
+
...jsonBodySerializer,
|
|
696
|
+
headers: defaultHeaders,
|
|
697
|
+
parseAs: "auto",
|
|
698
|
+
querySerializer: defaultQuerySerializer,
|
|
699
|
+
...override
|
|
700
|
+
});
|
|
701
|
+
const createClient = (config = {}) => {
|
|
702
|
+
let _config = mergeConfigs(createConfig(), config);
|
|
703
|
+
const getConfig = () => ({ ..._config });
|
|
704
|
+
const setConfig = (config2) => {
|
|
705
|
+
_config = mergeConfigs(_config, config2);
|
|
706
|
+
return getConfig();
|
|
707
|
+
};
|
|
708
|
+
const interceptors = createInterceptors();
|
|
709
|
+
const beforeRequest = async (options) => {
|
|
710
|
+
const opts = {
|
|
711
|
+
..._config,
|
|
712
|
+
...options,
|
|
713
|
+
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
|
|
714
|
+
headers: mergeHeaders(_config.headers, options.headers),
|
|
715
|
+
serializedBody: void 0
|
|
716
|
+
};
|
|
717
|
+
if (opts.security) {
|
|
718
|
+
await setAuthParams({
|
|
719
|
+
...opts,
|
|
720
|
+
security: opts.security
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
if (opts.requestValidator) {
|
|
724
|
+
await opts.requestValidator(opts);
|
|
725
|
+
}
|
|
726
|
+
if (opts.body !== void 0 && opts.bodySerializer) {
|
|
727
|
+
opts.serializedBody = opts.bodySerializer(opts.body);
|
|
728
|
+
}
|
|
729
|
+
if (opts.body === void 0 || opts.serializedBody === "") {
|
|
730
|
+
opts.headers.delete("Content-Type");
|
|
731
|
+
}
|
|
732
|
+
const url = buildUrl(opts);
|
|
733
|
+
return { opts, url };
|
|
734
|
+
};
|
|
735
|
+
const request = async (options) => {
|
|
736
|
+
const { opts, url } = await beforeRequest(options);
|
|
737
|
+
const requestInit = {
|
|
738
|
+
redirect: "follow",
|
|
739
|
+
...opts,
|
|
740
|
+
body: getValidRequestBody(opts)
|
|
741
|
+
};
|
|
742
|
+
let request2 = new Request(url, requestInit);
|
|
743
|
+
for (const fn of interceptors.request.fns) {
|
|
744
|
+
if (fn) {
|
|
745
|
+
request2 = await fn(request2, opts);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
const _fetch = opts.fetch;
|
|
749
|
+
let response;
|
|
750
|
+
try {
|
|
751
|
+
response = await _fetch(request2);
|
|
752
|
+
} catch (error2) {
|
|
753
|
+
let finalError2 = error2;
|
|
754
|
+
for (const fn of interceptors.error.fns) {
|
|
755
|
+
if (fn) {
|
|
756
|
+
finalError2 = await fn(
|
|
757
|
+
error2,
|
|
758
|
+
void 0,
|
|
759
|
+
request2,
|
|
760
|
+
opts
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
finalError2 = finalError2 || {};
|
|
765
|
+
if (opts.throwOnError) {
|
|
766
|
+
throw finalError2;
|
|
767
|
+
}
|
|
768
|
+
return opts.responseStyle === "data" ? void 0 : {
|
|
769
|
+
error: finalError2,
|
|
770
|
+
request: request2,
|
|
771
|
+
response: void 0
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
for (const fn of interceptors.response.fns) {
|
|
775
|
+
if (fn) {
|
|
776
|
+
response = await fn(response, request2, opts);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const result = {
|
|
780
|
+
request: request2,
|
|
781
|
+
response
|
|
782
|
+
};
|
|
783
|
+
if (response.ok) {
|
|
784
|
+
const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
|
|
785
|
+
if (response.status === 204 || response.headers.get("Content-Length") === "0") {
|
|
786
|
+
let emptyData;
|
|
787
|
+
switch (parseAs) {
|
|
788
|
+
case "arrayBuffer":
|
|
789
|
+
case "blob":
|
|
790
|
+
case "text":
|
|
791
|
+
emptyData = await response[parseAs]();
|
|
792
|
+
break;
|
|
793
|
+
case "formData":
|
|
794
|
+
emptyData = new FormData();
|
|
795
|
+
break;
|
|
796
|
+
case "stream":
|
|
797
|
+
emptyData = response.body;
|
|
798
|
+
break;
|
|
799
|
+
case "json":
|
|
800
|
+
default:
|
|
801
|
+
emptyData = {};
|
|
802
|
+
break;
|
|
803
|
+
}
|
|
804
|
+
return opts.responseStyle === "data" ? emptyData : {
|
|
805
|
+
data: emptyData,
|
|
806
|
+
...result
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
let data;
|
|
810
|
+
switch (parseAs) {
|
|
811
|
+
case "arrayBuffer":
|
|
812
|
+
case "blob":
|
|
813
|
+
case "formData":
|
|
814
|
+
case "json":
|
|
815
|
+
case "text":
|
|
816
|
+
data = await response[parseAs]();
|
|
817
|
+
break;
|
|
818
|
+
case "stream":
|
|
819
|
+
return opts.responseStyle === "data" ? response.body : {
|
|
820
|
+
data: response.body,
|
|
821
|
+
...result
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
if (parseAs === "json") {
|
|
825
|
+
if (opts.responseValidator) {
|
|
826
|
+
await opts.responseValidator(data);
|
|
827
|
+
}
|
|
828
|
+
if (opts.responseTransformer) {
|
|
829
|
+
data = await opts.responseTransformer(data);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return opts.responseStyle === "data" ? data : {
|
|
833
|
+
data,
|
|
834
|
+
...result
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
const textError = await response.text();
|
|
838
|
+
let jsonError;
|
|
839
|
+
try {
|
|
840
|
+
jsonError = JSON.parse(textError);
|
|
841
|
+
} catch {
|
|
842
|
+
}
|
|
843
|
+
const error = jsonError ?? textError;
|
|
844
|
+
let finalError = error;
|
|
845
|
+
for (const fn of interceptors.error.fns) {
|
|
846
|
+
if (fn) {
|
|
847
|
+
finalError = await fn(error, response, request2, opts);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
finalError = finalError || {};
|
|
851
|
+
if (opts.throwOnError) {
|
|
852
|
+
throw finalError;
|
|
853
|
+
}
|
|
854
|
+
return opts.responseStyle === "data" ? void 0 : {
|
|
855
|
+
error: finalError,
|
|
856
|
+
...result
|
|
857
|
+
};
|
|
858
|
+
};
|
|
859
|
+
const makeMethodFn = (method) => (options) => request({ ...options, method });
|
|
860
|
+
const makeSseFn = (method) => async (options) => {
|
|
861
|
+
const { opts, url } = await beforeRequest(options);
|
|
862
|
+
return createSseClient({
|
|
863
|
+
...opts,
|
|
864
|
+
body: opts.body,
|
|
865
|
+
headers: opts.headers,
|
|
866
|
+
method,
|
|
867
|
+
onRequest: async (url2, init) => {
|
|
868
|
+
let request2 = new Request(url2, init);
|
|
869
|
+
for (const fn of interceptors.request.fns) {
|
|
870
|
+
if (fn) {
|
|
871
|
+
request2 = await fn(request2, opts);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return request2;
|
|
875
|
+
},
|
|
876
|
+
url
|
|
877
|
+
});
|
|
878
|
+
};
|
|
879
|
+
return {
|
|
880
|
+
buildUrl,
|
|
881
|
+
connect: makeMethodFn("CONNECT"),
|
|
882
|
+
delete: makeMethodFn("DELETE"),
|
|
883
|
+
get: makeMethodFn("GET"),
|
|
884
|
+
getConfig,
|
|
885
|
+
head: makeMethodFn("HEAD"),
|
|
886
|
+
interceptors,
|
|
887
|
+
options: makeMethodFn("OPTIONS"),
|
|
888
|
+
patch: makeMethodFn("PATCH"),
|
|
889
|
+
post: makeMethodFn("POST"),
|
|
890
|
+
put: makeMethodFn("PUT"),
|
|
891
|
+
request,
|
|
892
|
+
setConfig,
|
|
893
|
+
sse: {
|
|
894
|
+
connect: makeSseFn("CONNECT"),
|
|
895
|
+
delete: makeSseFn("DELETE"),
|
|
896
|
+
get: makeSseFn("GET"),
|
|
897
|
+
head: makeSseFn("HEAD"),
|
|
898
|
+
options: makeSseFn("OPTIONS"),
|
|
899
|
+
patch: makeSseFn("PATCH"),
|
|
900
|
+
post: makeSseFn("POST"),
|
|
901
|
+
put: makeSseFn("PUT"),
|
|
902
|
+
trace: makeSseFn("TRACE")
|
|
903
|
+
},
|
|
904
|
+
trace: makeMethodFn("TRACE")
|
|
905
|
+
};
|
|
906
|
+
};
|
|
907
|
+
const client = createClient(
|
|
908
|
+
createConfig({ baseUrl: "https://api.themolt.net" })
|
|
909
|
+
);
|
|
910
|
+
const registerAgent = (options) => (options.client ?? client).post({
|
|
911
|
+
url: "/auth/register",
|
|
912
|
+
...options,
|
|
913
|
+
headers: {
|
|
914
|
+
"Content-Type": "application/json",
|
|
915
|
+
...options.headers
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
ed.etc.sha512Sync = (...m) => {
|
|
919
|
+
const hash = createHash("sha512");
|
|
920
|
+
m.forEach((msg) => hash.update(msg));
|
|
921
|
+
return hash.digest();
|
|
922
|
+
};
|
|
923
|
+
const cryptoService = {
|
|
924
|
+
/**
|
|
925
|
+
* Generate a new Ed25519 keypair
|
|
926
|
+
*/
|
|
927
|
+
async generateKeyPair() {
|
|
928
|
+
const privateKeyBytes = ed.utils.randomPrivateKey();
|
|
929
|
+
const publicKeyBytes = await ed.getPublicKeyAsync(privateKeyBytes);
|
|
930
|
+
const privateKey = Buffer.from(privateKeyBytes).toString("base64");
|
|
931
|
+
const publicKey = `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
|
|
932
|
+
const fingerprint = this.generateFingerprint(publicKeyBytes);
|
|
933
|
+
return { publicKey, privateKey, fingerprint };
|
|
934
|
+
},
|
|
935
|
+
/**
|
|
936
|
+
* Generate human-readable fingerprint from public key
|
|
937
|
+
* Format: A1B2-C3D4-E5F6-G7H8 (first 16 hex chars of SHA256)
|
|
938
|
+
*/
|
|
939
|
+
generateFingerprint(publicKeyBytes) {
|
|
940
|
+
const hash = createHash("sha256").update(publicKeyBytes).digest("hex");
|
|
941
|
+
const segments = hash.slice(0, 16).toUpperCase().match(/.{4}/g) ?? [];
|
|
942
|
+
return segments.join("-");
|
|
943
|
+
},
|
|
944
|
+
/**
|
|
945
|
+
* Parse public key from string format
|
|
946
|
+
*/
|
|
947
|
+
parsePublicKey(publicKey) {
|
|
948
|
+
const base64 = publicKey.replace(/^ed25519:/, "");
|
|
949
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
950
|
+
},
|
|
951
|
+
/**
|
|
952
|
+
* Sign a message with private key
|
|
953
|
+
*/
|
|
954
|
+
async sign(message, privateKeyBase64) {
|
|
955
|
+
const privateKeyBytes = new Uint8Array(
|
|
956
|
+
Buffer.from(privateKeyBase64, "base64")
|
|
957
|
+
);
|
|
958
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
959
|
+
const signature = await ed.signAsync(messageBytes, privateKeyBytes);
|
|
960
|
+
return Buffer.from(signature).toString("base64");
|
|
961
|
+
},
|
|
962
|
+
/**
|
|
963
|
+
* Verify a signature against a message and public key
|
|
964
|
+
*/
|
|
965
|
+
async verify(message, signature, publicKey) {
|
|
966
|
+
try {
|
|
967
|
+
const publicKeyBytes = this.parsePublicKey(publicKey);
|
|
968
|
+
const signatureBytes = new Uint8Array(Buffer.from(signature, "base64"));
|
|
969
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
970
|
+
return await ed.verifyAsync(signatureBytes, messageBytes, publicKeyBytes);
|
|
971
|
+
} catch {
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
/**
|
|
976
|
+
* Create a signed message object
|
|
977
|
+
*/
|
|
978
|
+
async createSignedMessage(message, privateKeyBase64, publicKey) {
|
|
979
|
+
const signature = await this.sign(message, privateKeyBase64);
|
|
980
|
+
return { message, signature, publicKey };
|
|
981
|
+
},
|
|
982
|
+
/**
|
|
983
|
+
* Verify a signed message object
|
|
984
|
+
*/
|
|
985
|
+
async verifySignedMessage(signedMessage) {
|
|
986
|
+
return this.verify(
|
|
987
|
+
signedMessage.message,
|
|
988
|
+
signedMessage.signature,
|
|
989
|
+
signedMessage.publicKey
|
|
990
|
+
);
|
|
991
|
+
},
|
|
992
|
+
/**
|
|
993
|
+
* Generate a random challenge for authentication
|
|
994
|
+
*/
|
|
995
|
+
generateChallenge() {
|
|
996
|
+
return `moltnet:challenge:${randomBytes(32).toString("hex")}:${Date.now()}`;
|
|
997
|
+
},
|
|
998
|
+
/**
|
|
999
|
+
* Derive public key from private key
|
|
1000
|
+
*/
|
|
1001
|
+
async derivePublicKey(privateKeyBase64) {
|
|
1002
|
+
const privateKeyBytes = new Uint8Array(
|
|
1003
|
+
Buffer.from(privateKeyBase64, "base64")
|
|
1004
|
+
);
|
|
1005
|
+
const publicKeyBytes = await ed.getPublicKeyAsync(privateKeyBytes);
|
|
1006
|
+
return `ed25519:${Buffer.from(publicKeyBytes).toString("base64")}`;
|
|
1007
|
+
},
|
|
1008
|
+
/**
|
|
1009
|
+
* Get fingerprint from public key string
|
|
1010
|
+
*/
|
|
1011
|
+
getFingerprintFromPublicKey(publicKey) {
|
|
1012
|
+
const publicKeyBytes = this.parsePublicKey(publicKey);
|
|
1013
|
+
return this.generateFingerprint(publicKeyBytes);
|
|
1014
|
+
},
|
|
1015
|
+
/**
|
|
1016
|
+
* Create a proof of identity ownership (for DCR metadata)
|
|
1017
|
+
*/
|
|
1018
|
+
async createIdentityProof(identityId, privateKeyBase64) {
|
|
1019
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1020
|
+
const message = `moltnet:register:${identityId}:${timestamp}`;
|
|
1021
|
+
const signature = await this.sign(message, privateKeyBase64);
|
|
1022
|
+
return { message, signature, timestamp };
|
|
1023
|
+
},
|
|
1024
|
+
/**
|
|
1025
|
+
* Verify an identity proof
|
|
1026
|
+
*/
|
|
1027
|
+
async verifyIdentityProof(proof, publicKey, expectedIdentityId) {
|
|
1028
|
+
const isValid = await this.verify(
|
|
1029
|
+
proof.message,
|
|
1030
|
+
proof.signature,
|
|
1031
|
+
publicKey
|
|
1032
|
+
);
|
|
1033
|
+
if (!isValid) return false;
|
|
1034
|
+
const expectedPrefix = `moltnet:register:${expectedIdentityId}:`;
|
|
1035
|
+
if (!proof.message.startsWith(expectedPrefix)) return false;
|
|
1036
|
+
const proofTime = new Date(proof.timestamp).getTime();
|
|
1037
|
+
const now = Date.now();
|
|
1038
|
+
const fiveMinutes = 5 * 60 * 1e3;
|
|
1039
|
+
if (now - proofTime > fiveMinutes) return false;
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
const DEFAULT_API_URL = "https://api.themolt.net";
|
|
1044
|
+
function buildMcpConfig(apiUrl) {
|
|
1045
|
+
const base = apiUrl.replace(/\/$/, "");
|
|
1046
|
+
return {
|
|
1047
|
+
mcpServers: {
|
|
1048
|
+
moltnet: {
|
|
1049
|
+
url: `${base}/mcp`,
|
|
1050
|
+
transport: "sse"
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
async function register(options) {
|
|
1056
|
+
var _a;
|
|
1057
|
+
const apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
1058
|
+
const keyPair = await cryptoService.generateKeyPair();
|
|
1059
|
+
const client2 = createClient({ baseUrl: apiUrl });
|
|
1060
|
+
let data;
|
|
1061
|
+
try {
|
|
1062
|
+
const result = await registerAgent({
|
|
1063
|
+
client: client2,
|
|
1064
|
+
body: {
|
|
1065
|
+
public_key: keyPair.publicKey,
|
|
1066
|
+
voucher_code: options.voucherCode
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
if (result.error) {
|
|
1070
|
+
const problem = result.error;
|
|
1071
|
+
throw problemToError(problem, problem.status ?? 500);
|
|
1072
|
+
}
|
|
1073
|
+
if (!result.data) {
|
|
1074
|
+
throw new NetworkError("Empty response from registration endpoint");
|
|
1075
|
+
}
|
|
1076
|
+
data = result.data;
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
if (error instanceof NetworkError || error instanceof RegistrationError) {
|
|
1079
|
+
throw error;
|
|
1080
|
+
}
|
|
1081
|
+
throw new NetworkError(
|
|
1082
|
+
error instanceof Error ? error.message : "Registration request failed",
|
|
1083
|
+
{
|
|
1084
|
+
detail: error instanceof Error ? (_a = error.cause) == null ? void 0 : _a.toString() : String(error)
|
|
1085
|
+
}
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
return {
|
|
1089
|
+
identity: {
|
|
1090
|
+
publicKey: keyPair.publicKey,
|
|
1091
|
+
privateKey: keyPair.privateKey,
|
|
1092
|
+
fingerprint: data.fingerprint,
|
|
1093
|
+
identityId: data.identityId
|
|
1094
|
+
},
|
|
1095
|
+
credentials: {
|
|
1096
|
+
clientId: data.clientId,
|
|
1097
|
+
clientSecret: data.clientSecret
|
|
1098
|
+
},
|
|
1099
|
+
mcpConfig: buildMcpConfig(apiUrl),
|
|
1100
|
+
apiUrl
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
const MoltNet = { register };
|
|
1104
|
+
export {
|
|
1105
|
+
MoltNet,
|
|
1106
|
+
MoltNetError,
|
|
1107
|
+
NetworkError,
|
|
1108
|
+
RegistrationError,
|
|
1109
|
+
buildMcpConfig,
|
|
1110
|
+
getConfigDir,
|
|
1111
|
+
getCredentialsPath,
|
|
1112
|
+
problemToError,
|
|
1113
|
+
readCredentials,
|
|
1114
|
+
register,
|
|
1115
|
+
writeCredentials,
|
|
1116
|
+
writeMcpConfig
|
|
1117
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface RegisterOptions {
|
|
2
|
+
voucherCode: string;
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface McpConfig {
|
|
6
|
+
mcpServers: {
|
|
7
|
+
moltnet: {
|
|
8
|
+
url: string;
|
|
9
|
+
transport: 'sse';
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface RegisterResult {
|
|
14
|
+
identity: {
|
|
15
|
+
publicKey: string;
|
|
16
|
+
privateKey: string;
|
|
17
|
+
fingerprint: string;
|
|
18
|
+
identityId: string;
|
|
19
|
+
};
|
|
20
|
+
credentials: {
|
|
21
|
+
clientId: string;
|
|
22
|
+
clientSecret: string;
|
|
23
|
+
};
|
|
24
|
+
mcpConfig: McpConfig;
|
|
25
|
+
apiUrl: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function buildMcpConfig(apiUrl: string): McpConfig;
|
|
28
|
+
export declare function register(options: RegisterOptions): Promise<RegisterResult>;
|
|
29
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE;QACV,OAAO,EAAE;YACP,GAAG,EAAE,MAAM,CAAC;YACZ,SAAS,EAAE,KAAK,CAAC;SAClB,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,WAAW,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAUxD;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC,CAoDzB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@themoltnet/sdk",
|
|
3
|
+
"version": "0.0.0-seed",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "MoltNet Agent SDK — register identity, store credentials, configure MCP",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@noble/ed25519": "^2.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.11.0",
|
|
22
|
+
"typescript": "^5.3.3",
|
|
23
|
+
"vite": "^6.0.0",
|
|
24
|
+
"vitest": "^3.0.0",
|
|
25
|
+
"@moltnet/api-client": "0.1.0",
|
|
26
|
+
"@moltnet/crypto-service": "0.1.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "vite build && tsc -b --emitDeclarationOnly --force",
|
|
33
|
+
"typecheck": "tsc -b --emitDeclarationOnly",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"lint": "eslint src/"
|
|
36
|
+
}
|
|
37
|
+
}
|