@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/platform.cjs
CHANGED
|
@@ -27,24 +27,20 @@ __export(platform_exports, {
|
|
|
27
27
|
AssetsModule: () => AssetsModule,
|
|
28
28
|
AuthModule: () => AuthModule,
|
|
29
29
|
CollectionsModule: () => CollectionsModule,
|
|
30
|
-
EmailTemplatesClient: () => EmailTemplatesClient,
|
|
31
30
|
EntriesModule: () => EntriesModule,
|
|
32
31
|
EntryTypesModule: () => EntryTypesModule,
|
|
33
32
|
EventLogModule: () => EventLogModule,
|
|
34
33
|
EventsModule: () => EventsModule,
|
|
35
|
-
FormsModule: () => FormsModule,
|
|
36
34
|
InvitesModule: () => InvitesModule,
|
|
37
35
|
NotificationsModule: () => NotificationsModule,
|
|
38
36
|
PlatformClient: () => PlatformClient,
|
|
39
37
|
ResourcesModule: () => ResourcesModule,
|
|
40
|
-
ScheduledTasksModule: () => ScheduledTasksModule,
|
|
41
38
|
ThemeModule: () => ThemeModule,
|
|
42
39
|
UserModule: () => UserModule,
|
|
43
40
|
WebhooksModule: () => WebhooksModule,
|
|
44
41
|
WorkspaceMembersModule: () => WorkspaceMembersModule,
|
|
45
42
|
WorkspacesModule: () => WorkspacesModule,
|
|
46
|
-
createPlatformClient: () => createPlatformClient
|
|
47
|
-
createPlatformConfigFromEnv: () => createPlatformConfigFromEnv
|
|
43
|
+
createPlatformClient: () => createPlatformClient
|
|
48
44
|
});
|
|
49
45
|
module.exports = __toCommonJS(platform_exports);
|
|
50
46
|
|
|
@@ -78,49 +74,6 @@ ${body}` : ""}`;
|
|
|
78
74
|
return new _MarvinApiError(message, status, endpoint, body);
|
|
79
75
|
}
|
|
80
76
|
};
|
|
81
|
-
var MarvinAuthError = class _MarvinAuthError extends MarvinError {
|
|
82
|
-
constructor(message, statusCode) {
|
|
83
|
-
super(message, "AUTH_ERROR", statusCode);
|
|
84
|
-
this.name = "MarvinAuthError";
|
|
85
|
-
Object.setPrototypeOf(this, _MarvinAuthError.prototype);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
var MarvinValidationError = class _MarvinValidationError extends MarvinError {
|
|
89
|
-
constructor(message, field) {
|
|
90
|
-
super(message, "VALIDATION_ERROR");
|
|
91
|
-
this.field = field;
|
|
92
|
-
this.name = "MarvinValidationError";
|
|
93
|
-
Object.setPrototypeOf(this, _MarvinValidationError.prototype);
|
|
94
|
-
}
|
|
95
|
-
field;
|
|
96
|
-
};
|
|
97
|
-
var MarvinNotFoundError = class _MarvinNotFoundError extends MarvinError {
|
|
98
|
-
constructor(message, endpoint) {
|
|
99
|
-
super(message, "NOT_FOUND", 404);
|
|
100
|
-
this.endpoint = endpoint;
|
|
101
|
-
this.name = "MarvinNotFoundError";
|
|
102
|
-
Object.setPrototypeOf(this, _MarvinNotFoundError.prototype);
|
|
103
|
-
}
|
|
104
|
-
endpoint;
|
|
105
|
-
};
|
|
106
|
-
var MarvinNetworkError = class _MarvinNetworkError extends MarvinError {
|
|
107
|
-
constructor(message, originalError) {
|
|
108
|
-
super(message, "NETWORK_ERROR");
|
|
109
|
-
this.originalError = originalError;
|
|
110
|
-
this.name = "MarvinNetworkError";
|
|
111
|
-
Object.setPrototypeOf(this, _MarvinNetworkError.prototype);
|
|
112
|
-
}
|
|
113
|
-
originalError;
|
|
114
|
-
};
|
|
115
|
-
var MarvinServerError = class _MarvinServerError extends MarvinError {
|
|
116
|
-
constructor(message, statusCode, endpoint) {
|
|
117
|
-
super(message, "SERVER_ERROR", statusCode);
|
|
118
|
-
this.endpoint = endpoint;
|
|
119
|
-
this.name = "MarvinServerError";
|
|
120
|
-
Object.setPrototypeOf(this, _MarvinServerError.prototype);
|
|
121
|
-
}
|
|
122
|
-
endpoint;
|
|
123
|
-
};
|
|
124
77
|
|
|
125
78
|
// src/core/http/HttpClient.ts
|
|
126
79
|
var HttpClient = class {
|
|
@@ -129,47 +82,12 @@ var HttpClient = class {
|
|
|
129
82
|
defaultHeaders;
|
|
130
83
|
credentials;
|
|
131
84
|
timeout;
|
|
132
|
-
debug;
|
|
133
|
-
retryConfig;
|
|
134
|
-
logger;
|
|
135
85
|
constructor(config) {
|
|
136
86
|
this.baseUrl = config.baseUrl;
|
|
137
87
|
this.auth = config.auth;
|
|
138
88
|
this.defaultHeaders = config.defaultHeaders || {};
|
|
139
89
|
this.credentials = config.credentials || "same-origin";
|
|
140
|
-
|
|
141
|
-
this.timeout = Math.min(config.timeout || 3e4, MAX_TIMEOUT);
|
|
142
|
-
this.debug = config.debug ?? false;
|
|
143
|
-
this.logger = config.logger ?? console;
|
|
144
|
-
this.retryConfig = {
|
|
145
|
-
maxRetries: config.retry?.maxRetries ?? 3,
|
|
146
|
-
initialDelay: config.retry?.initialDelay ?? 1e3,
|
|
147
|
-
maxDelay: config.retry?.maxDelay ?? 1e4,
|
|
148
|
-
retryableStatuses: config.retry?.retryableStatuses ?? [408, 429, 500, 502, 503, 504]
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Validate path parameter to prevent path traversal attacks
|
|
153
|
-
*/
|
|
154
|
-
validatePathParam(param, paramName = "parameter") {
|
|
155
|
-
if (typeof param === "number") {
|
|
156
|
-
return String(param);
|
|
157
|
-
}
|
|
158
|
-
if (!param || typeof param !== "string") {
|
|
159
|
-
throw new MarvinValidationError(`Invalid ${paramName}: must be a non-empty string or number`);
|
|
160
|
-
}
|
|
161
|
-
if (param.includes("..") || param.includes("/") || param.includes("\\")) {
|
|
162
|
-
throw new MarvinValidationError(
|
|
163
|
-
`Invalid ${paramName}: cannot contain path traversal characters (.. / \\)`
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
if (param.includes("\0")) {
|
|
167
|
-
throw new MarvinValidationError(`Invalid ${paramName}: cannot contain null bytes`);
|
|
168
|
-
}
|
|
169
|
-
if (param.length > 255) {
|
|
170
|
-
throw new MarvinValidationError(`Invalid ${paramName}: exceeds maximum length of 255 characters`);
|
|
171
|
-
}
|
|
172
|
-
return param;
|
|
90
|
+
this.timeout = config.timeout || 3e4;
|
|
173
91
|
}
|
|
174
92
|
/**
|
|
175
93
|
* Build full URL from endpoint
|
|
@@ -203,78 +121,14 @@ var HttpClient = class {
|
|
|
203
121
|
return filtered.length > 0 ? `?${filtered.join("&")}` : "";
|
|
204
122
|
}
|
|
205
123
|
/**
|
|
206
|
-
*
|
|
207
|
-
*/
|
|
208
|
-
sanitizeForLogging(data) {
|
|
209
|
-
if (!data || typeof data !== "object") {
|
|
210
|
-
return data;
|
|
211
|
-
}
|
|
212
|
-
const sensitiveFields = [
|
|
213
|
-
"token",
|
|
214
|
-
"access_token",
|
|
215
|
-
"refresh_token",
|
|
216
|
-
"password",
|
|
217
|
-
"secret",
|
|
218
|
-
"apiKey",
|
|
219
|
-
"api_key",
|
|
220
|
-
"bearer",
|
|
221
|
-
"authorization",
|
|
222
|
-
"csrf_token",
|
|
223
|
-
"session",
|
|
224
|
-
"cookie"
|
|
225
|
-
];
|
|
226
|
-
const sanitize = (obj) => {
|
|
227
|
-
const sanitized = {};
|
|
228
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
229
|
-
const keyLower = key.toLowerCase();
|
|
230
|
-
const isSensitive = sensitiveFields.some((field) => keyLower.includes(field));
|
|
231
|
-
if (isSensitive) {
|
|
232
|
-
sanitized[key] = "[REDACTED]";
|
|
233
|
-
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
234
|
-
sanitized[key] = sanitize(value);
|
|
235
|
-
} else if (Array.isArray(value)) {
|
|
236
|
-
sanitized[key] = value.map(
|
|
237
|
-
(item) => item && typeof item === "object" ? sanitize(item) : item
|
|
238
|
-
);
|
|
239
|
-
} else {
|
|
240
|
-
sanitized[key] = value;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return sanitized;
|
|
244
|
-
};
|
|
245
|
-
if (Array.isArray(data)) {
|
|
246
|
-
return data.map(
|
|
247
|
-
(item) => item && typeof item === "object" ? sanitize(item) : item
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
return sanitize(data);
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Calculate delay for retry with exponential backoff
|
|
254
|
-
*/
|
|
255
|
-
calculateRetryDelay(attempt) {
|
|
256
|
-
const delay = this.retryConfig.initialDelay * Math.pow(2, attempt);
|
|
257
|
-
return Math.min(delay, this.retryConfig.maxDelay);
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Sleep for specified milliseconds
|
|
124
|
+
* Perform HTTP request
|
|
261
125
|
*/
|
|
262
|
-
|
|
263
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Perform HTTP request with automatic retry on transient failures
|
|
267
|
-
*/
|
|
268
|
-
async request(method, endpoint, options, retryAttempt = 0) {
|
|
126
|
+
async request(method, endpoint, options) {
|
|
269
127
|
const url = this.buildUrl(endpoint) + (options?.params ? this.buildQueryString(options.params) : "");
|
|
270
128
|
const isFormData = options?.body instanceof FormData;
|
|
271
129
|
const headers = this.buildHeaders(options?.headers, isFormData);
|
|
272
130
|
const controller = new AbortController();
|
|
273
131
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
274
|
-
const startTime = Date.now();
|
|
275
|
-
if (this.debug) {
|
|
276
|
-
this.logger.log(`[Marvin] ${method} ${url}`);
|
|
277
|
-
}
|
|
278
132
|
try {
|
|
279
133
|
const response = await fetch(url, {
|
|
280
134
|
method,
|
|
@@ -284,54 +138,11 @@ var HttpClient = class {
|
|
|
284
138
|
signal: controller.signal
|
|
285
139
|
});
|
|
286
140
|
clearTimeout(timeoutId);
|
|
287
|
-
const duration = Date.now() - startTime;
|
|
288
|
-
if (this.debug) {
|
|
289
|
-
this.logger.log(
|
|
290
|
-
`[Marvin] ${response.status} ${response.statusText} (${duration}ms)`
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
141
|
if (response.status === 401 && this.auth.handleUnauthorized) {
|
|
294
142
|
await this.auth.handleUnauthorized();
|
|
295
143
|
}
|
|
296
144
|
if (!response.ok) {
|
|
297
145
|
const errorText = await response.text().catch(() => "");
|
|
298
|
-
if (this.debug) {
|
|
299
|
-
this.logger.error(
|
|
300
|
-
`[Marvin] Error response: ${response.status} ${response.statusText}`,
|
|
301
|
-
errorText ? `Body: ${errorText}` : ""
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
const isRetryable = this.retryConfig.retryableStatuses.includes(response.status);
|
|
305
|
-
const canRetry = retryAttempt < this.retryConfig.maxRetries;
|
|
306
|
-
if (isRetryable && canRetry) {
|
|
307
|
-
const delay = this.calculateRetryDelay(retryAttempt);
|
|
308
|
-
if (this.debug) {
|
|
309
|
-
this.logger.warn(
|
|
310
|
-
`[Marvin] Retry ${retryAttempt + 1}/${this.retryConfig.maxRetries} after ${delay}ms (status: ${response.status})`
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
await this.sleep(delay);
|
|
314
|
-
return this.request(method, endpoint, options, retryAttempt + 1);
|
|
315
|
-
}
|
|
316
|
-
if (response.status === 404) {
|
|
317
|
-
throw new MarvinNotFoundError(
|
|
318
|
-
`Resource not found: ${endpoint}`,
|
|
319
|
-
endpoint
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
if (response.status === 401 || response.status === 403) {
|
|
323
|
-
throw new MarvinAuthError(
|
|
324
|
-
`Authentication failed: ${response.statusText}`,
|
|
325
|
-
response.status
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
if (response.status >= 500) {
|
|
329
|
-
throw new MarvinServerError(
|
|
330
|
-
`Server error: ${response.status} ${response.statusText}`,
|
|
331
|
-
response.status,
|
|
332
|
-
endpoint
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
146
|
throw MarvinApiError.fromResponse(
|
|
336
147
|
response.status,
|
|
337
148
|
response.statusText,
|
|
@@ -343,39 +154,21 @@ var HttpClient = class {
|
|
|
343
154
|
if (!contentType || !contentType.includes("application/json")) {
|
|
344
155
|
return void 0;
|
|
345
156
|
}
|
|
346
|
-
|
|
347
|
-
if (this.debug) {
|
|
348
|
-
const sanitized = this.sanitizeForLogging(data);
|
|
349
|
-
this.logger.log("[Marvin] Response:", JSON.stringify(sanitized, null, 2));
|
|
350
|
-
}
|
|
351
|
-
return data;
|
|
157
|
+
return await response.json();
|
|
352
158
|
} catch (error) {
|
|
353
159
|
clearTimeout(timeoutId);
|
|
354
|
-
if (error instanceof MarvinApiError
|
|
160
|
+
if (error instanceof MarvinApiError) {
|
|
355
161
|
throw error;
|
|
356
162
|
}
|
|
357
163
|
if (error instanceof Error) {
|
|
358
|
-
const canRetry = retryAttempt < this.retryConfig.maxRetries;
|
|
359
|
-
if (canRetry && error.name !== "AbortError") {
|
|
360
|
-
const delay = this.calculateRetryDelay(retryAttempt);
|
|
361
|
-
if (this.debug) {
|
|
362
|
-
this.logger.warn(
|
|
363
|
-
`[Marvin] Retry ${retryAttempt + 1}/${this.retryConfig.maxRetries} after ${delay}ms (network error)`
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
await this.sleep(delay);
|
|
367
|
-
return this.request(method, endpoint, options, retryAttempt + 1);
|
|
368
|
-
}
|
|
369
164
|
if (error.name === "AbortError") {
|
|
370
|
-
throw new
|
|
165
|
+
throw new MarvinApiError(
|
|
371
166
|
`Request timeout after ${this.timeout}ms`,
|
|
372
|
-
|
|
167
|
+
408,
|
|
168
|
+
endpoint
|
|
373
169
|
);
|
|
374
170
|
}
|
|
375
|
-
throw new
|
|
376
|
-
`Network error: ${error.message}`,
|
|
377
|
-
error
|
|
378
|
-
);
|
|
171
|
+
throw new MarvinApiError(error.message, 0, endpoint);
|
|
379
172
|
}
|
|
380
173
|
throw error;
|
|
381
174
|
}
|
|
@@ -420,27 +213,10 @@ var SessionAuth = class {
|
|
|
420
213
|
}
|
|
421
214
|
sessionToken;
|
|
422
215
|
onUnauthorized;
|
|
423
|
-
csrfToken;
|
|
424
|
-
/**
|
|
425
|
-
* Set CSRF token for cross-site request forgery protection
|
|
426
|
-
* The token should be obtained from the server after login
|
|
427
|
-
*/
|
|
428
|
-
setCsrfToken(token) {
|
|
429
|
-
this.csrfToken = token;
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Get current CSRF token
|
|
433
|
-
*/
|
|
434
|
-
getCsrfToken() {
|
|
435
|
-
return this.csrfToken;
|
|
436
|
-
}
|
|
437
216
|
injectAuth(headers) {
|
|
438
217
|
if (this.sessionToken) {
|
|
439
218
|
headers.set("Authorization", `Bearer ${this.sessionToken}`);
|
|
440
219
|
}
|
|
441
|
-
if (this.csrfToken) {
|
|
442
|
-
headers.set("X-CSRF-Token", this.csrfToken);
|
|
443
|
-
}
|
|
444
220
|
}
|
|
445
221
|
async handleUnauthorized() {
|
|
446
222
|
if (this.onUnauthorized) {
|
|
@@ -448,9 +224,7 @@ var SessionAuth = class {
|
|
|
448
224
|
}
|
|
449
225
|
}
|
|
450
226
|
getDescription() {
|
|
451
|
-
|
|
452
|
-
const csrf = this.csrfToken ? " with CSRF protection" : "";
|
|
453
|
-
return auth + csrf;
|
|
227
|
+
return this.sessionToken ? "Session authentication (explicit token)" : "Session authentication (cookies)";
|
|
454
228
|
}
|
|
455
229
|
};
|
|
456
230
|
|
|
@@ -470,8 +244,7 @@ var EntriesModule = class {
|
|
|
470
244
|
* Get a single entry by ID
|
|
471
245
|
*/
|
|
472
246
|
async get(id) {
|
|
473
|
-
|
|
474
|
-
return this.http.get(`/api/platform/entries/${validId}`);
|
|
247
|
+
return this.http.get(`/api/platform/entries/${id}`);
|
|
475
248
|
}
|
|
476
249
|
/**
|
|
477
250
|
* Create a new entry
|
|
@@ -483,31 +256,26 @@ var EntriesModule = class {
|
|
|
483
256
|
* Update an entry
|
|
484
257
|
*/
|
|
485
258
|
async update(id, data) {
|
|
486
|
-
|
|
487
|
-
return this.http.patch(`/api/platform/entries/${validId}`, data);
|
|
259
|
+
return this.http.patch(`/api/platform/entries/${id}`, data);
|
|
488
260
|
}
|
|
489
261
|
/**
|
|
490
262
|
* Delete an entry
|
|
491
263
|
*/
|
|
492
264
|
async delete(id) {
|
|
493
|
-
|
|
494
|
-
return this.http.delete(`/api/platform/entries/${validId}`);
|
|
265
|
+
return this.http.delete(`/api/platform/entries/${id}`);
|
|
495
266
|
}
|
|
496
267
|
/**
|
|
497
268
|
* Get collections that this entry belongs to
|
|
498
269
|
*/
|
|
499
270
|
async listCollections(entryId) {
|
|
500
|
-
|
|
501
|
-
return this.http.get(`/api/platform/entries/${validId}/collections`);
|
|
271
|
+
return this.http.get(`/api/platform/entries/${entryId}/collections`);
|
|
502
272
|
}
|
|
503
273
|
/**
|
|
504
274
|
* Add entry to a collection
|
|
505
275
|
*/
|
|
506
276
|
async addToCollection(entryId, collectionId) {
|
|
507
|
-
const validEntryId = this.http.validatePathParam(entryId, "entry ID");
|
|
508
|
-
const validCollectionId = this.http.validatePathParam(collectionId, "collection ID");
|
|
509
277
|
return this.http.post(
|
|
510
|
-
`/api/platform/entries/${
|
|
278
|
+
`/api/platform/entries/${entryId}/collections/${collectionId}`,
|
|
511
279
|
{}
|
|
512
280
|
);
|
|
513
281
|
}
|
|
@@ -515,9 +283,7 @@ var EntriesModule = class {
|
|
|
515
283
|
* Remove entry from a collection
|
|
516
284
|
*/
|
|
517
285
|
async removeFromCollection(entryId, collectionId) {
|
|
518
|
-
|
|
519
|
-
const validCollectionId = this.http.validatePathParam(collectionId, "collection ID");
|
|
520
|
-
return this.http.delete(`/api/platform/entries/${validEntryId}/collections/${validCollectionId}`);
|
|
286
|
+
return this.http.delete(`/api/platform/entries/${entryId}/collections/${collectionId}`);
|
|
521
287
|
}
|
|
522
288
|
};
|
|
523
289
|
|
|
@@ -537,8 +303,7 @@ var CollectionsModule = class {
|
|
|
537
303
|
* Get a single collection by ID
|
|
538
304
|
*/
|
|
539
305
|
async get(id) {
|
|
540
|
-
|
|
541
|
-
return this.http.get(`/api/platform/collections/${validId}`);
|
|
306
|
+
return this.http.get(`/api/platform/collections/${id}`);
|
|
542
307
|
}
|
|
543
308
|
/**
|
|
544
309
|
* Create a new collection
|
|
@@ -550,29 +315,25 @@ var CollectionsModule = class {
|
|
|
550
315
|
* Update a collection
|
|
551
316
|
*/
|
|
552
317
|
async update(id, data) {
|
|
553
|
-
|
|
554
|
-
return this.http.patch(`/api/platform/collections/${validId}`, data);
|
|
318
|
+
return this.http.patch(`/api/platform/collections/${id}`, data);
|
|
555
319
|
}
|
|
556
320
|
/**
|
|
557
321
|
* Delete a collection
|
|
558
322
|
*/
|
|
559
323
|
async delete(id) {
|
|
560
|
-
|
|
561
|
-
return this.http.delete(`/api/platform/collections/${validId}`);
|
|
324
|
+
return this.http.delete(`/api/platform/collections/${id}`);
|
|
562
325
|
}
|
|
563
326
|
/**
|
|
564
327
|
* Get entries in a collection
|
|
565
328
|
*/
|
|
566
329
|
async getEntries(id) {
|
|
567
|
-
|
|
568
|
-
return this.http.get(`/api/platform/collections/${validId}/entries`);
|
|
330
|
+
return this.http.get(`/api/platform/collections/${id}/entries`);
|
|
569
331
|
}
|
|
570
332
|
/**
|
|
571
333
|
* Reorder entries in a collection
|
|
572
334
|
*/
|
|
573
335
|
async reorderEntries(id, entries) {
|
|
574
|
-
|
|
575
|
-
return this.http.patch(`/api/platform/collections/${validId}/entries/order`, { entries });
|
|
336
|
+
return this.http.patch(`/api/platform/collections/${id}/entries/order`, { entries });
|
|
576
337
|
}
|
|
577
338
|
};
|
|
578
339
|
|
|
@@ -592,8 +353,7 @@ var ResourcesModule = class {
|
|
|
592
353
|
* Get a single resource by ID
|
|
593
354
|
*/
|
|
594
355
|
async get(id) {
|
|
595
|
-
|
|
596
|
-
return this.http.get(`/api/platform/resources/${validId}`);
|
|
356
|
+
return this.http.get(`/api/platform/resources/${id}`);
|
|
597
357
|
}
|
|
598
358
|
/**
|
|
599
359
|
* Create a new resource
|
|
@@ -605,8 +365,7 @@ var ResourcesModule = class {
|
|
|
605
365
|
* Update a resource
|
|
606
366
|
*/
|
|
607
367
|
async update(id, data) {
|
|
608
|
-
|
|
609
|
-
return this.http.patch(`/api/platform/resources/${validId}`, data);
|
|
368
|
+
return this.http.patch(`/api/platform/resources/${id}`, data);
|
|
610
369
|
}
|
|
611
370
|
/**
|
|
612
371
|
* Delete a resource
|
|
@@ -628,47 +387,6 @@ var AssetsModule = class {
|
|
|
628
387
|
this.http = http;
|
|
629
388
|
}
|
|
630
389
|
http;
|
|
631
|
-
/**
|
|
632
|
-
* Validate file before upload
|
|
633
|
-
*/
|
|
634
|
-
validateFile(file, options) {
|
|
635
|
-
const maxSize = options?.maxFileSize || 100 * 1024 * 1024;
|
|
636
|
-
const allowedTypes = options?.allowedMimeTypes;
|
|
637
|
-
if (file.size === 0) {
|
|
638
|
-
throw new MarvinValidationError("File is empty");
|
|
639
|
-
}
|
|
640
|
-
if (file.size > maxSize) {
|
|
641
|
-
const maxMB = Math.round(maxSize / (1024 * 1024));
|
|
642
|
-
const fileMB = Math.round(file.size / (1024 * 1024));
|
|
643
|
-
throw new MarvinValidationError(
|
|
644
|
-
`File size (${fileMB}MB) exceeds maximum allowed size of ${maxMB}MB`
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
|
-
if (allowedTypes && allowedTypes.length > 0) {
|
|
648
|
-
if (file instanceof File && file.type) {
|
|
649
|
-
const isAllowed = allowedTypes.some((allowed) => {
|
|
650
|
-
if (allowed.endsWith("/*")) {
|
|
651
|
-
const prefix = allowed.slice(0, -2);
|
|
652
|
-
return file.type.startsWith(prefix);
|
|
653
|
-
}
|
|
654
|
-
return file.type === allowed;
|
|
655
|
-
});
|
|
656
|
-
if (!isAllowed) {
|
|
657
|
-
throw new MarvinValidationError(
|
|
658
|
-
`File type "${file.type}" is not allowed. Allowed types: ${allowedTypes.join(", ")}`
|
|
659
|
-
);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
if (file instanceof File) {
|
|
664
|
-
if (!file.name || file.name.trim().length === 0) {
|
|
665
|
-
throw new MarvinValidationError("File must have a valid name");
|
|
666
|
-
}
|
|
667
|
-
if (file.name.includes("..") || file.name.includes("/") || file.name.includes("\\")) {
|
|
668
|
-
throw new MarvinValidationError("Invalid filename: cannot contain path separators");
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
390
|
/**
|
|
673
391
|
* List all assets
|
|
674
392
|
*/
|
|
@@ -679,31 +397,16 @@ var AssetsModule = class {
|
|
|
679
397
|
* Get a single asset by ID
|
|
680
398
|
*/
|
|
681
399
|
async get(id) {
|
|
682
|
-
|
|
683
|
-
return this.http.get(`/api/platform/assets/${validId}`);
|
|
400
|
+
return this.http.get(`/api/platform/assets/${id}`);
|
|
684
401
|
}
|
|
685
402
|
/**
|
|
686
403
|
* Upload a new asset file
|
|
687
404
|
*
|
|
688
405
|
* @param file - The file to upload (File or Blob)
|
|
689
406
|
* @param metadata - Asset metadata (slug, name, altText, description, metadata)
|
|
690
|
-
* @param options - Upload options (file size limits, allowed types)
|
|
691
407
|
* @returns The created asset with extracted metadata
|
|
692
|
-
*
|
|
693
|
-
* @example
|
|
694
|
-
* ```typescript
|
|
695
|
-
* // Upload with default limits (100MB, all types)
|
|
696
|
-
* const asset = await assets.upload(file, { slug: 'my-image', name: 'My Image' });
|
|
697
|
-
*
|
|
698
|
-
* // Upload with restrictions
|
|
699
|
-
* const asset = await assets.upload(file,
|
|
700
|
-
* { slug: 'avatar', name: 'Avatar' },
|
|
701
|
-
* { maxFileSize: 5 * 1024 * 1024, allowedMimeTypes: ['image/*'] }
|
|
702
|
-
* );
|
|
703
|
-
* ```
|
|
704
408
|
*/
|
|
705
|
-
async upload(file, metadata
|
|
706
|
-
this.validateFile(file, options);
|
|
409
|
+
async upload(file, metadata) {
|
|
707
410
|
const formData = new FormData();
|
|
708
411
|
formData.append("file", file);
|
|
709
412
|
formData.append("slug", metadata.slug);
|
|
@@ -723,24 +426,13 @@ var AssetsModule = class {
|
|
|
723
426
|
* Update an asset (editable fields only: slug, name, altText, description, metadata)
|
|
724
427
|
*/
|
|
725
428
|
async update(id, data) {
|
|
726
|
-
|
|
727
|
-
return this.http.patch(`/api/platform/assets/${validId}`, data);
|
|
429
|
+
return this.http.patch(`/api/platform/assets/${id}`, data);
|
|
728
430
|
}
|
|
729
431
|
/**
|
|
730
432
|
* Delete an asset (removes file from storage and database)
|
|
731
433
|
*/
|
|
732
434
|
async delete(id) {
|
|
733
|
-
|
|
734
|
-
return this.http.delete(`/api/platform/assets/${validId}`);
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* Get download URL for an asset file
|
|
738
|
-
* Use this to create download links or fetch the file directly
|
|
739
|
-
*/
|
|
740
|
-
getDownloadUrl(id, baseUrl) {
|
|
741
|
-
const validId = this.http.validatePathParam(id, "asset ID");
|
|
742
|
-
const base = baseUrl || (typeof window !== "undefined" && window.location ? window.location.origin : "");
|
|
743
|
-
return `${base}/api/platform/assets/${validId}/file`;
|
|
435
|
+
return this.http.delete(`/api/platform/assets/${id}`);
|
|
744
436
|
}
|
|
745
437
|
};
|
|
746
438
|
|
|
@@ -760,8 +452,7 @@ var APIClientsModule = class {
|
|
|
760
452
|
* Get a single API client by ID
|
|
761
453
|
*/
|
|
762
454
|
async get(id) {
|
|
763
|
-
|
|
764
|
-
return this.http.get(`/api/platform/api-clients/${validId}`);
|
|
455
|
+
return this.http.get(`/api/platform/api-clients/${id}`);
|
|
765
456
|
}
|
|
766
457
|
/**
|
|
767
458
|
* Create a new API client (returns client with token)
|
|
@@ -773,8 +464,7 @@ var APIClientsModule = class {
|
|
|
773
464
|
* Update an API client
|
|
774
465
|
*/
|
|
775
466
|
async update(id, data) {
|
|
776
|
-
|
|
777
|
-
return this.http.patch(`/api/platform/api-clients/${validId}`, data);
|
|
467
|
+
return this.http.patch(`/api/platform/api-clients/${id}`, data);
|
|
778
468
|
}
|
|
779
469
|
/**
|
|
780
470
|
* Delete an API client
|
|
@@ -938,15 +628,6 @@ var WorkspacesModule = class {
|
|
|
938
628
|
const url = `/api/admin/groups/${id}${force ? "?force=true" : ""}`;
|
|
939
629
|
await this.http.delete(url);
|
|
940
630
|
}
|
|
941
|
-
/**
|
|
942
|
-
* Export workspace data as JSON (collections, entry types, entries, site config)
|
|
943
|
-
*/
|
|
944
|
-
async export(options) {
|
|
945
|
-
const pretty = options?.pretty ?? true;
|
|
946
|
-
const endpoint = pretty ? "/api/platform/workspace/export/pretty" : "/api/platform/workspace/export";
|
|
947
|
-
const params = options?.includeSystemTypes ? { include_system_types: true } : void 0;
|
|
948
|
-
return this.http.get(endpoint, params);
|
|
949
|
-
}
|
|
950
631
|
/**
|
|
951
632
|
* Get workspace preferences
|
|
952
633
|
*/
|
|
@@ -994,23 +675,11 @@ var InvitesModule = class {
|
|
|
994
675
|
}
|
|
995
676
|
/**
|
|
996
677
|
* Generate invitation URL from a token
|
|
997
|
-
*
|
|
998
|
-
* @param token - Invitation token
|
|
999
|
-
* @param baseUrl - Base URL for the frontend (required in browser environments)
|
|
1000
|
-
* @returns Full invitation URL
|
|
1001
|
-
*
|
|
1002
|
-
* @example
|
|
1003
|
-
* ```typescript
|
|
1004
|
-
* // Node.js (can use env vars)
|
|
1005
|
-
* const url = invites.getInvitationUrl(token);
|
|
1006
|
-
*
|
|
1007
|
-
* // Browser (must provide baseUrl)
|
|
1008
|
-
* const url = invites.getInvitationUrl(token, 'https://app.example.com');
|
|
1009
|
-
* ```
|
|
678
|
+
* Uses MARVIN_FRONTEND_URL for the base URL, falls back to MARVIN_API_URL with common port mappings
|
|
1010
679
|
*/
|
|
1011
680
|
getInvitationUrl(token, baseUrl) {
|
|
1012
681
|
let base = baseUrl;
|
|
1013
|
-
if (!base
|
|
682
|
+
if (!base) {
|
|
1014
683
|
base = process.env.MARVIN_FRONTEND_URL;
|
|
1015
684
|
if (!base && process.env.MARVIN_API_URL) {
|
|
1016
685
|
const apiUrl = process.env.MARVIN_API_URL;
|
|
@@ -1022,17 +691,11 @@ var InvitesModule = class {
|
|
|
1022
691
|
base = apiUrl;
|
|
1023
692
|
}
|
|
1024
693
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
if (typeof window !== "undefined" && window.location) {
|
|
1028
|
-
base = window.location.origin;
|
|
1029
|
-
} else {
|
|
1030
|
-
throw new Error(
|
|
1031
|
-
'baseUrl is required when not in Node.js environment. Pass the frontend URL as second argument: getInvitationUrl(token, "https://app.example.com")'
|
|
1032
|
-
);
|
|
694
|
+
if (!base) {
|
|
695
|
+
base = "http://localhost:4321";
|
|
1033
696
|
}
|
|
1034
697
|
}
|
|
1035
|
-
return `${base}/register?token=${
|
|
698
|
+
return `${base}/register?token=${token}`;
|
|
1036
699
|
}
|
|
1037
700
|
};
|
|
1038
701
|
|
|
@@ -1086,57 +749,6 @@ var WebhooksModule = class {
|
|
|
1086
749
|
this.http = http;
|
|
1087
750
|
}
|
|
1088
751
|
http;
|
|
1089
|
-
/**
|
|
1090
|
-
* Validate webhook URL to prevent SSRF attacks
|
|
1091
|
-
*/
|
|
1092
|
-
validateWebhookUrl(url) {
|
|
1093
|
-
if (!url || typeof url !== "string") {
|
|
1094
|
-
throw new MarvinValidationError("Webhook URL is required and must be a string");
|
|
1095
|
-
}
|
|
1096
|
-
let parsed;
|
|
1097
|
-
try {
|
|
1098
|
-
parsed = new URL(url);
|
|
1099
|
-
} catch {
|
|
1100
|
-
throw new MarvinValidationError("Invalid webhook URL format. Expected: https://example.com/webhook");
|
|
1101
|
-
}
|
|
1102
|
-
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
1103
|
-
throw new MarvinValidationError(
|
|
1104
|
-
`Webhook URL must use HTTP or HTTPS protocol. Got: ${parsed.protocol}`
|
|
1105
|
-
);
|
|
1106
|
-
}
|
|
1107
|
-
const hostname = parsed.hostname.toLowerCase();
|
|
1108
|
-
const blockedHosts = [
|
|
1109
|
-
"localhost",
|
|
1110
|
-
"127.0.0.1",
|
|
1111
|
-
"0.0.0.0",
|
|
1112
|
-
"::1",
|
|
1113
|
-
"169.254.169.254",
|
|
1114
|
-
// AWS metadata service
|
|
1115
|
-
"[::]"
|
|
1116
|
-
];
|
|
1117
|
-
if (blockedHosts.some((blocked) => hostname === blocked || hostname.includes(blocked))) {
|
|
1118
|
-
throw new MarvinValidationError(
|
|
1119
|
-
"Webhook URL cannot target localhost or internal network addresses"
|
|
1120
|
-
);
|
|
1121
|
-
}
|
|
1122
|
-
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
1123
|
-
const match = hostname.match(ipv4Regex);
|
|
1124
|
-
if (match) {
|
|
1125
|
-
const octets = match.slice(1).map(Number);
|
|
1126
|
-
if (octets[0] === 10) {
|
|
1127
|
-
throw new MarvinValidationError("Webhook URL cannot target private IP range (10.x.x.x)");
|
|
1128
|
-
}
|
|
1129
|
-
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {
|
|
1130
|
-
throw new MarvinValidationError("Webhook URL cannot target private IP range (172.16-31.x.x)");
|
|
1131
|
-
}
|
|
1132
|
-
if (octets[0] === 192 && octets[1] === 168) {
|
|
1133
|
-
throw new MarvinValidationError("Webhook URL cannot target private IP range (192.168.x.x)");
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
if (url.length > 2048) {
|
|
1137
|
-
throw new MarvinValidationError("Webhook URL exceeds maximum length of 2048 characters");
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
752
|
/**
|
|
1141
753
|
* List all webhooks for the current workspace
|
|
1142
754
|
*/
|
|
@@ -1148,43 +760,31 @@ var WebhooksModule = class {
|
|
|
1148
760
|
* Get a webhook by ID
|
|
1149
761
|
*/
|
|
1150
762
|
async get(id) {
|
|
1151
|
-
|
|
1152
|
-
return this.http.get(`/api/groups/webhooks/${validId}`);
|
|
763
|
+
return this.http.get(`/api/groups/webhooks/${id}`);
|
|
1153
764
|
}
|
|
1154
765
|
/**
|
|
1155
766
|
* Create a new webhook
|
|
1156
|
-
* Validates URL to prevent SSRF attacks
|
|
1157
767
|
*/
|
|
1158
768
|
async create(data) {
|
|
1159
|
-
if (data.url) {
|
|
1160
|
-
this.validateWebhookUrl(data.url);
|
|
1161
|
-
}
|
|
1162
769
|
return this.http.post("/api/groups/webhooks", data);
|
|
1163
770
|
}
|
|
1164
771
|
/**
|
|
1165
772
|
* Update a webhook
|
|
1166
|
-
* Validates URL if provided to prevent SSRF attacks
|
|
1167
773
|
*/
|
|
1168
774
|
async update(id, data) {
|
|
1169
|
-
|
|
1170
|
-
if (data.url) {
|
|
1171
|
-
this.validateWebhookUrl(data.url);
|
|
1172
|
-
}
|
|
1173
|
-
return this.http.put(`/api/groups/webhooks/${validId}`, data);
|
|
775
|
+
return this.http.put(`/api/groups/webhooks/${id}`, data);
|
|
1174
776
|
}
|
|
1175
777
|
/**
|
|
1176
778
|
* Delete a webhook
|
|
1177
779
|
*/
|
|
1178
780
|
async delete(id) {
|
|
1179
|
-
|
|
1180
|
-
await this.http.delete(`/api/groups/webhooks/${validId}`);
|
|
781
|
+
await this.http.delete(`/api/groups/webhooks/${id}`);
|
|
1181
782
|
}
|
|
1182
783
|
/**
|
|
1183
784
|
* Test a webhook
|
|
1184
785
|
*/
|
|
1185
786
|
async test(id) {
|
|
1186
|
-
|
|
1187
|
-
return this.http.post(`/api/groups/webhooks/${validId}/test`, {});
|
|
787
|
+
return this.http.post(`/api/groups/webhooks/${id}/test`, {});
|
|
1188
788
|
}
|
|
1189
789
|
/**
|
|
1190
790
|
* Rerun failed webhooks
|
|
@@ -1276,7 +876,6 @@ var AdminSystemModule = class {
|
|
|
1276
876
|
}
|
|
1277
877
|
/**
|
|
1278
878
|
* Update email settings
|
|
1279
|
-
* Note: Password field is write-only and will not be returned in response
|
|
1280
879
|
*/
|
|
1281
880
|
async updateEmailSettings(data) {
|
|
1282
881
|
return this.http.put("/api/admin/email", data);
|
|
@@ -1552,234 +1151,7 @@ var ThemeModule = class {
|
|
|
1552
1151
|
}
|
|
1553
1152
|
};
|
|
1554
1153
|
|
|
1555
|
-
// src/platform/emailTemplates.ts
|
|
1556
|
-
var EmailTemplatesClient = class {
|
|
1557
|
-
constructor(http) {
|
|
1558
|
-
this.http = http;
|
|
1559
|
-
}
|
|
1560
|
-
http;
|
|
1561
|
-
/**
|
|
1562
|
-
* List all email templates for the workspace
|
|
1563
|
-
* @param groupId - Workspace ID
|
|
1564
|
-
*/
|
|
1565
|
-
async list(groupId) {
|
|
1566
|
-
return this.http.get(`/api/platform/workspaces/${groupId}/email-templates`);
|
|
1567
|
-
}
|
|
1568
|
-
/**
|
|
1569
|
-
* Get a specific email template
|
|
1570
|
-
* @param groupId - Workspace ID
|
|
1571
|
-
* @param templateId - Template ID
|
|
1572
|
-
*/
|
|
1573
|
-
async get(groupId, templateId) {
|
|
1574
|
-
return this.http.get(`/api/platform/workspaces/${groupId}/email-templates/${templateId}`);
|
|
1575
|
-
}
|
|
1576
|
-
/**
|
|
1577
|
-
* Create a new email template
|
|
1578
|
-
* @param groupId - Workspace ID
|
|
1579
|
-
* @param data - Template data
|
|
1580
|
-
*/
|
|
1581
|
-
async create(groupId, data) {
|
|
1582
|
-
return this.http.post(`/api/platform/workspaces/${groupId}/email-templates`, data);
|
|
1583
|
-
}
|
|
1584
|
-
/**
|
|
1585
|
-
* Update an email template
|
|
1586
|
-
* @param groupId - Workspace ID
|
|
1587
|
-
* @param templateId - Template ID
|
|
1588
|
-
* @param data - Updated template data
|
|
1589
|
-
*/
|
|
1590
|
-
async update(groupId, templateId, data) {
|
|
1591
|
-
return this.http.patch(`/api/platform/workspaces/${groupId}/email-templates/${templateId}`, data);
|
|
1592
|
-
}
|
|
1593
|
-
/**
|
|
1594
|
-
* Delete an email template
|
|
1595
|
-
* @param groupId - Workspace ID
|
|
1596
|
-
* @param templateId - Template ID
|
|
1597
|
-
*/
|
|
1598
|
-
async delete(groupId, templateId) {
|
|
1599
|
-
await this.http.delete(`/api/platform/workspaces/${groupId}/email-templates/${templateId}`);
|
|
1600
|
-
}
|
|
1601
|
-
/**
|
|
1602
|
-
* Send a test email using a template
|
|
1603
|
-
* @param groupId - Workspace ID
|
|
1604
|
-
* @param templateId - Template ID
|
|
1605
|
-
* @param recipientEmail - Test recipient email address
|
|
1606
|
-
*/
|
|
1607
|
-
async sendTest(groupId, templateId, recipientEmail) {
|
|
1608
|
-
return this.http.post(`/api/platform/workspaces/${groupId}/email-templates/${templateId}/test`, {
|
|
1609
|
-
recipient_email: recipientEmail
|
|
1610
|
-
});
|
|
1611
|
-
}
|
|
1612
|
-
};
|
|
1613
|
-
|
|
1614
|
-
// src/platform/scheduledTasks.ts
|
|
1615
|
-
var ScheduledTasksModule = class {
|
|
1616
|
-
constructor(http) {
|
|
1617
|
-
this.http = http;
|
|
1618
|
-
}
|
|
1619
|
-
http;
|
|
1620
|
-
/**
|
|
1621
|
-
* List all scheduled tasks for the current workspace
|
|
1622
|
-
*/
|
|
1623
|
-
async list() {
|
|
1624
|
-
return this.http.get("/api/platform/scheduled-tasks");
|
|
1625
|
-
}
|
|
1626
|
-
/**
|
|
1627
|
-
* Get a scheduled task by ID or slug
|
|
1628
|
-
*/
|
|
1629
|
-
async get(idOrSlug) {
|
|
1630
|
-
const validIdOrSlug = this.http.validatePathParam(idOrSlug, "idOrSlug");
|
|
1631
|
-
return this.http.get(`/api/platform/scheduled-tasks/${validIdOrSlug}`);
|
|
1632
|
-
}
|
|
1633
|
-
/**
|
|
1634
|
-
* Create a new scheduled task
|
|
1635
|
-
*/
|
|
1636
|
-
async create(data) {
|
|
1637
|
-
return this.http.post("/api/platform/scheduled-tasks", data);
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Update a scheduled task
|
|
1641
|
-
*/
|
|
1642
|
-
async update(idOrSlug, data) {
|
|
1643
|
-
const validIdOrSlug = this.http.validatePathParam(idOrSlug, "idOrSlug");
|
|
1644
|
-
return this.http.patch(`/api/platform/scheduled-tasks/${validIdOrSlug}`, data);
|
|
1645
|
-
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Delete a scheduled task
|
|
1648
|
-
*/
|
|
1649
|
-
async delete(idOrSlug) {
|
|
1650
|
-
await this.http.delete(`/api/platform/scheduled-tasks/${idOrSlug}`);
|
|
1651
|
-
}
|
|
1652
|
-
/**
|
|
1653
|
-
* Manually trigger task execution (bypass schedule)
|
|
1654
|
-
*/
|
|
1655
|
-
async execute(idOrSlug) {
|
|
1656
|
-
await this.http.post(`/api/platform/scheduled-tasks/${idOrSlug}/execute`, {});
|
|
1657
|
-
}
|
|
1658
|
-
/**
|
|
1659
|
-
* Get task execution history
|
|
1660
|
-
*/
|
|
1661
|
-
async history(idOrSlug, options) {
|
|
1662
|
-
const params = new URLSearchParams();
|
|
1663
|
-
if (options?.limit) {
|
|
1664
|
-
params.set("limit", options.limit.toString());
|
|
1665
|
-
}
|
|
1666
|
-
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1667
|
-
return this.http.get(
|
|
1668
|
-
`/api/platform/scheduled-tasks/${idOrSlug}/history${query}`
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
/**
|
|
1672
|
-
* Get available task types
|
|
1673
|
-
*/
|
|
1674
|
-
async taskTypes(options) {
|
|
1675
|
-
const params = new URLSearchParams();
|
|
1676
|
-
if (options?.detailed) {
|
|
1677
|
-
params.set("detailed", "true");
|
|
1678
|
-
}
|
|
1679
|
-
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1680
|
-
return this.http.get(`/api/platform/scheduled-tasks/task-types${query}`);
|
|
1681
|
-
}
|
|
1682
|
-
};
|
|
1683
|
-
|
|
1684
|
-
// src/platform/forms.ts
|
|
1685
|
-
var FormsModule = class {
|
|
1686
|
-
constructor(http) {
|
|
1687
|
-
this.http = http;
|
|
1688
|
-
}
|
|
1689
|
-
http;
|
|
1690
|
-
/**
|
|
1691
|
-
* Validate form submission data
|
|
1692
|
-
* - Prevents XSS by checking for script tags
|
|
1693
|
-
* - Validates data is a proper object
|
|
1694
|
-
* - Checks for reasonable size limits
|
|
1695
|
-
*/
|
|
1696
|
-
validateFormData(data) {
|
|
1697
|
-
if (!data || typeof data !== "object") {
|
|
1698
|
-
throw new MarvinValidationError("Form data must be a valid object");
|
|
1699
|
-
}
|
|
1700
|
-
const jsonString = JSON.stringify(data);
|
|
1701
|
-
const sizeInBytes = new Blob([jsonString]).size;
|
|
1702
|
-
if (sizeInBytes > 1024 * 1024) {
|
|
1703
|
-
throw new MarvinValidationError("Form data exceeds maximum size of 1MB");
|
|
1704
|
-
}
|
|
1705
|
-
const checkForScripts = (value) => {
|
|
1706
|
-
if (typeof value === "string") {
|
|
1707
|
-
if (/<script[\s\S]*?>[\s\S]*?<\/script>/gi.test(value)) {
|
|
1708
|
-
throw new MarvinValidationError("Form data contains potentially malicious content");
|
|
1709
|
-
}
|
|
1710
|
-
} else if (Array.isArray(value)) {
|
|
1711
|
-
value.forEach(checkForScripts);
|
|
1712
|
-
} else if (value && typeof value === "object") {
|
|
1713
|
-
Object.values(value).forEach(checkForScripts);
|
|
1714
|
-
}
|
|
1715
|
-
};
|
|
1716
|
-
Object.values(data).forEach(checkForScripts);
|
|
1717
|
-
}
|
|
1718
|
-
/**
|
|
1719
|
-
* List all forms
|
|
1720
|
-
*/
|
|
1721
|
-
async list() {
|
|
1722
|
-
return this.http.get("/api/platform/forms");
|
|
1723
|
-
}
|
|
1724
|
-
/**
|
|
1725
|
-
* Get a single form by ID
|
|
1726
|
-
*/
|
|
1727
|
-
async get(id) {
|
|
1728
|
-
const validId = this.http.validatePathParam(id, "id");
|
|
1729
|
-
return this.http.get(`/api/platform/forms/${validId}`);
|
|
1730
|
-
}
|
|
1731
|
-
/**
|
|
1732
|
-
* Create a new form
|
|
1733
|
-
*/
|
|
1734
|
-
async create(data) {
|
|
1735
|
-
return this.http.post("/api/platform/forms", data);
|
|
1736
|
-
}
|
|
1737
|
-
/**
|
|
1738
|
-
* Update a form
|
|
1739
|
-
*/
|
|
1740
|
-
async update(id, data) {
|
|
1741
|
-
const validId = this.http.validatePathParam(id, "id");
|
|
1742
|
-
return this.http.patch(`/api/platform/forms/${validId}`, data);
|
|
1743
|
-
}
|
|
1744
|
-
/**
|
|
1745
|
-
* Delete a form
|
|
1746
|
-
*/
|
|
1747
|
-
async delete(id) {
|
|
1748
|
-
return this.http.delete(`/api/platform/forms/${id}`);
|
|
1749
|
-
}
|
|
1750
|
-
/**
|
|
1751
|
-
* Get submissions for a form
|
|
1752
|
-
*/
|
|
1753
|
-
async getSubmissions(formId) {
|
|
1754
|
-
return this.http.get(`/api/platform/forms/${formId}/submissions`);
|
|
1755
|
-
}
|
|
1756
|
-
/**
|
|
1757
|
-
* Get a published form by slug (Publishing API - public access)
|
|
1758
|
-
*/
|
|
1759
|
-
async getPublishedForm(slug) {
|
|
1760
|
-
return this.http.get(`/api/publish/forms/${slug}`);
|
|
1761
|
-
}
|
|
1762
|
-
/**
|
|
1763
|
-
* Submit data to a published form (Publishing API - public access)
|
|
1764
|
-
* Validates data before submission to prevent XSS and malicious content
|
|
1765
|
-
*/
|
|
1766
|
-
async submitForm(slug, data) {
|
|
1767
|
-
const validSlug = this.http.validatePathParam(slug, "form slug");
|
|
1768
|
-
this.validateFormData(data);
|
|
1769
|
-
return this.http.post(`/api/publish/forms/${validSlug}/submit`, {
|
|
1770
|
-
dataJson: data
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
};
|
|
1774
|
-
|
|
1775
1154
|
// src/platform/client.ts
|
|
1776
|
-
function createPlatformConfigFromEnv(overrides) {
|
|
1777
|
-
return {
|
|
1778
|
-
apiUrl: overrides?.apiUrl || process.env.MARVIN_API_URL || "http://localhost:8080",
|
|
1779
|
-
userToken: overrides?.userToken || process.env.MARVIN_USER_TOKEN,
|
|
1780
|
-
credentials: overrides?.credentials || "include"
|
|
1781
|
-
};
|
|
1782
|
-
}
|
|
1783
1155
|
var PlatformClient = class extends HttpClient {
|
|
1784
1156
|
entries;
|
|
1785
1157
|
collections;
|
|
@@ -1797,16 +1169,13 @@ var PlatformClient = class extends HttpClient {
|
|
|
1797
1169
|
eventLog;
|
|
1798
1170
|
session;
|
|
1799
1171
|
theme;
|
|
1800
|
-
emailTemplates;
|
|
1801
|
-
scheduledTasks;
|
|
1802
|
-
forms;
|
|
1803
1172
|
// Admin modules
|
|
1804
1173
|
adminUsers;
|
|
1805
1174
|
adminSystem;
|
|
1806
1175
|
adminMaintenance;
|
|
1807
1176
|
constructor(config) {
|
|
1808
1177
|
const httpConfig = {
|
|
1809
|
-
baseUrl: config.apiUrl
|
|
1178
|
+
baseUrl: config.apiUrl,
|
|
1810
1179
|
auth: new SessionAuth(config.userToken),
|
|
1811
1180
|
credentials: config.credentials || "include"
|
|
1812
1181
|
};
|
|
@@ -1827,17 +1196,13 @@ var PlatformClient = class extends HttpClient {
|
|
|
1827
1196
|
this.eventLog = new EventLogModule(this);
|
|
1828
1197
|
this.session = new AuthModule(this);
|
|
1829
1198
|
this.theme = new ThemeModule(this);
|
|
1830
|
-
this.emailTemplates = new EmailTemplatesClient(this);
|
|
1831
|
-
this.scheduledTasks = new ScheduledTasksModule(this);
|
|
1832
|
-
this.forms = new FormsModule(this);
|
|
1833
1199
|
this.adminUsers = new AdminUsersModule(this);
|
|
1834
1200
|
this.adminSystem = new AdminSystemModule(this);
|
|
1835
1201
|
this.adminMaintenance = new AdminMaintenanceModule(this);
|
|
1836
1202
|
}
|
|
1837
1203
|
};
|
|
1838
1204
|
function createPlatformClient(config) {
|
|
1839
|
-
|
|
1840
|
-
return new PlatformClient(finalConfig);
|
|
1205
|
+
return new PlatformClient(config);
|
|
1841
1206
|
}
|
|
1842
1207
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1843
1208
|
0 && (module.exports = {
|
|
@@ -1848,22 +1213,18 @@ function createPlatformClient(config) {
|
|
|
1848
1213
|
AssetsModule,
|
|
1849
1214
|
AuthModule,
|
|
1850
1215
|
CollectionsModule,
|
|
1851
|
-
EmailTemplatesClient,
|
|
1852
1216
|
EntriesModule,
|
|
1853
1217
|
EntryTypesModule,
|
|
1854
1218
|
EventLogModule,
|
|
1855
1219
|
EventsModule,
|
|
1856
|
-
FormsModule,
|
|
1857
1220
|
InvitesModule,
|
|
1858
1221
|
NotificationsModule,
|
|
1859
1222
|
PlatformClient,
|
|
1860
1223
|
ResourcesModule,
|
|
1861
|
-
ScheduledTasksModule,
|
|
1862
1224
|
ThemeModule,
|
|
1863
1225
|
UserModule,
|
|
1864
1226
|
WebhooksModule,
|
|
1865
1227
|
WorkspaceMembersModule,
|
|
1866
1228
|
WorkspacesModule,
|
|
1867
|
-
createPlatformClient
|
|
1868
|
-
createPlatformConfigFromEnv
|
|
1229
|
+
createPlatformClient
|
|
1869
1230
|
});
|