@minimoth/sdk-node 0.1.0
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/LICENSE +21 -0
- package/README.md +216 -0
- package/dist/index.cjs +556 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +144 -0
- package/dist/index.d.ts +144 -0
- package/dist/index.js +514 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
// src/otp/OtpClient.ts
|
|
2
|
+
import jwt from "jsonwebtoken";
|
|
3
|
+
|
|
4
|
+
// src/MiniMothError.ts
|
|
5
|
+
var MiniMothError = class extends Error {
|
|
6
|
+
code;
|
|
7
|
+
statusCode;
|
|
8
|
+
constructor(code, message, statusCode, cause) {
|
|
9
|
+
super(message, cause !== void 0 ? { cause } : void 0);
|
|
10
|
+
this.name = "MiniMothError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.statusCode = statusCode;
|
|
13
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/http.ts
|
|
18
|
+
import { createRequire } from "module";
|
|
19
|
+
var require2 = createRequire(import.meta.url);
|
|
20
|
+
var pkg = require2("../package.json");
|
|
21
|
+
var BASE_URL = "https://api.minimoth.dev";
|
|
22
|
+
var USER_AGENT = `minimoth-sdk-node/${pkg.version}`;
|
|
23
|
+
var BACKEND_CODE_MAP = {
|
|
24
|
+
INVALID_ACCESS_TOKEN: "INVALID_TOKEN",
|
|
25
|
+
TOKEN_EXPIRED: "TOKEN_EXPIRED",
|
|
26
|
+
TOKEN_REVOKED: "TOKEN_REVOKED",
|
|
27
|
+
INVALID_OTP: "INVALID_OTP",
|
|
28
|
+
OTP_NOT_FOUND: "OTP_EXPIRED",
|
|
29
|
+
OTP_RATE_LIMITED: "RATE_LIMITED",
|
|
30
|
+
VERIFY_RATE_LIMITED: "RATE_LIMITED",
|
|
31
|
+
INSUFFICIENT_BALANCE: "INSUFFICIENT_BALANCE",
|
|
32
|
+
INVALID_PHONE: "INVALID_PHONE"
|
|
33
|
+
};
|
|
34
|
+
async function handleResponse(response) {
|
|
35
|
+
if (response.ok) return response.json();
|
|
36
|
+
let body = {};
|
|
37
|
+
try {
|
|
38
|
+
body = await response.json();
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
const sdkCode = BACKEND_CODE_MAP[body.code ?? ""] ?? "UNKNOWN_ERROR";
|
|
42
|
+
throw new MiniMothError(sdkCode, body.error ?? "Unknown error", response.status);
|
|
43
|
+
}
|
|
44
|
+
async function apiPost(path, apiKey, body) {
|
|
45
|
+
let response;
|
|
46
|
+
try {
|
|
47
|
+
response = await fetch(`${BASE_URL}${path}`, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: {
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
"X-Api-Key": apiKey,
|
|
52
|
+
"User-Agent": USER_AGENT
|
|
53
|
+
},
|
|
54
|
+
body: JSON.stringify(body)
|
|
55
|
+
});
|
|
56
|
+
} catch (err) {
|
|
57
|
+
throw new MiniMothError("NETWORK_ERROR", "Network request failed", 0, err);
|
|
58
|
+
}
|
|
59
|
+
return handleResponse(response);
|
|
60
|
+
}
|
|
61
|
+
async function apiGet(path) {
|
|
62
|
+
let response;
|
|
63
|
+
try {
|
|
64
|
+
response = await fetch(`${BASE_URL}${path}`, {
|
|
65
|
+
headers: { "User-Agent": USER_AGENT }
|
|
66
|
+
});
|
|
67
|
+
} catch (err) {
|
|
68
|
+
throw new MiniMothError("NETWORK_ERROR", "Network request failed", 0, err);
|
|
69
|
+
}
|
|
70
|
+
return handleResponse(response);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/otp/OtpClient.ts
|
|
74
|
+
var OtpClient = class {
|
|
75
|
+
constructor(apiKey, store, logger) {
|
|
76
|
+
this.apiKey = apiKey;
|
|
77
|
+
this.store = store;
|
|
78
|
+
this.logger = logger;
|
|
79
|
+
}
|
|
80
|
+
apiKey;
|
|
81
|
+
store;
|
|
82
|
+
logger;
|
|
83
|
+
async send(params) {
|
|
84
|
+
const phone = normalizePhone(params.phone);
|
|
85
|
+
await apiPost("/v1/otp/send", this.apiKey, { phone });
|
|
86
|
+
this.logger.info("otp.sent", { phone: maskPhone(phone) });
|
|
87
|
+
}
|
|
88
|
+
async verify(params) {
|
|
89
|
+
const phone = normalizePhone(params.phone);
|
|
90
|
+
validateOtp(params.otp);
|
|
91
|
+
try {
|
|
92
|
+
const res = await apiPost(
|
|
93
|
+
"/v1/otp/verify",
|
|
94
|
+
this.apiKey,
|
|
95
|
+
{ phone, code: params.otp }
|
|
96
|
+
);
|
|
97
|
+
const payload = jwt.decode(res.access_token);
|
|
98
|
+
if (!payload?.session_id) {
|
|
99
|
+
throw new MiniMothError("INVALID_TOKEN", "Could not decode session_id from access token", 0);
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
await this.store.setTokens(payload.session_id, {
|
|
103
|
+
accessToken: res.access_token,
|
|
104
|
+
refreshToken: res.refresh_token
|
|
105
|
+
});
|
|
106
|
+
} catch {
|
|
107
|
+
this.logger.error("otp.store.setTokens.failed", { sessionId: payload.session_id.slice(0, 8) });
|
|
108
|
+
}
|
|
109
|
+
this.logger.info("otp.verified", { sessionId: payload.session_id.slice(0, 8) });
|
|
110
|
+
return {
|
|
111
|
+
valid: true,
|
|
112
|
+
accessToken: res.access_token,
|
|
113
|
+
refreshToken: res.refresh_token,
|
|
114
|
+
sessionId: payload.session_id
|
|
115
|
+
};
|
|
116
|
+
} catch (err) {
|
|
117
|
+
if (err instanceof MiniMothError) {
|
|
118
|
+
if (err.code === "INVALID_OTP" || err.code === "OTP_EXPIRED" || err.code === "INVALID_PHONE") {
|
|
119
|
+
this.logger.debug("otp.verify.failed", { code: err.code });
|
|
120
|
+
return { valid: false, code: err.code };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
function normalizePhone(raw) {
|
|
128
|
+
if (!raw || typeof raw !== "string") {
|
|
129
|
+
throw new MiniMothError("INVALID_PHONE", "Phone number is required", 422);
|
|
130
|
+
}
|
|
131
|
+
if (/[\s-]/.test(raw)) {
|
|
132
|
+
throw new MiniMothError("INVALID_PHONE", "Phone number must not contain spaces or hyphens", 422);
|
|
133
|
+
}
|
|
134
|
+
let phone = raw;
|
|
135
|
+
if (phone.startsWith("+")) phone = phone.slice(1);
|
|
136
|
+
if (phone.length === 10) phone = "91" + phone;
|
|
137
|
+
return phone;
|
|
138
|
+
}
|
|
139
|
+
function validateOtp(otp) {
|
|
140
|
+
if (!otp || typeof otp !== "string") {
|
|
141
|
+
throw new MiniMothError("INVALID_OTP", "OTP is required", 422);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function maskPhone(phone) {
|
|
145
|
+
if (phone.length <= 4) return "****";
|
|
146
|
+
return phone.slice(0, 4) + "*".repeat(Math.max(phone.length - 6, 1)) + phone.slice(-2);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/session/SessionClient.ts
|
|
150
|
+
import jwt2 from "jsonwebtoken";
|
|
151
|
+
var SessionClient = class {
|
|
152
|
+
constructor(apiKey, jwksCache, revocationCache, deduplicator, store, logger) {
|
|
153
|
+
this.apiKey = apiKey;
|
|
154
|
+
this.jwksCache = jwksCache;
|
|
155
|
+
this.revocationCache = revocationCache;
|
|
156
|
+
this.deduplicator = deduplicator;
|
|
157
|
+
this.store = store;
|
|
158
|
+
this.logger = logger;
|
|
159
|
+
}
|
|
160
|
+
apiKey;
|
|
161
|
+
jwksCache;
|
|
162
|
+
revocationCache;
|
|
163
|
+
deduplicator;
|
|
164
|
+
store;
|
|
165
|
+
logger;
|
|
166
|
+
async validate(accessToken) {
|
|
167
|
+
const decoded = jwt2.decode(accessToken, { complete: true });
|
|
168
|
+
if (!decoded || typeof decoded === "string") {
|
|
169
|
+
throw new MiniMothError("INVALID_TOKEN", "Token is malformed", 401);
|
|
170
|
+
}
|
|
171
|
+
const kid = decoded.header["kid"];
|
|
172
|
+
if (!kid) throw new MiniMothError("INVALID_TOKEN", "Token is missing kid header", 401);
|
|
173
|
+
const publicKey = await this.jwksCache.getKey(kid);
|
|
174
|
+
let currentToken = accessToken;
|
|
175
|
+
let newTokens;
|
|
176
|
+
try {
|
|
177
|
+
jwt2.verify(accessToken, publicKey, { algorithms: ["RS256"] });
|
|
178
|
+
} catch (err) {
|
|
179
|
+
if (err instanceof jwt2.TokenExpiredError) {
|
|
180
|
+
const expiredPayload = decoded.payload;
|
|
181
|
+
const sessionId = expiredPayload?.session_id;
|
|
182
|
+
if (!sessionId) throw new MiniMothError("INVALID_TOKEN", "Token is invalid", 401);
|
|
183
|
+
newTokens = await this.deduplicator.getOrRun(sessionId, async () => {
|
|
184
|
+
const refreshToken = await this.store.getRefreshToken(sessionId);
|
|
185
|
+
if (!refreshToken) {
|
|
186
|
+
throw new MiniMothError("TOKEN_EXPIRED", "Access token expired and no refresh token available", 401);
|
|
187
|
+
}
|
|
188
|
+
this.logger.info("session.auto_refresh", { sessionId: sessionId.slice(0, 8) });
|
|
189
|
+
const res = await apiPost(
|
|
190
|
+
"/v1/session/refresh",
|
|
191
|
+
this.apiKey,
|
|
192
|
+
{ refresh_token: refreshToken }
|
|
193
|
+
);
|
|
194
|
+
try {
|
|
195
|
+
await this.store.setTokens(sessionId, {
|
|
196
|
+
accessToken: res.access_token,
|
|
197
|
+
refreshToken: res.refresh_token
|
|
198
|
+
});
|
|
199
|
+
} catch (err2) {
|
|
200
|
+
this.logger.error("session.store.setTokens.failed", { sessionId: sessionId.slice(0, 8) });
|
|
201
|
+
}
|
|
202
|
+
return { accessToken: res.access_token, refreshToken: res.refresh_token };
|
|
203
|
+
});
|
|
204
|
+
currentToken = newTokens.accessToken;
|
|
205
|
+
const newDecoded = jwt2.decode(currentToken, { complete: true });
|
|
206
|
+
const newKid = newDecoded?.header?.["kid"];
|
|
207
|
+
const newKey = newKid ? await this.jwksCache.getKey(newKid) : publicKey;
|
|
208
|
+
jwt2.verify(currentToken, newKey, { algorithms: ["RS256"] });
|
|
209
|
+
} else {
|
|
210
|
+
throw new MiniMothError("INVALID_TOKEN", "Token signature is invalid", 401);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const payload = jwt2.decode(currentToken);
|
|
214
|
+
const isValid = await this.revocationCache.check(payload.jti, currentToken);
|
|
215
|
+
if (!isValid) throw new MiniMothError("TOKEN_REVOKED", "Session has been revoked", 401);
|
|
216
|
+
const session = {
|
|
217
|
+
phone: payload.sub,
|
|
218
|
+
projectId: payload.project_id,
|
|
219
|
+
sessionId: payload.session_id,
|
|
220
|
+
expiresAt: new Date(payload.exp * 1e3)
|
|
221
|
+
};
|
|
222
|
+
if (newTokens) session.newTokens = newTokens;
|
|
223
|
+
this.logger.info("session.validated", { sessionId: payload.session_id.slice(0, 8) });
|
|
224
|
+
return session;
|
|
225
|
+
}
|
|
226
|
+
async safeValidate(accessToken) {
|
|
227
|
+
try {
|
|
228
|
+
const session = await this.validate(accessToken);
|
|
229
|
+
return { valid: true, session };
|
|
230
|
+
} catch (err) {
|
|
231
|
+
if (err instanceof MiniMothError) return { valid: false, code: err.code };
|
|
232
|
+
throw err;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async refresh(refreshToken) {
|
|
236
|
+
const res = await apiPost(
|
|
237
|
+
"/v1/session/refresh",
|
|
238
|
+
this.apiKey,
|
|
239
|
+
{ refresh_token: refreshToken }
|
|
240
|
+
);
|
|
241
|
+
const payload = jwt2.decode(res.access_token);
|
|
242
|
+
if (payload?.session_id) {
|
|
243
|
+
try {
|
|
244
|
+
await this.store.setTokens(payload.session_id, {
|
|
245
|
+
accessToken: res.access_token,
|
|
246
|
+
refreshToken: res.refresh_token
|
|
247
|
+
});
|
|
248
|
+
} catch (err) {
|
|
249
|
+
this.logger.error("session.store.setTokens.failed", { sessionId: payload.session_id.slice(0, 8) });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
this.logger.info("session.refreshed");
|
|
253
|
+
return { accessToken: res.access_token, refreshToken: res.refresh_token };
|
|
254
|
+
}
|
|
255
|
+
async logout(params) {
|
|
256
|
+
const payload = jwt2.decode(params.accessToken);
|
|
257
|
+
const sessionId = payload?.session_id ?? null;
|
|
258
|
+
if (!sessionId) throw new MiniMothError("INVALID_TOKEN", "Cannot decode token", 401);
|
|
259
|
+
let currentAccessToken = params.accessToken;
|
|
260
|
+
if (payload && payload.exp < Math.floor(Date.now() / 1e3)) {
|
|
261
|
+
const newTokens = await this.deduplicator.getOrRun(sessionId, async () => {
|
|
262
|
+
const res = await apiPost(
|
|
263
|
+
"/v1/session/refresh",
|
|
264
|
+
this.apiKey,
|
|
265
|
+
{ refresh_token: params.refreshToken }
|
|
266
|
+
);
|
|
267
|
+
return { accessToken: res.access_token, refreshToken: res.refresh_token };
|
|
268
|
+
});
|
|
269
|
+
currentAccessToken = newTokens.accessToken;
|
|
270
|
+
try {
|
|
271
|
+
await this.store.setTokens(sessionId, newTokens);
|
|
272
|
+
} catch (err) {
|
|
273
|
+
this.logger.error("session.store.setTokens.failed", { sessionId: sessionId.slice(0, 8) });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
await apiPost(
|
|
277
|
+
"/v1/session/logout",
|
|
278
|
+
this.apiKey,
|
|
279
|
+
{ access_token: currentAccessToken }
|
|
280
|
+
);
|
|
281
|
+
try {
|
|
282
|
+
await this.store.deleteSession(sessionId);
|
|
283
|
+
} catch (err) {
|
|
284
|
+
this.logger.error("session.store.deleteSession.failed", { sessionId: sessionId.slice(0, 8) });
|
|
285
|
+
}
|
|
286
|
+
this.logger.info("session.logged_out", { sessionId: sessionId.slice(0, 8) });
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// src/session/JwksCache.ts
|
|
291
|
+
import crypto from "crypto";
|
|
292
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
293
|
+
var JwksCache = class {
|
|
294
|
+
constructor(logger) {
|
|
295
|
+
this.logger = logger;
|
|
296
|
+
}
|
|
297
|
+
logger;
|
|
298
|
+
keyMap = /* @__PURE__ */ new Map();
|
|
299
|
+
fetchedAt = 0;
|
|
300
|
+
fetching = null;
|
|
301
|
+
async getKey(kid) {
|
|
302
|
+
if (this.keyMap.size === 0 || Date.now() - this.fetchedAt > CACHE_TTL_MS) {
|
|
303
|
+
await this.fetch();
|
|
304
|
+
}
|
|
305
|
+
let key = this.keyMap.get(kid);
|
|
306
|
+
if (!key) {
|
|
307
|
+
this.logger.debug("jwks.unknown_kid", { kid });
|
|
308
|
+
await this.fetch();
|
|
309
|
+
key = this.keyMap.get(kid);
|
|
310
|
+
}
|
|
311
|
+
if (!key) throw new MiniMothError("INVALID_TOKEN", `No public key found for kid: ${kid}`, 401);
|
|
312
|
+
return key;
|
|
313
|
+
}
|
|
314
|
+
fetch() {
|
|
315
|
+
if (this.fetching) return this.fetching;
|
|
316
|
+
this.fetching = this.doFetch().finally(() => {
|
|
317
|
+
this.fetching = null;
|
|
318
|
+
});
|
|
319
|
+
return this.fetching;
|
|
320
|
+
}
|
|
321
|
+
async doFetch() {
|
|
322
|
+
this.logger.debug("jwks.fetch");
|
|
323
|
+
const data = await apiGet("/.well-known/jwks.json");
|
|
324
|
+
const newMap = /* @__PURE__ */ new Map();
|
|
325
|
+
for (const jwk of data.keys) {
|
|
326
|
+
if (jwk["kid"] && jwk["kty"] === "RSA") {
|
|
327
|
+
const key = crypto.createPublicKey({ key: jwk, format: "jwk" });
|
|
328
|
+
newMap.set(jwk["kid"], key);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
this.keyMap = newMap;
|
|
332
|
+
this.fetchedAt = Date.now();
|
|
333
|
+
this.logger.debug("jwks.updated", { keyCount: newMap.size });
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// src/session/RevocationCache.ts
|
|
338
|
+
var MODE_TTL_MS = {
|
|
339
|
+
recheck_1m: 6e4,
|
|
340
|
+
recheck_3m: 18e4
|
|
341
|
+
};
|
|
342
|
+
var EVICTION_TTL_MS = 5 * 60 * 1e3;
|
|
343
|
+
var CLEANUP_WINDOW_MS = 5 * 60 * 1e3;
|
|
344
|
+
var RevocationCache = class {
|
|
345
|
+
constructor(mode, apiKey, logger) {
|
|
346
|
+
this.mode = mode;
|
|
347
|
+
this.apiKey = apiKey;
|
|
348
|
+
this.logger = logger;
|
|
349
|
+
}
|
|
350
|
+
mode;
|
|
351
|
+
apiKey;
|
|
352
|
+
logger;
|
|
353
|
+
cache = /* @__PURE__ */ new Map();
|
|
354
|
+
lastCleanedAt = 0;
|
|
355
|
+
async check(jti, accessToken) {
|
|
356
|
+
if (this.mode === "instant") return true;
|
|
357
|
+
if (this.mode !== "strict") this.maybeCleanup();
|
|
358
|
+
const ttl = MODE_TTL_MS[this.mode] ?? 0;
|
|
359
|
+
if (ttl > 0) {
|
|
360
|
+
const entry = this.cache.get(jti);
|
|
361
|
+
if (entry && Date.now() - entry.checkedAt < ttl) {
|
|
362
|
+
this.logger.debug("revocation.cache.hit", { jti: jti.slice(0, 8) });
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
const result = await apiPost(
|
|
368
|
+
"/v1/session/validate",
|
|
369
|
+
this.apiKey,
|
|
370
|
+
{ access_token: accessToken }
|
|
371
|
+
);
|
|
372
|
+
if (result.valid && ttl > 0) {
|
|
373
|
+
this.cache.set(jti, { checkedAt: Date.now() });
|
|
374
|
+
}
|
|
375
|
+
return result.valid;
|
|
376
|
+
} catch (err) {
|
|
377
|
+
if (this.mode === "strict") throw err;
|
|
378
|
+
this.logger.warn("revocation.check.failed_open", {
|
|
379
|
+
jti: jti.slice(0, 8),
|
|
380
|
+
error: err instanceof Error ? err.message : "unknown"
|
|
381
|
+
});
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
maybeCleanup() {
|
|
386
|
+
const now = Date.now();
|
|
387
|
+
if (now - this.lastCleanedAt < CLEANUP_WINDOW_MS) return;
|
|
388
|
+
this.lastCleanedAt = now;
|
|
389
|
+
for (const [jti, entry] of this.cache) {
|
|
390
|
+
if (now - entry.checkedAt > EVICTION_TTL_MS) {
|
|
391
|
+
this.cache.delete(jti);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// src/session/RefreshDeduplicator.ts
|
|
398
|
+
var RefreshDeduplicator = class {
|
|
399
|
+
constructor(logger) {
|
|
400
|
+
this.logger = logger;
|
|
401
|
+
}
|
|
402
|
+
logger;
|
|
403
|
+
inflight = /* @__PURE__ */ new Map();
|
|
404
|
+
getOrRun(sessionId, fn) {
|
|
405
|
+
const existing = this.inflight.get(sessionId);
|
|
406
|
+
if (existing) {
|
|
407
|
+
this.logger.debug("refresh dedup hit", { key: sessionId });
|
|
408
|
+
return existing;
|
|
409
|
+
}
|
|
410
|
+
const promise = fn().finally(() => {
|
|
411
|
+
this.inflight.delete(sessionId);
|
|
412
|
+
});
|
|
413
|
+
this.inflight.set(sessionId, promise);
|
|
414
|
+
return promise;
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// src/store/DefaultSessionStore.ts
|
|
419
|
+
var DefaultSessionStore = class {
|
|
420
|
+
constructor(logger) {
|
|
421
|
+
this.logger = logger;
|
|
422
|
+
}
|
|
423
|
+
logger;
|
|
424
|
+
map = /* @__PURE__ */ new Map();
|
|
425
|
+
warnedProduction = false;
|
|
426
|
+
maybeWarn() {
|
|
427
|
+
if (process.env["NODE_ENV"] === "production" && !this.warnedProduction) {
|
|
428
|
+
this.warnedProduction = true;
|
|
429
|
+
this.logger.warn("using default in-memory session store \u2014 not suitable for multi-replica deployments");
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async getRefreshToken(sessionId) {
|
|
433
|
+
this.maybeWarn();
|
|
434
|
+
return this.map.get(sessionId)?.refreshToken ?? null;
|
|
435
|
+
}
|
|
436
|
+
async setTokens(sessionId, tokens) {
|
|
437
|
+
this.maybeWarn();
|
|
438
|
+
this.map.set(sessionId, tokens);
|
|
439
|
+
}
|
|
440
|
+
async deleteSession(sessionId) {
|
|
441
|
+
this.map.delete(sessionId);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// src/logger/ConsoleLogger.ts
|
|
446
|
+
var LEVEL_ORDER = {
|
|
447
|
+
debug: 0,
|
|
448
|
+
info: 1,
|
|
449
|
+
warn: 2,
|
|
450
|
+
error: 3,
|
|
451
|
+
silent: 4
|
|
452
|
+
};
|
|
453
|
+
var ConsoleLogger = class {
|
|
454
|
+
level;
|
|
455
|
+
constructor(level = "info") {
|
|
456
|
+
this.level = level;
|
|
457
|
+
}
|
|
458
|
+
setLevel(level) {
|
|
459
|
+
this.level = level;
|
|
460
|
+
}
|
|
461
|
+
shouldLog(level) {
|
|
462
|
+
return LEVEL_ORDER[level] >= LEVEL_ORDER[this.level];
|
|
463
|
+
}
|
|
464
|
+
format(msg, meta) {
|
|
465
|
+
if (!meta || Object.keys(meta).length === 0) return `[minimoth] ${msg}`;
|
|
466
|
+
return `[minimoth] ${msg} ${JSON.stringify(meta)}`;
|
|
467
|
+
}
|
|
468
|
+
debug(msg, meta) {
|
|
469
|
+
if (this.shouldLog("debug")) console.debug(this.format(msg, meta));
|
|
470
|
+
}
|
|
471
|
+
info(msg, meta) {
|
|
472
|
+
if (this.shouldLog("info")) console.info(this.format(msg, meta));
|
|
473
|
+
}
|
|
474
|
+
warn(msg, meta) {
|
|
475
|
+
if (this.shouldLog("warn")) console.warn(this.format(msg, meta));
|
|
476
|
+
}
|
|
477
|
+
error(msg, meta) {
|
|
478
|
+
if (this.shouldLog("error")) console.error(this.format(msg, meta));
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
// src/MiniMoth.ts
|
|
483
|
+
var MiniMoth = class {
|
|
484
|
+
otp;
|
|
485
|
+
session;
|
|
486
|
+
consoleLogger;
|
|
487
|
+
constructor(config) {
|
|
488
|
+
this.consoleLogger = new ConsoleLogger(config.logLevel ?? "info");
|
|
489
|
+
const logger = config.logger ?? this.consoleLogger;
|
|
490
|
+
const store = config.session?.store ?? new DefaultSessionStore(logger);
|
|
491
|
+
const validateMode = config.session?.validateMode ?? "instant";
|
|
492
|
+
const jwksCache = new JwksCache(logger);
|
|
493
|
+
const revocationCache = new RevocationCache(validateMode, config.apiKey, logger);
|
|
494
|
+
const deduplicator = new RefreshDeduplicator(logger);
|
|
495
|
+
this.otp = new OtpClient(config.apiKey, store, logger);
|
|
496
|
+
this.session = new SessionClient(
|
|
497
|
+
config.apiKey,
|
|
498
|
+
jwksCache,
|
|
499
|
+
revocationCache,
|
|
500
|
+
deduplicator,
|
|
501
|
+
store,
|
|
502
|
+
logger
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
// Only affects the built-in ConsoleLogger. Has no effect if a custom logger was provided.
|
|
506
|
+
setLogLevel(level) {
|
|
507
|
+
this.consoleLogger.setLevel(level);
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
export {
|
|
511
|
+
MiniMoth,
|
|
512
|
+
MiniMothError
|
|
513
|
+
};
|
|
514
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/otp/OtpClient.ts","../src/MiniMothError.ts","../src/http.ts","../src/session/SessionClient.ts","../src/session/JwksCache.ts","../src/session/RevocationCache.ts","../src/session/RefreshDeduplicator.ts","../src/store/DefaultSessionStore.ts","../src/logger/ConsoleLogger.ts","../src/MiniMoth.ts"],"sourcesContent":["import jwt from 'jsonwebtoken'\nimport { apiPost } from '../http'\nimport { MiniMothError, type MiniMothErrorCode } from '../MiniMothError'\nimport type { MiniMothSessionStore } from '../store/MiniMothSessionStore'\nimport type { MiniMothLogger } from '../logger/MiniMothLogger'\nimport type { AccessTokenPayload } from '../types'\n\ntype VerifyResult =\n | { valid: true; accessToken: string; refreshToken: string; sessionId: string }\n | { valid: false; code: MiniMothErrorCode }\n\nexport class OtpClient {\n constructor(\n private readonly apiKey: string,\n private readonly store: MiniMothSessionStore,\n private readonly logger: MiniMothLogger\n ) {}\n\n async send(params: { phone: string }): Promise<void> {\n const phone = normalizePhone(params.phone)\n await apiPost<{ message: string }>('/v1/otp/send', this.apiKey, { phone })\n this.logger.info('otp.sent', { phone: maskPhone(phone) })\n }\n\n async verify(params: { phone: string; otp: string }): Promise<VerifyResult> {\n const phone = normalizePhone(params.phone)\n validateOtp(params.otp)\n\n try {\n const res = await apiPost<{ access_token: string; refresh_token: string; expires_at: string }>(\n '/v1/otp/verify',\n this.apiKey,\n { phone, code: params.otp }\n )\n\n const payload = jwt.decode(res.access_token) as AccessTokenPayload | null\n if (!payload?.session_id) {\n throw new MiniMothError('INVALID_TOKEN', 'Could not decode session_id from access token', 0)\n }\n\n try {\n await this.store.setTokens(payload.session_id, {\n accessToken: res.access_token,\n refreshToken: res.refresh_token,\n })\n } catch {\n this.logger.error('otp.store.setTokens.failed', { sessionId: payload.session_id.slice(0, 8) })\n }\n\n this.logger.info('otp.verified', { sessionId: payload.session_id.slice(0, 8) })\n\n return {\n valid: true,\n accessToken: res.access_token,\n refreshToken: res.refresh_token,\n sessionId: payload.session_id,\n }\n } catch (err) {\n if (err instanceof MiniMothError) {\n if (err.code === 'INVALID_OTP' || err.code === 'OTP_EXPIRED' || err.code === 'INVALID_PHONE') {\n this.logger.debug('otp.verify.failed', { code: err.code })\n return { valid: false, code: err.code }\n }\n }\n throw err\n }\n }\n}\n\n// Normalizes to 12-digit canonical Indian format (91XXXXXXXXXX).\n// Rejects if spaces or hyphens are present.\nfunction normalizePhone(raw: string): string {\n if (!raw || typeof raw !== 'string') {\n throw new MiniMothError('INVALID_PHONE', 'Phone number is required', 422)\n }\n if (/[\\s-]/.test(raw)) {\n throw new MiniMothError('INVALID_PHONE', 'Phone number must not contain spaces or hyphens', 422)\n }\n let phone = raw\n if (phone.startsWith('+')) phone = phone.slice(1)\n // 10-digit subscriber number — prepend country code\n if (phone.length === 10) phone = '91' + phone\n return phone\n}\n\nfunction validateOtp(otp: string): void {\n if (!otp || typeof otp !== 'string') {\n throw new MiniMothError('INVALID_OTP', 'OTP is required', 422)\n }\n}\n\nexport function maskPhone(phone: string): string {\n if (phone.length <= 4) return '****'\n return phone.slice(0, 4) + '*'.repeat(Math.max(phone.length - 6, 1)) + phone.slice(-2)\n}\n","export type MiniMothErrorCode =\n | 'TOKEN_EXPIRED'\n | 'TOKEN_REVOKED'\n | 'INVALID_TOKEN'\n | 'INVALID_OTP'\n | 'OTP_EXPIRED'\n | 'RATE_LIMITED'\n | 'INSUFFICIENT_BALANCE'\n | 'NETWORK_ERROR'\n | 'INVALID_PHONE'\n | 'UNKNOWN_ERROR'\n\nexport class MiniMothError extends Error {\n readonly code: MiniMothErrorCode\n readonly statusCode: number\n\n constructor(code: MiniMothErrorCode, message: string, statusCode: number, cause?: unknown) {\n super(message, cause !== undefined ? { cause } : undefined)\n this.name = 'MiniMothError'\n this.code = code\n this.statusCode = statusCode\n // Restore prototype chain for instanceof checks across module boundaries\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n","import { MiniMothError, type MiniMothErrorCode } from './MiniMothError'\nimport { createRequire } from 'module'\n\nconst require = createRequire(import.meta.url)\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nconst pkg = require('../package.json') as { version: string }\n\nconst BASE_URL = 'https://api.minimoth.dev'\nconst USER_AGENT = `minimoth-sdk-node/${pkg.version}`\n\nconst BACKEND_CODE_MAP: Record<string, MiniMothErrorCode> = {\n INVALID_ACCESS_TOKEN: 'INVALID_TOKEN',\n TOKEN_EXPIRED: 'TOKEN_EXPIRED',\n TOKEN_REVOKED: 'TOKEN_REVOKED',\n INVALID_OTP: 'INVALID_OTP',\n OTP_NOT_FOUND: 'OTP_EXPIRED',\n OTP_RATE_LIMITED: 'RATE_LIMITED',\n VERIFY_RATE_LIMITED: 'RATE_LIMITED',\n INSUFFICIENT_BALANCE: 'INSUFFICIENT_BALANCE',\n INVALID_PHONE: 'INVALID_PHONE',\n}\n\nasync function handleResponse<T>(response: Response): Promise<T> {\n if (response.ok) return response.json() as Promise<T>\n\n let body: { code?: string; error?: string } = {}\n try { body = await response.json() as { code?: string; error?: string } } catch {}\n\n const sdkCode: MiniMothErrorCode = BACKEND_CODE_MAP[body.code ?? ''] ?? 'UNKNOWN_ERROR'\n throw new MiniMothError(sdkCode, body.error ?? 'Unknown error', response.status)\n}\n\nexport async function apiPost<T>(\n path: string,\n apiKey: string,\n body: Record<string, unknown>\n): Promise<T> {\n let response: Response\n try {\n response = await fetch(`${BASE_URL}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Api-Key': apiKey,\n 'User-Agent': USER_AGENT,\n },\n body: JSON.stringify(body),\n })\n } catch (err) {\n throw new MiniMothError('NETWORK_ERROR', 'Network request failed', 0, err)\n }\n return handleResponse<T>(response)\n}\n\nexport async function apiGet<T>(path: string): Promise<T> {\n let response: Response\n try {\n response = await fetch(`${BASE_URL}${path}`, {\n headers: { 'User-Agent': USER_AGENT },\n })\n } catch (err) {\n throw new MiniMothError('NETWORK_ERROR', 'Network request failed', 0, err)\n }\n return handleResponse<T>(response)\n}\n","import jwt from 'jsonwebtoken'\nimport { apiPost } from '../http'\nimport { MiniMothError, type MiniMothErrorCode } from '../MiniMothError'\nimport type { JwksCache } from './JwksCache'\nimport type { RevocationCache } from './RevocationCache'\nimport type { RefreshDeduplicator } from './RefreshDeduplicator'\nimport type { MiniMothSessionStore } from '../store/MiniMothSessionStore'\nimport type { MiniMothLogger } from '../logger/MiniMothLogger'\nimport type { Session, AccessTokenPayload } from '../types'\n\ntype Tokens = { accessToken: string; refreshToken: string }\n\ntype SafeValidateResult =\n | { valid: true; session: Session }\n | { valid: false; code: MiniMothErrorCode }\n\nexport class SessionClient {\n constructor(\n private readonly apiKey: string,\n private readonly jwksCache: JwksCache,\n private readonly revocationCache: RevocationCache,\n private readonly deduplicator: RefreshDeduplicator,\n private readonly store: MiniMothSessionStore,\n private readonly logger: MiniMothLogger\n ) {}\n\n async validate(accessToken: string): Promise<Session> {\n const decoded = jwt.decode(accessToken, { complete: true })\n if (!decoded || typeof decoded === 'string') {\n throw new MiniMothError('INVALID_TOKEN', 'Token is malformed', 401)\n }\n\n const kid = (decoded.header as unknown as Record<string, string>)['kid']\n if (!kid) throw new MiniMothError('INVALID_TOKEN', 'Token is missing kid header', 401)\n\n const publicKey = await this.jwksCache.getKey(kid)\n\n let currentToken = accessToken\n let newTokens: Tokens | undefined\n\n try {\n jwt.verify(accessToken, publicKey, { algorithms: ['RS256'] })\n } catch (err) {\n if (err instanceof jwt.TokenExpiredError) {\n const expiredPayload = decoded.payload as AccessTokenPayload\n const sessionId = expiredPayload?.session_id\n if (!sessionId) throw new MiniMothError('INVALID_TOKEN', 'Token is invalid', 401)\n\n newTokens = await this.deduplicator.getOrRun(sessionId, async () => {\n const refreshToken = await this.store.getRefreshToken(sessionId)\n if (!refreshToken) {\n throw new MiniMothError('TOKEN_EXPIRED', 'Access token expired and no refresh token available', 401)\n }\n\n this.logger.info('session.auto_refresh', { sessionId: sessionId.slice(0, 8) })\n const res = await apiPost<{ access_token: string; refresh_token: string }>(\n '/v1/session/refresh',\n this.apiKey,\n { refresh_token: refreshToken }\n )\n\n try {\n await this.store.setTokens(sessionId, {\n accessToken: res.access_token,\n refreshToken: res.refresh_token,\n })\n } catch (err) {\n this.logger.error('session.store.setTokens.failed', { sessionId: sessionId.slice(0, 8) })\n }\n\n return { accessToken: res.access_token, refreshToken: res.refresh_token }\n })\n\n currentToken = newTokens.accessToken\n\n // Verify the new token\n const newDecoded = jwt.decode(currentToken, { complete: true })\n const newKid = (newDecoded?.header as Record<string, string> | undefined)?.['kid']\n const newKey = newKid ? await this.jwksCache.getKey(newKid) : publicKey\n jwt.verify(currentToken, newKey, { algorithms: ['RS256'] })\n } else {\n throw new MiniMothError('INVALID_TOKEN', 'Token signature is invalid', 401)\n }\n }\n\n const payload = jwt.decode(currentToken) as AccessTokenPayload\n const isValid = await this.revocationCache.check(payload.jti, currentToken)\n if (!isValid) throw new MiniMothError('TOKEN_REVOKED', 'Session has been revoked', 401)\n\n const session: Session = {\n phone: payload.sub,\n projectId: payload.project_id,\n sessionId: payload.session_id,\n expiresAt: new Date(payload.exp * 1000),\n }\n if (newTokens) session.newTokens = newTokens\n\n this.logger.info('session.validated', { sessionId: payload.session_id.slice(0, 8) })\n return session\n }\n\n async safeValidate(accessToken: string): Promise<SafeValidateResult> {\n try {\n const session = await this.validate(accessToken)\n return { valid: true, session }\n } catch (err) {\n if (err instanceof MiniMothError) return { valid: false, code: err.code }\n throw err\n }\n }\n\n async refresh(refreshToken: string): Promise<Tokens> {\n const res = await apiPost<{ access_token: string; refresh_token: string }>(\n '/v1/session/refresh',\n this.apiKey,\n { refresh_token: refreshToken }\n )\n\n // Update store if session_id is decodable from new token\n const payload = jwt.decode(res.access_token) as AccessTokenPayload | null\n if (payload?.session_id) {\n try {\n await this.store.setTokens(payload.session_id, {\n accessToken: res.access_token,\n refreshToken: res.refresh_token,\n })\n } catch (err) {\n this.logger.error('session.store.setTokens.failed', { sessionId: payload.session_id.slice(0, 8) })\n }\n }\n\n this.logger.info('session.refreshed')\n return { accessToken: res.access_token, refreshToken: res.refresh_token }\n }\n\n async logout(params: { accessToken: string; refreshToken: string }): Promise<void> {\n const payload = jwt.decode(params.accessToken) as AccessTokenPayload | null\n const sessionId = payload?.session_id ?? null\n\n if (!sessionId) throw new MiniMothError('INVALID_TOKEN', 'Cannot decode token', 401)\n\n let currentAccessToken = params.accessToken\n\n // If token is expired, refresh before logout — deduplicated to prevent concurrent refresh races\n if (payload && payload.exp < Math.floor(Date.now() / 1000)) {\n const newTokens = await this.deduplicator.getOrRun(sessionId, async () => {\n const res = await apiPost<{ access_token: string; refresh_token: string }>(\n '/v1/session/refresh',\n this.apiKey,\n { refresh_token: params.refreshToken }\n )\n return { accessToken: res.access_token, refreshToken: res.refresh_token }\n })\n currentAccessToken = newTokens.accessToken\n try {\n await this.store.setTokens(sessionId, newTokens)\n } catch (err) {\n this.logger.error('session.store.setTokens.failed', { sessionId: sessionId.slice(0, 8) })\n }\n }\n\n await apiPost<{ message: string }>(\n '/v1/session/logout',\n this.apiKey,\n { access_token: currentAccessToken }\n )\n\n try {\n await this.store.deleteSession(sessionId)\n } catch (err) {\n this.logger.error('session.store.deleteSession.failed', { sessionId: sessionId.slice(0, 8) })\n }\n this.logger.info('session.logged_out', { sessionId: sessionId.slice(0, 8) })\n }\n}\n","import crypto from 'crypto'\nimport { apiGet } from '../http'\nimport { MiniMothError } from '../MiniMothError'\nimport type { MiniMothLogger } from '../logger/MiniMothLogger'\n\nconst CACHE_TTL_MS = 60 * 60 * 1000 // 1 hour\n\ntype JwkEntry = Record<string, string>\n\nexport class JwksCache {\n private keyMap = new Map<string, crypto.KeyObject>()\n private fetchedAt = 0\n private fetching: Promise<void> | null = null\n\n constructor(private readonly logger: MiniMothLogger) {}\n\n async getKey(kid: string): Promise<crypto.KeyObject> {\n if (this.keyMap.size === 0 || Date.now() - this.fetchedAt > CACHE_TTL_MS) {\n await this.fetch()\n }\n\n let key = this.keyMap.get(kid)\n if (!key) {\n this.logger.debug('jwks.unknown_kid', { kid })\n await this.fetch()\n key = this.keyMap.get(kid)\n }\n\n if (!key) throw new MiniMothError('INVALID_TOKEN', `No public key found for kid: ${kid}`, 401)\n return key\n }\n\n private fetch(): Promise<void> {\n if (this.fetching) return this.fetching\n this.fetching = this.doFetch().finally(() => { this.fetching = null })\n return this.fetching\n }\n\n private async doFetch(): Promise<void> {\n this.logger.debug('jwks.fetch')\n const data = await apiGet<{ keys: JwkEntry[] }>('/.well-known/jwks.json')\n const newMap = new Map<string, crypto.KeyObject>()\n for (const jwk of data.keys) {\n if (jwk['kid'] && jwk['kty'] === 'RSA') {\n const key = crypto.createPublicKey({ key: jwk as unknown as crypto.JsonWebKey, format: 'jwk' })\n newMap.set(jwk['kid'], key)\n }\n }\n this.keyMap = newMap\n this.fetchedAt = Date.now()\n this.logger.debug('jwks.updated', { keyCount: newMap.size })\n }\n}\n","import { apiPost } from '../http'\nimport type { MiniMothLogger } from '../logger/MiniMothLogger'\nimport type { ValidateMode } from '../types'\n\nconst MODE_TTL_MS: Partial<Record<ValidateMode, number>> = {\n recheck_1m: 60_000,\n recheck_3m: 180_000,\n}\n\nconst EVICTION_TTL_MS = 5 * 60 * 1000 // JWT max lifetime\nconst CLEANUP_WINDOW_MS = 5 * 60 * 1000\n\ntype Entry = { checkedAt: number }\n\nexport class RevocationCache {\n private readonly cache = new Map<string, Entry>()\n private lastCleanedAt = 0\n\n constructor(\n private readonly mode: ValidateMode,\n private readonly apiKey: string,\n private readonly logger: MiniMothLogger\n ) {}\n\n async check(jti: string, accessToken: string): Promise<boolean> {\n if (this.mode === 'instant') return true\n\n if (this.mode !== 'strict') this.maybeCleanup()\n\n const ttl = MODE_TTL_MS[this.mode] ?? 0\n if (ttl > 0) {\n const entry = this.cache.get(jti)\n if (entry && Date.now() - entry.checkedAt < ttl) {\n this.logger.debug('revocation.cache.hit', { jti: jti.slice(0, 8) })\n return true\n }\n }\n\n try {\n const result = await apiPost<{ valid: boolean }>(\n '/v1/session/validate',\n this.apiKey,\n { access_token: accessToken }\n )\n\n if (result.valid && ttl > 0) {\n this.cache.set(jti, { checkedAt: Date.now() })\n }\n\n return result.valid\n } catch (err) {\n if (this.mode === 'strict') throw err\n\n // recheck_*m: fail open\n this.logger.warn('revocation.check.failed_open', {\n jti: jti.slice(0, 8),\n error: err instanceof Error ? err.message : 'unknown',\n })\n return true\n }\n }\n\n private maybeCleanup(): void {\n const now = Date.now()\n if (now - this.lastCleanedAt < CLEANUP_WINDOW_MS) return\n this.lastCleanedAt = now // set before iteration to prevent concurrent double-clean\n for (const [jti, entry] of this.cache) {\n if (now - entry.checkedAt > EVICTION_TTL_MS) {\n this.cache.delete(jti)\n }\n }\n }\n}\n","import { MiniMothLogger } from '../logger/MiniMothLogger'\n\ntype Tokens = { accessToken: string; refreshToken: string }\n\nexport class RefreshDeduplicator {\n private readonly inflight = new Map<string, Promise<unknown>>()\n\n constructor(private readonly logger: MiniMothLogger) {}\n\n getOrRun<T>(sessionId: string, fn: () => Promise<T>): Promise<T> {\n const existing = this.inflight.get(sessionId)\n if (existing) {\n this.logger.debug('refresh dedup hit', { key: sessionId })\n return existing as Promise<T>\n }\n\n const promise = fn().finally(() => {\n this.inflight.delete(sessionId)\n })\n\n this.inflight.set(sessionId, promise)\n return promise\n }\n}\n","import type { MiniMothSessionStore } from './MiniMothSessionStore'\nimport type { MiniMothLogger } from '../logger/MiniMothLogger'\n\ntype Entry = { accessToken: string; refreshToken: string }\n\nexport class DefaultSessionStore implements MiniMothSessionStore {\n private readonly map = new Map<string, Entry>()\n private warnedProduction = false\n\n constructor(private readonly logger: MiniMothLogger) {}\n\n private maybeWarn(): void {\n if (process.env['NODE_ENV'] === 'production' && !this.warnedProduction) {\n this.warnedProduction = true\n this.logger.warn('using default in-memory session store — not suitable for multi-replica deployments')\n }\n }\n\n async getRefreshToken(sessionId: string): Promise<string | null> {\n this.maybeWarn()\n return this.map.get(sessionId)?.refreshToken ?? null\n }\n\n async setTokens(sessionId: string, tokens: { accessToken: string; refreshToken: string }): Promise<void> {\n this.maybeWarn()\n this.map.set(sessionId, tokens)\n }\n\n async deleteSession(sessionId: string): Promise<void> {\n this.map.delete(sessionId)\n }\n}\n","import type { MiniMothLogger } from './MiniMothLogger'\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0, info: 1, warn: 2, error: 3, silent: 4,\n}\n\nexport class ConsoleLogger implements MiniMothLogger {\n private level: LogLevel\n\n constructor(level: LogLevel = 'info') {\n this.level = level\n }\n\n setLevel(level: LogLevel): void {\n this.level = level\n }\n\n private shouldLog(level: LogLevel): boolean {\n return LEVEL_ORDER[level] >= LEVEL_ORDER[this.level]\n }\n\n private format(msg: string, meta?: Record<string, unknown>): string {\n if (!meta || Object.keys(meta).length === 0) return `[minimoth] ${msg}`\n return `[minimoth] ${msg} ${JSON.stringify(meta)}`\n }\n\n debug(msg: string, meta?: Record<string, unknown>): void {\n if (this.shouldLog('debug')) console.debug(this.format(msg, meta))\n }\n\n info(msg: string, meta?: Record<string, unknown>): void {\n if (this.shouldLog('info')) console.info(this.format(msg, meta))\n }\n\n warn(msg: string, meta?: Record<string, unknown>): void {\n if (this.shouldLog('warn')) console.warn(this.format(msg, meta))\n }\n\n error(msg: string, meta?: Record<string, unknown>): void {\n if (this.shouldLog('error')) console.error(this.format(msg, meta))\n }\n}\n","import { OtpClient } from './otp/OtpClient'\nimport { SessionClient } from './session/SessionClient'\nimport { JwksCache } from './session/JwksCache'\nimport { RevocationCache } from './session/RevocationCache'\nimport { RefreshDeduplicator } from './session/RefreshDeduplicator'\nimport { DefaultSessionStore } from './store/DefaultSessionStore'\nimport { ConsoleLogger } from './logger/ConsoleLogger'\nimport type { MiniMothConfig } from './types'\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'\n\nexport class MiniMoth {\n readonly otp: OtpClient\n readonly session: SessionClient\n private readonly consoleLogger: ConsoleLogger\n\n constructor(config: MiniMothConfig) {\n this.consoleLogger = new ConsoleLogger(config.logLevel ?? 'info')\n const logger = config.logger ?? this.consoleLogger\n\n const store = config.session?.store ?? new DefaultSessionStore(logger)\n const validateMode = config.session?.validateMode ?? 'instant'\n\n const jwksCache = new JwksCache(logger)\n const revocationCache = new RevocationCache(validateMode, config.apiKey, logger)\n const deduplicator = new RefreshDeduplicator(logger)\n\n this.otp = new OtpClient(config.apiKey, store, logger)\n this.session = new SessionClient(\n config.apiKey,\n jwksCache,\n revocationCache,\n deduplicator,\n store,\n logger\n )\n }\n\n // Only affects the built-in ConsoleLogger. Has no effect if a custom logger was provided.\n setLogLevel(level: LogLevel): void {\n this.consoleLogger.setLevel(level)\n }\n}\n"],"mappings":"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","names":["require","jwt","jwt","err"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@minimoth/sdk-node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official MiniMoth Node.js SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": ["dist"],
|
|
17
|
+
"engines": { "node": ">=18" },
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"test": "vitest run --reporter=verbose",
|
|
21
|
+
"test:integration": "vitest run --config vitest.integration.config.ts --reporter=verbose",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"type-check": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"jsonwebtoken": "^9.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/jsonwebtoken": "^9.0.0",
|
|
30
|
+
"@types/node": "^22.0.0",
|
|
31
|
+
"tsup": "^8.0.0",
|
|
32
|
+
"typescript": "^5.0.0",
|
|
33
|
+
"vitest": "^2.0.0",
|
|
34
|
+
"dotenv": "^16.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|