@inneropen/marvin-sdk 2.0.0-next.18 → 2.0.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -136
- package/dist/{HttpClient-CdF3IHM1.d.cts → HttpClient-CLtgQGXH.d.cts} +2 -49
- package/dist/{HttpClient-CdF3IHM1.d.ts → HttpClient-CLtgQGXH.d.ts} +2 -49
- package/dist/chunk-25F3IF6S.js +396 -0
- package/dist/index.cjs +25 -536
- package/dist/index.d.cts +544 -20
- package/dist/index.d.ts +544 -20
- package/dist/index.js +791 -27
- package/dist/platform.cjs +42 -681
- package/dist/platform.d.cts +76 -811
- package/dist/platform.d.ts +76 -811
- package/dist/platform.js +32 -449
- package/dist/types/index.d.cts +1 -9
- package/dist/types/index.d.ts +1 -9
- package/package.json +7 -18
- package/dist/chunk-GJNOYXUQ.js +0 -980
- package/dist/chunk-WJC4X7TB.js +0 -738
- package/dist/marvin-Bx5k6tsF.d.cts +0 -654
- package/dist/marvin-g7dayc83.d.ts +0 -654
- package/dist/publish.cjs +0 -1487
- package/dist/publish.d.cts +0 -49
- package/dist/publish.d.ts +0 -49
- package/dist/publish.js +0 -75
package/dist/chunk-WJC4X7TB.js
DELETED
|
@@ -1,738 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __esm = (fn, res) => function __init() {
|
|
6
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
-
};
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
-
|
|
22
|
-
// src/core/errors/MarvinError.ts
|
|
23
|
-
var MarvinError, MarvinApiError, MarvinAuthError, MarvinConfigError, MarvinValidationError, MarvinNotFoundError, MarvinNetworkError, MarvinServerError;
|
|
24
|
-
var init_MarvinError = __esm({
|
|
25
|
-
"src/core/errors/MarvinError.ts"() {
|
|
26
|
-
"use strict";
|
|
27
|
-
MarvinError = class _MarvinError extends Error {
|
|
28
|
-
constructor(message, code, statusCode) {
|
|
29
|
-
super(message);
|
|
30
|
-
this.code = code;
|
|
31
|
-
this.statusCode = statusCode;
|
|
32
|
-
this.name = "MarvinError";
|
|
33
|
-
Object.setPrototypeOf(this, _MarvinError.prototype);
|
|
34
|
-
}
|
|
35
|
-
code;
|
|
36
|
-
statusCode;
|
|
37
|
-
};
|
|
38
|
-
MarvinApiError = class _MarvinApiError extends MarvinError {
|
|
39
|
-
constructor(message, statusCode, endpoint, responseBody) {
|
|
40
|
-
super(message, "API_ERROR", statusCode);
|
|
41
|
-
this.statusCode = statusCode;
|
|
42
|
-
this.endpoint = endpoint;
|
|
43
|
-
this.responseBody = responseBody;
|
|
44
|
-
this.name = "MarvinApiError";
|
|
45
|
-
Object.setPrototypeOf(this, _MarvinApiError.prototype);
|
|
46
|
-
}
|
|
47
|
-
statusCode;
|
|
48
|
-
endpoint;
|
|
49
|
-
responseBody;
|
|
50
|
-
static fromResponse(status, statusText, endpoint, body) {
|
|
51
|
-
const message = `Marvin API error: ${status} ${statusText} at ${endpoint}${body ? `
|
|
52
|
-
${body}` : ""}`;
|
|
53
|
-
return new _MarvinApiError(message, status, endpoint, body);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
MarvinAuthError = class _MarvinAuthError extends MarvinError {
|
|
57
|
-
constructor(message, statusCode) {
|
|
58
|
-
super(message, "AUTH_ERROR", statusCode);
|
|
59
|
-
this.name = "MarvinAuthError";
|
|
60
|
-
Object.setPrototypeOf(this, _MarvinAuthError.prototype);
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
MarvinConfigError = class _MarvinConfigError extends MarvinError {
|
|
64
|
-
constructor(message) {
|
|
65
|
-
super(message, "CONFIG_ERROR");
|
|
66
|
-
this.name = "MarvinConfigError";
|
|
67
|
-
Object.setPrototypeOf(this, _MarvinConfigError.prototype);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
MarvinValidationError = class _MarvinValidationError extends MarvinError {
|
|
71
|
-
constructor(message, field) {
|
|
72
|
-
super(message, "VALIDATION_ERROR");
|
|
73
|
-
this.field = field;
|
|
74
|
-
this.name = "MarvinValidationError";
|
|
75
|
-
Object.setPrototypeOf(this, _MarvinValidationError.prototype);
|
|
76
|
-
}
|
|
77
|
-
field;
|
|
78
|
-
};
|
|
79
|
-
MarvinNotFoundError = class _MarvinNotFoundError extends MarvinError {
|
|
80
|
-
constructor(message, endpoint) {
|
|
81
|
-
super(message, "NOT_FOUND", 404);
|
|
82
|
-
this.endpoint = endpoint;
|
|
83
|
-
this.name = "MarvinNotFoundError";
|
|
84
|
-
Object.setPrototypeOf(this, _MarvinNotFoundError.prototype);
|
|
85
|
-
}
|
|
86
|
-
endpoint;
|
|
87
|
-
};
|
|
88
|
-
MarvinNetworkError = class _MarvinNetworkError extends MarvinError {
|
|
89
|
-
constructor(message, originalError) {
|
|
90
|
-
super(message, "NETWORK_ERROR");
|
|
91
|
-
this.originalError = originalError;
|
|
92
|
-
this.name = "MarvinNetworkError";
|
|
93
|
-
Object.setPrototypeOf(this, _MarvinNetworkError.prototype);
|
|
94
|
-
}
|
|
95
|
-
originalError;
|
|
96
|
-
};
|
|
97
|
-
MarvinServerError = class _MarvinServerError extends MarvinError {
|
|
98
|
-
constructor(message, statusCode, endpoint) {
|
|
99
|
-
super(message, "SERVER_ERROR", statusCode);
|
|
100
|
-
this.endpoint = endpoint;
|
|
101
|
-
this.name = "MarvinServerError";
|
|
102
|
-
Object.setPrototypeOf(this, _MarvinServerError.prototype);
|
|
103
|
-
}
|
|
104
|
-
endpoint;
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// src/core/errors/index.ts
|
|
110
|
-
var init_errors = __esm({
|
|
111
|
-
"src/core/errors/index.ts"() {
|
|
112
|
-
"use strict";
|
|
113
|
-
init_MarvinError();
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// src/core/http/HttpClient.ts
|
|
118
|
-
var HttpClient;
|
|
119
|
-
var init_HttpClient = __esm({
|
|
120
|
-
"src/core/http/HttpClient.ts"() {
|
|
121
|
-
"use strict";
|
|
122
|
-
init_errors();
|
|
123
|
-
HttpClient = class {
|
|
124
|
-
baseUrl;
|
|
125
|
-
auth;
|
|
126
|
-
defaultHeaders;
|
|
127
|
-
credentials;
|
|
128
|
-
timeout;
|
|
129
|
-
debug;
|
|
130
|
-
retryConfig;
|
|
131
|
-
logger;
|
|
132
|
-
constructor(config) {
|
|
133
|
-
this.baseUrl = config.baseUrl;
|
|
134
|
-
this.auth = config.auth;
|
|
135
|
-
this.defaultHeaders = config.defaultHeaders || {};
|
|
136
|
-
this.credentials = config.credentials || "same-origin";
|
|
137
|
-
const MAX_TIMEOUT = 12e4;
|
|
138
|
-
this.timeout = Math.min(config.timeout || 3e4, MAX_TIMEOUT);
|
|
139
|
-
this.debug = config.debug ?? false;
|
|
140
|
-
this.logger = config.logger ?? console;
|
|
141
|
-
this.retryConfig = {
|
|
142
|
-
maxRetries: config.retry?.maxRetries ?? 3,
|
|
143
|
-
initialDelay: config.retry?.initialDelay ?? 1e3,
|
|
144
|
-
maxDelay: config.retry?.maxDelay ?? 1e4,
|
|
145
|
-
retryableStatuses: config.retry?.retryableStatuses ?? [408, 429, 500, 502, 503, 504]
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Validate path parameter to prevent path traversal attacks
|
|
150
|
-
*/
|
|
151
|
-
validatePathParam(param, paramName = "parameter") {
|
|
152
|
-
if (typeof param === "number") {
|
|
153
|
-
return String(param);
|
|
154
|
-
}
|
|
155
|
-
if (!param || typeof param !== "string") {
|
|
156
|
-
throw new MarvinValidationError(`Invalid ${paramName}: must be a non-empty string or number`);
|
|
157
|
-
}
|
|
158
|
-
if (param.includes("..") || param.includes("/") || param.includes("\\")) {
|
|
159
|
-
throw new MarvinValidationError(
|
|
160
|
-
`Invalid ${paramName}: cannot contain path traversal characters (.. / \\)`
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
if (param.includes("\0")) {
|
|
164
|
-
throw new MarvinValidationError(`Invalid ${paramName}: cannot contain null bytes`);
|
|
165
|
-
}
|
|
166
|
-
if (param.length > 255) {
|
|
167
|
-
throw new MarvinValidationError(`Invalid ${paramName}: exceeds maximum length of 255 characters`);
|
|
168
|
-
}
|
|
169
|
-
return param;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Build full URL from endpoint
|
|
173
|
-
*/
|
|
174
|
-
buildUrl(endpoint) {
|
|
175
|
-
const base = this.baseUrl.replace(/\/$/, "");
|
|
176
|
-
const path = endpoint.replace(/^\//, "");
|
|
177
|
-
return `${base}/${path}`;
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Build headers for request
|
|
181
|
-
*/
|
|
182
|
-
buildHeaders(additionalHeaders, isFormData = false) {
|
|
183
|
-
const headerInit = {};
|
|
184
|
-
if (!isFormData) {
|
|
185
|
-
headerInit["Content-Type"] = "application/json";
|
|
186
|
-
}
|
|
187
|
-
const headers = new Headers({
|
|
188
|
-
...headerInit,
|
|
189
|
-
...this.defaultHeaders,
|
|
190
|
-
...additionalHeaders
|
|
191
|
-
});
|
|
192
|
-
this.auth.injectAuth(headers);
|
|
193
|
-
return headers;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Build query string from params
|
|
197
|
-
*/
|
|
198
|
-
buildQueryString(params) {
|
|
199
|
-
const filtered = Object.entries(params).filter(([_, value]) => value !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
|
200
|
-
return filtered.length > 0 ? `?${filtered.join("&")}` : "";
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Sanitize data for logging by redacting sensitive fields
|
|
204
|
-
*/
|
|
205
|
-
sanitizeForLogging(data) {
|
|
206
|
-
if (!data || typeof data !== "object") {
|
|
207
|
-
return data;
|
|
208
|
-
}
|
|
209
|
-
const sensitiveFields = [
|
|
210
|
-
"token",
|
|
211
|
-
"access_token",
|
|
212
|
-
"refresh_token",
|
|
213
|
-
"password",
|
|
214
|
-
"secret",
|
|
215
|
-
"apiKey",
|
|
216
|
-
"api_key",
|
|
217
|
-
"bearer",
|
|
218
|
-
"authorization",
|
|
219
|
-
"csrf_token",
|
|
220
|
-
"session",
|
|
221
|
-
"cookie"
|
|
222
|
-
];
|
|
223
|
-
const sanitize = (obj) => {
|
|
224
|
-
const sanitized = {};
|
|
225
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
226
|
-
const keyLower = key.toLowerCase();
|
|
227
|
-
const isSensitive = sensitiveFields.some((field) => keyLower.includes(field));
|
|
228
|
-
if (isSensitive) {
|
|
229
|
-
sanitized[key] = "[REDACTED]";
|
|
230
|
-
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
231
|
-
sanitized[key] = sanitize(value);
|
|
232
|
-
} else if (Array.isArray(value)) {
|
|
233
|
-
sanitized[key] = value.map(
|
|
234
|
-
(item) => item && typeof item === "object" ? sanitize(item) : item
|
|
235
|
-
);
|
|
236
|
-
} else {
|
|
237
|
-
sanitized[key] = value;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return sanitized;
|
|
241
|
-
};
|
|
242
|
-
if (Array.isArray(data)) {
|
|
243
|
-
return data.map(
|
|
244
|
-
(item) => item && typeof item === "object" ? sanitize(item) : item
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
return sanitize(data);
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Calculate delay for retry with exponential backoff
|
|
251
|
-
*/
|
|
252
|
-
calculateRetryDelay(attempt) {
|
|
253
|
-
const delay = this.retryConfig.initialDelay * Math.pow(2, attempt);
|
|
254
|
-
return Math.min(delay, this.retryConfig.maxDelay);
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Sleep for specified milliseconds
|
|
258
|
-
*/
|
|
259
|
-
sleep(ms) {
|
|
260
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Perform HTTP request with automatic retry on transient failures
|
|
264
|
-
*/
|
|
265
|
-
async request(method, endpoint, options, retryAttempt = 0) {
|
|
266
|
-
const url = this.buildUrl(endpoint) + (options?.params ? this.buildQueryString(options.params) : "");
|
|
267
|
-
const isFormData = options?.body instanceof FormData;
|
|
268
|
-
const headers = this.buildHeaders(options?.headers, isFormData);
|
|
269
|
-
const controller = new AbortController();
|
|
270
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
271
|
-
const startTime = Date.now();
|
|
272
|
-
if (this.debug) {
|
|
273
|
-
this.logger.log(`[Marvin] ${method} ${url}`);
|
|
274
|
-
}
|
|
275
|
-
try {
|
|
276
|
-
const response = await fetch(url, {
|
|
277
|
-
method,
|
|
278
|
-
headers,
|
|
279
|
-
body: isFormData ? options.body : options?.body ? JSON.stringify(options.body) : void 0,
|
|
280
|
-
credentials: this.credentials,
|
|
281
|
-
signal: controller.signal
|
|
282
|
-
});
|
|
283
|
-
clearTimeout(timeoutId);
|
|
284
|
-
const duration = Date.now() - startTime;
|
|
285
|
-
if (this.debug) {
|
|
286
|
-
this.logger.log(
|
|
287
|
-
`[Marvin] ${response.status} ${response.statusText} (${duration}ms)`
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
if (response.status === 401 && this.auth.handleUnauthorized) {
|
|
291
|
-
await this.auth.handleUnauthorized();
|
|
292
|
-
}
|
|
293
|
-
if (!response.ok) {
|
|
294
|
-
const errorText = await response.text().catch(() => "");
|
|
295
|
-
if (this.debug) {
|
|
296
|
-
this.logger.error(
|
|
297
|
-
`[Marvin] Error response: ${response.status} ${response.statusText}`,
|
|
298
|
-
errorText ? `Body: ${errorText}` : ""
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
const isRetryable = this.retryConfig.retryableStatuses.includes(response.status);
|
|
302
|
-
const canRetry = retryAttempt < this.retryConfig.maxRetries;
|
|
303
|
-
if (isRetryable && canRetry) {
|
|
304
|
-
const delay = this.calculateRetryDelay(retryAttempt);
|
|
305
|
-
if (this.debug) {
|
|
306
|
-
this.logger.warn(
|
|
307
|
-
`[Marvin] Retry ${retryAttempt + 1}/${this.retryConfig.maxRetries} after ${delay}ms (status: ${response.status})`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
await this.sleep(delay);
|
|
311
|
-
return this.request(method, endpoint, options, retryAttempt + 1);
|
|
312
|
-
}
|
|
313
|
-
if (response.status === 404) {
|
|
314
|
-
throw new MarvinNotFoundError(
|
|
315
|
-
`Resource not found: ${endpoint}`,
|
|
316
|
-
endpoint
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
if (response.status === 401 || response.status === 403) {
|
|
320
|
-
throw new MarvinAuthError(
|
|
321
|
-
`Authentication failed: ${response.statusText}`,
|
|
322
|
-
response.status
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
if (response.status >= 500) {
|
|
326
|
-
throw new MarvinServerError(
|
|
327
|
-
`Server error: ${response.status} ${response.statusText}`,
|
|
328
|
-
response.status,
|
|
329
|
-
endpoint
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
throw MarvinApiError.fromResponse(
|
|
333
|
-
response.status,
|
|
334
|
-
response.statusText,
|
|
335
|
-
endpoint,
|
|
336
|
-
errorText
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
const contentType = response.headers.get("content-type");
|
|
340
|
-
if (!contentType || !contentType.includes("application/json")) {
|
|
341
|
-
return void 0;
|
|
342
|
-
}
|
|
343
|
-
const data = await response.json();
|
|
344
|
-
if (this.debug) {
|
|
345
|
-
const sanitized = this.sanitizeForLogging(data);
|
|
346
|
-
this.logger.log("[Marvin] Response:", JSON.stringify(sanitized, null, 2));
|
|
347
|
-
}
|
|
348
|
-
return data;
|
|
349
|
-
} catch (error) {
|
|
350
|
-
clearTimeout(timeoutId);
|
|
351
|
-
if (error instanceof MarvinApiError || error instanceof MarvinNotFoundError || error instanceof MarvinAuthError || error instanceof MarvinServerError) {
|
|
352
|
-
throw error;
|
|
353
|
-
}
|
|
354
|
-
if (error instanceof Error) {
|
|
355
|
-
const canRetry = retryAttempt < this.retryConfig.maxRetries;
|
|
356
|
-
if (canRetry && error.name !== "AbortError") {
|
|
357
|
-
const delay = this.calculateRetryDelay(retryAttempt);
|
|
358
|
-
if (this.debug) {
|
|
359
|
-
this.logger.warn(
|
|
360
|
-
`[Marvin] Retry ${retryAttempt + 1}/${this.retryConfig.maxRetries} after ${delay}ms (network error)`
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
await this.sleep(delay);
|
|
364
|
-
return this.request(method, endpoint, options, retryAttempt + 1);
|
|
365
|
-
}
|
|
366
|
-
if (error.name === "AbortError") {
|
|
367
|
-
throw new MarvinNetworkError(
|
|
368
|
-
`Request timeout after ${this.timeout}ms`,
|
|
369
|
-
error
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
throw new MarvinNetworkError(
|
|
373
|
-
`Network error: ${error.message}`,
|
|
374
|
-
error
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
throw error;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* GET request
|
|
382
|
-
*/
|
|
383
|
-
async get(endpoint, params) {
|
|
384
|
-
return this.request("GET", endpoint, { params });
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* POST request
|
|
388
|
-
*/
|
|
389
|
-
async post(endpoint, body) {
|
|
390
|
-
return this.request("POST", endpoint, { body });
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* PUT request
|
|
394
|
-
*/
|
|
395
|
-
async put(endpoint, body) {
|
|
396
|
-
return this.request("PUT", endpoint, { body });
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* PATCH request
|
|
400
|
-
*/
|
|
401
|
-
async patch(endpoint, body) {
|
|
402
|
-
return this.request("PATCH", endpoint, { body });
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* DELETE request
|
|
406
|
-
*/
|
|
407
|
-
async delete(endpoint) {
|
|
408
|
-
return this.request("DELETE", endpoint);
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
// src/core/http/index.ts
|
|
415
|
-
var init_http = __esm({
|
|
416
|
-
"src/core/http/index.ts"() {
|
|
417
|
-
"use strict";
|
|
418
|
-
init_HttpClient();
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
// src/core/auth/AuthStrategy.ts
|
|
423
|
-
var BearerTokenAuth, SessionAuth, NoAuth;
|
|
424
|
-
var init_AuthStrategy = __esm({
|
|
425
|
-
"src/core/auth/AuthStrategy.ts"() {
|
|
426
|
-
"use strict";
|
|
427
|
-
BearerTokenAuth = class {
|
|
428
|
-
constructor(token) {
|
|
429
|
-
this.token = token;
|
|
430
|
-
}
|
|
431
|
-
token;
|
|
432
|
-
injectAuth(headers) {
|
|
433
|
-
headers.set("Authorization", `Bearer ${this.token}`);
|
|
434
|
-
}
|
|
435
|
-
getDescription() {
|
|
436
|
-
return "Bearer token authentication";
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
SessionAuth = class {
|
|
440
|
-
constructor(sessionToken, onUnauthorized) {
|
|
441
|
-
this.sessionToken = sessionToken;
|
|
442
|
-
this.onUnauthorized = onUnauthorized;
|
|
443
|
-
}
|
|
444
|
-
sessionToken;
|
|
445
|
-
onUnauthorized;
|
|
446
|
-
csrfToken;
|
|
447
|
-
/**
|
|
448
|
-
* Set CSRF token for cross-site request forgery protection
|
|
449
|
-
* The token should be obtained from the server after login
|
|
450
|
-
*/
|
|
451
|
-
setCsrfToken(token) {
|
|
452
|
-
this.csrfToken = token;
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Get current CSRF token
|
|
456
|
-
*/
|
|
457
|
-
getCsrfToken() {
|
|
458
|
-
return this.csrfToken;
|
|
459
|
-
}
|
|
460
|
-
injectAuth(headers) {
|
|
461
|
-
if (this.sessionToken) {
|
|
462
|
-
headers.set("Authorization", `Bearer ${this.sessionToken}`);
|
|
463
|
-
}
|
|
464
|
-
if (this.csrfToken) {
|
|
465
|
-
headers.set("X-CSRF-Token", this.csrfToken);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
async handleUnauthorized() {
|
|
469
|
-
if (this.onUnauthorized) {
|
|
470
|
-
await this.onUnauthorized();
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
getDescription() {
|
|
474
|
-
const auth = this.sessionToken ? "Session authentication (explicit token)" : "Session authentication (cookies)";
|
|
475
|
-
const csrf = this.csrfToken ? " with CSRF protection" : "";
|
|
476
|
-
return auth + csrf;
|
|
477
|
-
}
|
|
478
|
-
};
|
|
479
|
-
NoAuth = class {
|
|
480
|
-
injectAuth(_headers) {
|
|
481
|
-
}
|
|
482
|
-
getDescription() {
|
|
483
|
-
return "No authentication";
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
// src/core/auth/index.ts
|
|
490
|
-
var init_auth = __esm({
|
|
491
|
-
"src/core/auth/index.ts"() {
|
|
492
|
-
"use strict";
|
|
493
|
-
init_AuthStrategy();
|
|
494
|
-
}
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
// src/core/pagination/PaginatedResponse.ts
|
|
498
|
-
function createPaginatedResponse(data, meta) {
|
|
499
|
-
return { data, meta };
|
|
500
|
-
}
|
|
501
|
-
function extractData(response) {
|
|
502
|
-
if (Array.isArray(response)) {
|
|
503
|
-
return response;
|
|
504
|
-
}
|
|
505
|
-
return response.data;
|
|
506
|
-
}
|
|
507
|
-
function isPaginatedResponse(response) {
|
|
508
|
-
return !Array.isArray(response) && "data" in response;
|
|
509
|
-
}
|
|
510
|
-
function pageToOffset(page, limit) {
|
|
511
|
-
return (page - 1) * limit;
|
|
512
|
-
}
|
|
513
|
-
function offsetToPage(offset, limit) {
|
|
514
|
-
return Math.floor(offset / limit) + 1;
|
|
515
|
-
}
|
|
516
|
-
var init_PaginatedResponse = __esm({
|
|
517
|
-
"src/core/pagination/PaginatedResponse.ts"() {
|
|
518
|
-
"use strict";
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
// src/core/pagination/index.ts
|
|
523
|
-
var init_pagination = __esm({
|
|
524
|
-
"src/core/pagination/index.ts"() {
|
|
525
|
-
"use strict";
|
|
526
|
-
init_PaginatedResponse();
|
|
527
|
-
}
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
// src/core/cache.ts
|
|
531
|
-
var MarvinCache;
|
|
532
|
-
var init_cache = __esm({
|
|
533
|
-
"src/core/cache.ts"() {
|
|
534
|
-
"use strict";
|
|
535
|
-
MarvinCache = class {
|
|
536
|
-
constructor(duration = 5 * 60 * 1e3, maxEntries = 1e3) {
|
|
537
|
-
this.duration = duration;
|
|
538
|
-
this.maxEntries = maxEntries;
|
|
539
|
-
}
|
|
540
|
-
duration;
|
|
541
|
-
maxEntries;
|
|
542
|
-
cache = /* @__PURE__ */ new Map();
|
|
543
|
-
/**
|
|
544
|
-
* Get cached data by key
|
|
545
|
-
* Returns undefined if not found or expired
|
|
546
|
-
*/
|
|
547
|
-
get(key) {
|
|
548
|
-
const entry = this.cache.get(key);
|
|
549
|
-
if (!entry) return void 0;
|
|
550
|
-
const age = Date.now() - entry.timestamp;
|
|
551
|
-
if (age > this.duration) {
|
|
552
|
-
this.cache.delete(key);
|
|
553
|
-
return void 0;
|
|
554
|
-
}
|
|
555
|
-
return entry.data;
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* Set cached data with optional tags for invalidation
|
|
559
|
-
*/
|
|
560
|
-
set(key, data, tags) {
|
|
561
|
-
if (this.cache.size >= this.maxEntries) {
|
|
562
|
-
const oldestKey = this.cache.keys().next().value;
|
|
563
|
-
if (oldestKey) {
|
|
564
|
-
this.cache.delete(oldestKey);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
this.cache.set(key, {
|
|
568
|
-
data,
|
|
569
|
-
timestamp: Date.now(),
|
|
570
|
-
tags
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Check if key exists and is not expired
|
|
575
|
-
*/
|
|
576
|
-
has(key) {
|
|
577
|
-
return this.get(key) !== void 0;
|
|
578
|
-
}
|
|
579
|
-
/**
|
|
580
|
-
* Clear all cached entries
|
|
581
|
-
*/
|
|
582
|
-
clear() {
|
|
583
|
-
this.cache.clear();
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Delete a specific cache entry
|
|
587
|
-
*/
|
|
588
|
-
delete(key) {
|
|
589
|
-
this.cache.delete(key);
|
|
590
|
-
}
|
|
591
|
-
/**
|
|
592
|
-
* Invalidate all cache entries with a specific tag
|
|
593
|
-
* Useful for invalidating related data after mutations
|
|
594
|
-
*
|
|
595
|
-
* @example
|
|
596
|
-
* ```typescript
|
|
597
|
-
* // Cache entries with tags
|
|
598
|
-
* cache.set('entry:1', data, ['entries', 'entry:1']);
|
|
599
|
-
* cache.set('entries:list', data, ['entries']);
|
|
600
|
-
*
|
|
601
|
-
* // Invalidate all entries
|
|
602
|
-
* cache.invalidateByTag('entries');
|
|
603
|
-
* ```
|
|
604
|
-
*/
|
|
605
|
-
invalidateByTag(tag) {
|
|
606
|
-
let count = 0;
|
|
607
|
-
for (const [key, entry] of this.cache.entries()) {
|
|
608
|
-
if (entry.tags?.includes(tag)) {
|
|
609
|
-
this.cache.delete(key);
|
|
610
|
-
count++;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
return count;
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Get cache statistics
|
|
617
|
-
*/
|
|
618
|
-
getStats() {
|
|
619
|
-
let oldest = null;
|
|
620
|
-
let newest = null;
|
|
621
|
-
for (const entry of this.cache.values()) {
|
|
622
|
-
if (oldest === null || entry.timestamp < oldest) {
|
|
623
|
-
oldest = entry.timestamp;
|
|
624
|
-
}
|
|
625
|
-
if (newest === null || entry.timestamp > newest) {
|
|
626
|
-
newest = entry.timestamp;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
return {
|
|
630
|
-
size: this.cache.size,
|
|
631
|
-
maxEntries: this.maxEntries,
|
|
632
|
-
oldestEntry: oldest,
|
|
633
|
-
newestEntry: newest
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
/**
|
|
637
|
-
* Remove expired entries (garbage collection)
|
|
638
|
-
* Returns number of entries removed
|
|
639
|
-
*/
|
|
640
|
-
prune() {
|
|
641
|
-
let count = 0;
|
|
642
|
-
const now = Date.now();
|
|
643
|
-
for (const [key, entry] of this.cache.entries()) {
|
|
644
|
-
const age = now - entry.timestamp;
|
|
645
|
-
if (age > this.duration) {
|
|
646
|
-
this.cache.delete(key);
|
|
647
|
-
count++;
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
return count;
|
|
651
|
-
}
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
// src/core/validation.ts
|
|
657
|
-
function validateEmail(email, fieldName = "Email") {
|
|
658
|
-
if (!email || typeof email !== "string") {
|
|
659
|
-
throw new MarvinValidationError(`${fieldName} is required and must be a string`);
|
|
660
|
-
}
|
|
661
|
-
const trimmed = email.trim();
|
|
662
|
-
if (trimmed.length === 0) {
|
|
663
|
-
throw new MarvinValidationError(`${fieldName} cannot be empty`);
|
|
664
|
-
}
|
|
665
|
-
if (trimmed.length < 5) {
|
|
666
|
-
throw new MarvinValidationError(`${fieldName} is too short`);
|
|
667
|
-
}
|
|
668
|
-
if (trimmed.length > 254) {
|
|
669
|
-
throw new MarvinValidationError(`${fieldName} exceeds maximum length of 254 characters`);
|
|
670
|
-
}
|
|
671
|
-
if (!EMAIL_REGEX.test(trimmed)) {
|
|
672
|
-
throw new MarvinValidationError(
|
|
673
|
-
`${fieldName} format is invalid. Expected format: user@example.com`
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
const [localPart, domain] = trimmed.split("@");
|
|
677
|
-
if (localPart.length > 64) {
|
|
678
|
-
throw new MarvinValidationError(`${fieldName} local part exceeds 64 characters`);
|
|
679
|
-
}
|
|
680
|
-
if (domain.length > 253) {
|
|
681
|
-
throw new MarvinValidationError(`${fieldName} domain exceeds 253 characters`);
|
|
682
|
-
}
|
|
683
|
-
if (trimmed.includes("..")) {
|
|
684
|
-
throw new MarvinValidationError(`${fieldName} cannot contain consecutive dots`);
|
|
685
|
-
}
|
|
686
|
-
if (localPart.startsWith(".") || localPart.endsWith(".")) {
|
|
687
|
-
throw new MarvinValidationError(`${fieldName} local part cannot start or end with a dot`);
|
|
688
|
-
}
|
|
689
|
-
return trimmed;
|
|
690
|
-
}
|
|
691
|
-
var EMAIL_REGEX;
|
|
692
|
-
var init_validation = __esm({
|
|
693
|
-
"src/core/validation.ts"() {
|
|
694
|
-
"use strict";
|
|
695
|
-
init_errors();
|
|
696
|
-
EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
// src/core/index.ts
|
|
701
|
-
var init_core = __esm({
|
|
702
|
-
"src/core/index.ts"() {
|
|
703
|
-
"use strict";
|
|
704
|
-
init_http();
|
|
705
|
-
init_auth();
|
|
706
|
-
init_errors();
|
|
707
|
-
init_pagination();
|
|
708
|
-
init_cache();
|
|
709
|
-
init_validation();
|
|
710
|
-
}
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
export {
|
|
714
|
-
__esm,
|
|
715
|
-
__export,
|
|
716
|
-
__toCommonJS,
|
|
717
|
-
MarvinError,
|
|
718
|
-
MarvinApiError,
|
|
719
|
-
MarvinAuthError,
|
|
720
|
-
MarvinConfigError,
|
|
721
|
-
MarvinValidationError,
|
|
722
|
-
MarvinNotFoundError,
|
|
723
|
-
MarvinNetworkError,
|
|
724
|
-
MarvinServerError,
|
|
725
|
-
init_errors,
|
|
726
|
-
HttpClient,
|
|
727
|
-
BearerTokenAuth,
|
|
728
|
-
SessionAuth,
|
|
729
|
-
NoAuth,
|
|
730
|
-
createPaginatedResponse,
|
|
731
|
-
extractData,
|
|
732
|
-
isPaginatedResponse,
|
|
733
|
-
pageToOffset,
|
|
734
|
-
offsetToPage,
|
|
735
|
-
MarvinCache,
|
|
736
|
-
validateEmail,
|
|
737
|
-
init_core
|
|
738
|
-
};
|