@inneropen/marvin-sdk 2.0.0-next.13 → 2.0.0-next.15
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 +59 -10
- package/dist/{HttpClient-BX5Gqd0w.d.cts → HttpClient-CdF3IHM1.d.cts} +41 -2
- package/dist/{HttpClient-BX5Gqd0w.d.ts → HttpClient-CdF3IHM1.d.ts} +41 -2
- package/dist/chunk-GJNOYXUQ.js +980 -0
- package/dist/{chunk-DYC2BEA4.js → chunk-WJC4X7TB.js} +274 -9
- package/dist/index.cjs +356 -12
- package/dist/index.d.cts +20 -610
- package/dist/index.d.ts +20 -610
- package/dist/index.js +21 -880
- package/dist/marvin-Bx5k6tsF.d.cts +654 -0
- package/dist/marvin-g7dayc83.d.ts +654 -0
- package/dist/platform.cjs +396 -43
- package/dist/platform.d.cts +124 -15
- package/dist/platform.d.ts +124 -15
- package/dist/platform.js +251 -37
- package/dist/publish.cjs +1487 -0
- package/dist/publish.d.cts +49 -0
- package/dist/publish.d.ts +49 -0
- package/dist/publish.js +75 -0
- package/dist/types/index.d.cts +9 -1
- package/dist/types/index.d.ts +9 -1
- package/package.json +11 -6
package/dist/platform.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HttpClient,
|
|
3
|
+
MarvinValidationError,
|
|
3
4
|
SessionAuth,
|
|
4
5
|
init_core
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WJC4X7TB.js";
|
|
6
7
|
|
|
7
8
|
// src/platform/client.ts
|
|
8
9
|
init_core();
|
|
@@ -23,7 +24,8 @@ var EntriesModule = class {
|
|
|
23
24
|
* Get a single entry by ID
|
|
24
25
|
*/
|
|
25
26
|
async get(id) {
|
|
26
|
-
|
|
27
|
+
const validId = this.http.validatePathParam(id, "entry ID");
|
|
28
|
+
return this.http.get(`/api/platform/entries/${validId}`);
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
31
|
* Create a new entry
|
|
@@ -35,26 +37,31 @@ var EntriesModule = class {
|
|
|
35
37
|
* Update an entry
|
|
36
38
|
*/
|
|
37
39
|
async update(id, data) {
|
|
38
|
-
|
|
40
|
+
const validId = this.http.validatePathParam(id, "entry ID");
|
|
41
|
+
return this.http.patch(`/api/platform/entries/${validId}`, data);
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
44
|
* Delete an entry
|
|
42
45
|
*/
|
|
43
46
|
async delete(id) {
|
|
44
|
-
|
|
47
|
+
const validId = this.http.validatePathParam(id, "entry ID");
|
|
48
|
+
return this.http.delete(`/api/platform/entries/${validId}`);
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
47
51
|
* Get collections that this entry belongs to
|
|
48
52
|
*/
|
|
49
53
|
async listCollections(entryId) {
|
|
50
|
-
|
|
54
|
+
const validId = this.http.validatePathParam(entryId, "entry ID");
|
|
55
|
+
return this.http.get(`/api/platform/entries/${validId}/collections`);
|
|
51
56
|
}
|
|
52
57
|
/**
|
|
53
58
|
* Add entry to a collection
|
|
54
59
|
*/
|
|
55
60
|
async addToCollection(entryId, collectionId) {
|
|
61
|
+
const validEntryId = this.http.validatePathParam(entryId, "entry ID");
|
|
62
|
+
const validCollectionId = this.http.validatePathParam(collectionId, "collection ID");
|
|
56
63
|
return this.http.post(
|
|
57
|
-
`/api/platform/entries/${
|
|
64
|
+
`/api/platform/entries/${validEntryId}/collections/${validCollectionId}`,
|
|
58
65
|
{}
|
|
59
66
|
);
|
|
60
67
|
}
|
|
@@ -62,7 +69,9 @@ var EntriesModule = class {
|
|
|
62
69
|
* Remove entry from a collection
|
|
63
70
|
*/
|
|
64
71
|
async removeFromCollection(entryId, collectionId) {
|
|
65
|
-
|
|
72
|
+
const validEntryId = this.http.validatePathParam(entryId, "entry ID");
|
|
73
|
+
const validCollectionId = this.http.validatePathParam(collectionId, "collection ID");
|
|
74
|
+
return this.http.delete(`/api/platform/entries/${validEntryId}/collections/${validCollectionId}`);
|
|
66
75
|
}
|
|
67
76
|
};
|
|
68
77
|
|
|
@@ -82,7 +91,8 @@ var CollectionsModule = class {
|
|
|
82
91
|
* Get a single collection by ID
|
|
83
92
|
*/
|
|
84
93
|
async get(id) {
|
|
85
|
-
|
|
94
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
95
|
+
return this.http.get(`/api/platform/collections/${validId}`);
|
|
86
96
|
}
|
|
87
97
|
/**
|
|
88
98
|
* Create a new collection
|
|
@@ -94,25 +104,29 @@ var CollectionsModule = class {
|
|
|
94
104
|
* Update a collection
|
|
95
105
|
*/
|
|
96
106
|
async update(id, data) {
|
|
97
|
-
|
|
107
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
108
|
+
return this.http.patch(`/api/platform/collections/${validId}`, data);
|
|
98
109
|
}
|
|
99
110
|
/**
|
|
100
111
|
* Delete a collection
|
|
101
112
|
*/
|
|
102
113
|
async delete(id) {
|
|
103
|
-
|
|
114
|
+
const validId = this.http.validatePathParam(id, "collection ID");
|
|
115
|
+
return this.http.delete(`/api/platform/collections/${validId}`);
|
|
104
116
|
}
|
|
105
117
|
/**
|
|
106
118
|
* Get entries in a collection
|
|
107
119
|
*/
|
|
108
120
|
async getEntries(id) {
|
|
109
|
-
|
|
121
|
+
const validId = this.http.validatePathParam(id, "collection ID");
|
|
122
|
+
return this.http.get(`/api/platform/collections/${validId}/entries`);
|
|
110
123
|
}
|
|
111
124
|
/**
|
|
112
125
|
* Reorder entries in a collection
|
|
113
126
|
*/
|
|
114
127
|
async reorderEntries(id, entries) {
|
|
115
|
-
|
|
128
|
+
const validId = this.http.validatePathParam(id, "collection ID");
|
|
129
|
+
return this.http.patch(`/api/platform/collections/${validId}/entries/order`, { entries });
|
|
116
130
|
}
|
|
117
131
|
};
|
|
118
132
|
|
|
@@ -132,7 +146,8 @@ var ResourcesModule = class {
|
|
|
132
146
|
* Get a single resource by ID
|
|
133
147
|
*/
|
|
134
148
|
async get(id) {
|
|
135
|
-
|
|
149
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
150
|
+
return this.http.get(`/api/platform/resources/${validId}`);
|
|
136
151
|
}
|
|
137
152
|
/**
|
|
138
153
|
* Create a new resource
|
|
@@ -144,7 +159,8 @@ var ResourcesModule = class {
|
|
|
144
159
|
* Update a resource
|
|
145
160
|
*/
|
|
146
161
|
async update(id, data) {
|
|
147
|
-
|
|
162
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
163
|
+
return this.http.patch(`/api/platform/resources/${validId}`, data);
|
|
148
164
|
}
|
|
149
165
|
/**
|
|
150
166
|
* Delete a resource
|
|
@@ -161,11 +177,53 @@ var ResourcesModule = class {
|
|
|
161
177
|
};
|
|
162
178
|
|
|
163
179
|
// src/platform/assets.ts
|
|
180
|
+
init_core();
|
|
164
181
|
var AssetsModule = class {
|
|
165
182
|
constructor(http) {
|
|
166
183
|
this.http = http;
|
|
167
184
|
}
|
|
168
185
|
http;
|
|
186
|
+
/**
|
|
187
|
+
* Validate file before upload
|
|
188
|
+
*/
|
|
189
|
+
validateFile(file, options) {
|
|
190
|
+
const maxSize = options?.maxFileSize || 100 * 1024 * 1024;
|
|
191
|
+
const allowedTypes = options?.allowedMimeTypes;
|
|
192
|
+
if (file.size === 0) {
|
|
193
|
+
throw new MarvinValidationError("File is empty");
|
|
194
|
+
}
|
|
195
|
+
if (file.size > maxSize) {
|
|
196
|
+
const maxMB = Math.round(maxSize / (1024 * 1024));
|
|
197
|
+
const fileMB = Math.round(file.size / (1024 * 1024));
|
|
198
|
+
throw new MarvinValidationError(
|
|
199
|
+
`File size (${fileMB}MB) exceeds maximum allowed size of ${maxMB}MB`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
if (allowedTypes && allowedTypes.length > 0) {
|
|
203
|
+
if (file instanceof File && file.type) {
|
|
204
|
+
const isAllowed = allowedTypes.some((allowed) => {
|
|
205
|
+
if (allowed.endsWith("/*")) {
|
|
206
|
+
const prefix = allowed.slice(0, -2);
|
|
207
|
+
return file.type.startsWith(prefix);
|
|
208
|
+
}
|
|
209
|
+
return file.type === allowed;
|
|
210
|
+
});
|
|
211
|
+
if (!isAllowed) {
|
|
212
|
+
throw new MarvinValidationError(
|
|
213
|
+
`File type "${file.type}" is not allowed. Allowed types: ${allowedTypes.join(", ")}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (file instanceof File) {
|
|
219
|
+
if (!file.name || file.name.trim().length === 0) {
|
|
220
|
+
throw new MarvinValidationError("File must have a valid name");
|
|
221
|
+
}
|
|
222
|
+
if (file.name.includes("..") || file.name.includes("/") || file.name.includes("\\")) {
|
|
223
|
+
throw new MarvinValidationError("Invalid filename: cannot contain path separators");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
169
227
|
/**
|
|
170
228
|
* List all assets
|
|
171
229
|
*/
|
|
@@ -176,16 +234,31 @@ var AssetsModule = class {
|
|
|
176
234
|
* Get a single asset by ID
|
|
177
235
|
*/
|
|
178
236
|
async get(id) {
|
|
179
|
-
|
|
237
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
238
|
+
return this.http.get(`/api/platform/assets/${validId}`);
|
|
180
239
|
}
|
|
181
240
|
/**
|
|
182
241
|
* Upload a new asset file
|
|
183
242
|
*
|
|
184
243
|
* @param file - The file to upload (File or Blob)
|
|
185
244
|
* @param metadata - Asset metadata (slug, name, altText, description, metadata)
|
|
245
|
+
* @param options - Upload options (file size limits, allowed types)
|
|
186
246
|
* @returns The created asset with extracted metadata
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* // Upload with default limits (100MB, all types)
|
|
251
|
+
* const asset = await assets.upload(file, { slug: 'my-image', name: 'My Image' });
|
|
252
|
+
*
|
|
253
|
+
* // Upload with restrictions
|
|
254
|
+
* const asset = await assets.upload(file,
|
|
255
|
+
* { slug: 'avatar', name: 'Avatar' },
|
|
256
|
+
* { maxFileSize: 5 * 1024 * 1024, allowedMimeTypes: ['image/*'] }
|
|
257
|
+
* );
|
|
258
|
+
* ```
|
|
187
259
|
*/
|
|
188
|
-
async upload(file, metadata) {
|
|
260
|
+
async upload(file, metadata, options) {
|
|
261
|
+
this.validateFile(file, options);
|
|
189
262
|
const formData = new FormData();
|
|
190
263
|
formData.append("file", file);
|
|
191
264
|
formData.append("slug", metadata.slug);
|
|
@@ -205,13 +278,24 @@ var AssetsModule = class {
|
|
|
205
278
|
* Update an asset (editable fields only: slug, name, altText, description, metadata)
|
|
206
279
|
*/
|
|
207
280
|
async update(id, data) {
|
|
208
|
-
|
|
281
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
282
|
+
return this.http.patch(`/api/platform/assets/${validId}`, data);
|
|
209
283
|
}
|
|
210
284
|
/**
|
|
211
285
|
* Delete an asset (removes file from storage and database)
|
|
212
286
|
*/
|
|
213
287
|
async delete(id) {
|
|
214
|
-
|
|
288
|
+
const validId = this.http.validatePathParam(id, "asset ID");
|
|
289
|
+
return this.http.delete(`/api/platform/assets/${validId}`);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Get download URL for an asset file
|
|
293
|
+
* Use this to create download links or fetch the file directly
|
|
294
|
+
*/
|
|
295
|
+
getDownloadUrl(id, baseUrl) {
|
|
296
|
+
const validId = this.http.validatePathParam(id, "asset ID");
|
|
297
|
+
const base = baseUrl || (typeof window !== "undefined" && window.location ? window.location.origin : "");
|
|
298
|
+
return `${base}/api/platform/assets/${validId}/file`;
|
|
215
299
|
}
|
|
216
300
|
};
|
|
217
301
|
|
|
@@ -231,7 +315,8 @@ var APIClientsModule = class {
|
|
|
231
315
|
* Get a single API client by ID
|
|
232
316
|
*/
|
|
233
317
|
async get(id) {
|
|
234
|
-
|
|
318
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
319
|
+
return this.http.get(`/api/platform/api-clients/${validId}`);
|
|
235
320
|
}
|
|
236
321
|
/**
|
|
237
322
|
* Create a new API client (returns client with token)
|
|
@@ -243,7 +328,8 @@ var APIClientsModule = class {
|
|
|
243
328
|
* Update an API client
|
|
244
329
|
*/
|
|
245
330
|
async update(id, data) {
|
|
246
|
-
|
|
331
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
332
|
+
return this.http.patch(`/api/platform/api-clients/${validId}`, data);
|
|
247
333
|
}
|
|
248
334
|
/**
|
|
249
335
|
* Delete an API client
|
|
@@ -454,11 +540,23 @@ var InvitesModule = class {
|
|
|
454
540
|
}
|
|
455
541
|
/**
|
|
456
542
|
* Generate invitation URL from a token
|
|
457
|
-
*
|
|
543
|
+
*
|
|
544
|
+
* @param token - Invitation token
|
|
545
|
+
* @param baseUrl - Base URL for the frontend (required in browser environments)
|
|
546
|
+
* @returns Full invitation URL
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* ```typescript
|
|
550
|
+
* // Node.js (can use env vars)
|
|
551
|
+
* const url = invites.getInvitationUrl(token);
|
|
552
|
+
*
|
|
553
|
+
* // Browser (must provide baseUrl)
|
|
554
|
+
* const url = invites.getInvitationUrl(token, 'https://app.example.com');
|
|
555
|
+
* ```
|
|
458
556
|
*/
|
|
459
557
|
getInvitationUrl(token, baseUrl) {
|
|
460
558
|
let base = baseUrl;
|
|
461
|
-
if (!base) {
|
|
559
|
+
if (!base && typeof process !== "undefined" && process.env) {
|
|
462
560
|
base = process.env.MARVIN_FRONTEND_URL;
|
|
463
561
|
if (!base && process.env.MARVIN_API_URL) {
|
|
464
562
|
const apiUrl = process.env.MARVIN_API_URL;
|
|
@@ -470,11 +568,17 @@ var InvitesModule = class {
|
|
|
470
568
|
base = apiUrl;
|
|
471
569
|
}
|
|
472
570
|
}
|
|
473
|
-
|
|
474
|
-
|
|
571
|
+
}
|
|
572
|
+
if (!base) {
|
|
573
|
+
if (typeof window !== "undefined" && window.location) {
|
|
574
|
+
base = window.location.origin;
|
|
575
|
+
} else {
|
|
576
|
+
throw new Error(
|
|
577
|
+
'baseUrl is required when not in Node.js environment. Pass the frontend URL as second argument: getInvitationUrl(token, "https://app.example.com")'
|
|
578
|
+
);
|
|
475
579
|
}
|
|
476
580
|
}
|
|
477
|
-
return `${base}/register?token=${token}`;
|
|
581
|
+
return `${base}/register?token=${encodeURIComponent(token)}`;
|
|
478
582
|
}
|
|
479
583
|
};
|
|
480
584
|
|
|
@@ -523,11 +627,63 @@ var NotificationsModule = class {
|
|
|
523
627
|
};
|
|
524
628
|
|
|
525
629
|
// src/platform/webhooks.ts
|
|
630
|
+
init_core();
|
|
526
631
|
var WebhooksModule = class {
|
|
527
632
|
constructor(http) {
|
|
528
633
|
this.http = http;
|
|
529
634
|
}
|
|
530
635
|
http;
|
|
636
|
+
/**
|
|
637
|
+
* Validate webhook URL to prevent SSRF attacks
|
|
638
|
+
*/
|
|
639
|
+
validateWebhookUrl(url) {
|
|
640
|
+
if (!url || typeof url !== "string") {
|
|
641
|
+
throw new MarvinValidationError("Webhook URL is required and must be a string");
|
|
642
|
+
}
|
|
643
|
+
let parsed;
|
|
644
|
+
try {
|
|
645
|
+
parsed = new URL(url);
|
|
646
|
+
} catch {
|
|
647
|
+
throw new MarvinValidationError("Invalid webhook URL format. Expected: https://example.com/webhook");
|
|
648
|
+
}
|
|
649
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
650
|
+
throw new MarvinValidationError(
|
|
651
|
+
`Webhook URL must use HTTP or HTTPS protocol. Got: ${parsed.protocol}`
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
655
|
+
const blockedHosts = [
|
|
656
|
+
"localhost",
|
|
657
|
+
"127.0.0.1",
|
|
658
|
+
"0.0.0.0",
|
|
659
|
+
"::1",
|
|
660
|
+
"169.254.169.254",
|
|
661
|
+
// AWS metadata service
|
|
662
|
+
"[::]"
|
|
663
|
+
];
|
|
664
|
+
if (blockedHosts.some((blocked) => hostname === blocked || hostname.includes(blocked))) {
|
|
665
|
+
throw new MarvinValidationError(
|
|
666
|
+
"Webhook URL cannot target localhost or internal network addresses"
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
670
|
+
const match = hostname.match(ipv4Regex);
|
|
671
|
+
if (match) {
|
|
672
|
+
const octets = match.slice(1).map(Number);
|
|
673
|
+
if (octets[0] === 10) {
|
|
674
|
+
throw new MarvinValidationError("Webhook URL cannot target private IP range (10.x.x.x)");
|
|
675
|
+
}
|
|
676
|
+
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {
|
|
677
|
+
throw new MarvinValidationError("Webhook URL cannot target private IP range (172.16-31.x.x)");
|
|
678
|
+
}
|
|
679
|
+
if (octets[0] === 192 && octets[1] === 168) {
|
|
680
|
+
throw new MarvinValidationError("Webhook URL cannot target private IP range (192.168.x.x)");
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (url.length > 2048) {
|
|
684
|
+
throw new MarvinValidationError("Webhook URL exceeds maximum length of 2048 characters");
|
|
685
|
+
}
|
|
686
|
+
}
|
|
531
687
|
/**
|
|
532
688
|
* List all webhooks for the current workspace
|
|
533
689
|
*/
|
|
@@ -539,31 +695,43 @@ var WebhooksModule = class {
|
|
|
539
695
|
* Get a webhook by ID
|
|
540
696
|
*/
|
|
541
697
|
async get(id) {
|
|
542
|
-
|
|
698
|
+
const validId = this.http.validatePathParam(id, "webhook ID");
|
|
699
|
+
return this.http.get(`/api/groups/webhooks/${validId}`);
|
|
543
700
|
}
|
|
544
701
|
/**
|
|
545
702
|
* Create a new webhook
|
|
703
|
+
* Validates URL to prevent SSRF attacks
|
|
546
704
|
*/
|
|
547
705
|
async create(data) {
|
|
706
|
+
if (data.url) {
|
|
707
|
+
this.validateWebhookUrl(data.url);
|
|
708
|
+
}
|
|
548
709
|
return this.http.post("/api/groups/webhooks", data);
|
|
549
710
|
}
|
|
550
711
|
/**
|
|
551
712
|
* Update a webhook
|
|
713
|
+
* Validates URL if provided to prevent SSRF attacks
|
|
552
714
|
*/
|
|
553
715
|
async update(id, data) {
|
|
554
|
-
|
|
716
|
+
const validId = this.http.validatePathParam(id, "webhook ID");
|
|
717
|
+
if (data.url) {
|
|
718
|
+
this.validateWebhookUrl(data.url);
|
|
719
|
+
}
|
|
720
|
+
return this.http.put(`/api/groups/webhooks/${validId}`, data);
|
|
555
721
|
}
|
|
556
722
|
/**
|
|
557
723
|
* Delete a webhook
|
|
558
724
|
*/
|
|
559
725
|
async delete(id) {
|
|
560
|
-
|
|
726
|
+
const validId = this.http.validatePathParam(id, "webhook ID");
|
|
727
|
+
await this.http.delete(`/api/groups/webhooks/${validId}`);
|
|
561
728
|
}
|
|
562
729
|
/**
|
|
563
730
|
* Test a webhook
|
|
564
731
|
*/
|
|
565
732
|
async test(id) {
|
|
566
|
-
|
|
733
|
+
const validId = this.http.validatePathParam(id, "webhook ID");
|
|
734
|
+
return this.http.post(`/api/groups/webhooks/${validId}/test`, {});
|
|
567
735
|
}
|
|
568
736
|
/**
|
|
569
737
|
* Rerun failed webhooks
|
|
@@ -655,6 +823,7 @@ var AdminSystemModule = class {
|
|
|
655
823
|
}
|
|
656
824
|
/**
|
|
657
825
|
* Update email settings
|
|
826
|
+
* Note: Password field is write-only and will not be returned in response
|
|
658
827
|
*/
|
|
659
828
|
async updateEmailSettings(data) {
|
|
660
829
|
return this.http.put("/api/admin/email", data);
|
|
@@ -1005,7 +1174,8 @@ var ScheduledTasksModule = class {
|
|
|
1005
1174
|
* Get a scheduled task by ID or slug
|
|
1006
1175
|
*/
|
|
1007
1176
|
async get(idOrSlug) {
|
|
1008
|
-
|
|
1177
|
+
const validIdOrSlug = this.http.validatePathParam(idOrSlug, "idOrSlug");
|
|
1178
|
+
return this.http.get(`/api/platform/scheduled-tasks/${validIdOrSlug}`);
|
|
1009
1179
|
}
|
|
1010
1180
|
/**
|
|
1011
1181
|
* Create a new scheduled task
|
|
@@ -1017,7 +1187,8 @@ var ScheduledTasksModule = class {
|
|
|
1017
1187
|
* Update a scheduled task
|
|
1018
1188
|
*/
|
|
1019
1189
|
async update(idOrSlug, data) {
|
|
1020
|
-
|
|
1190
|
+
const validIdOrSlug = this.http.validatePathParam(idOrSlug, "idOrSlug");
|
|
1191
|
+
return this.http.patch(`/api/platform/scheduled-tasks/${validIdOrSlug}`, data);
|
|
1021
1192
|
}
|
|
1022
1193
|
/**
|
|
1023
1194
|
* Delete a scheduled task
|
|
@@ -1058,11 +1229,40 @@ var ScheduledTasksModule = class {
|
|
|
1058
1229
|
};
|
|
1059
1230
|
|
|
1060
1231
|
// src/platform/forms.ts
|
|
1232
|
+
init_core();
|
|
1061
1233
|
var FormsModule = class {
|
|
1062
1234
|
constructor(http) {
|
|
1063
1235
|
this.http = http;
|
|
1064
1236
|
}
|
|
1065
1237
|
http;
|
|
1238
|
+
/**
|
|
1239
|
+
* Validate form submission data
|
|
1240
|
+
* - Prevents XSS by checking for script tags
|
|
1241
|
+
* - Validates data is a proper object
|
|
1242
|
+
* - Checks for reasonable size limits
|
|
1243
|
+
*/
|
|
1244
|
+
validateFormData(data) {
|
|
1245
|
+
if (!data || typeof data !== "object") {
|
|
1246
|
+
throw new MarvinValidationError("Form data must be a valid object");
|
|
1247
|
+
}
|
|
1248
|
+
const jsonString = JSON.stringify(data);
|
|
1249
|
+
const sizeInBytes = new Blob([jsonString]).size;
|
|
1250
|
+
if (sizeInBytes > 1024 * 1024) {
|
|
1251
|
+
throw new MarvinValidationError("Form data exceeds maximum size of 1MB");
|
|
1252
|
+
}
|
|
1253
|
+
const checkForScripts = (value) => {
|
|
1254
|
+
if (typeof value === "string") {
|
|
1255
|
+
if (/<script[\s\S]*?>[\s\S]*?<\/script>/gi.test(value)) {
|
|
1256
|
+
throw new MarvinValidationError("Form data contains potentially malicious content");
|
|
1257
|
+
}
|
|
1258
|
+
} else if (Array.isArray(value)) {
|
|
1259
|
+
value.forEach(checkForScripts);
|
|
1260
|
+
} else if (value && typeof value === "object") {
|
|
1261
|
+
Object.values(value).forEach(checkForScripts);
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
Object.values(data).forEach(checkForScripts);
|
|
1265
|
+
}
|
|
1066
1266
|
/**
|
|
1067
1267
|
* List all forms
|
|
1068
1268
|
*/
|
|
@@ -1073,7 +1273,8 @@ var FormsModule = class {
|
|
|
1073
1273
|
* Get a single form by ID
|
|
1074
1274
|
*/
|
|
1075
1275
|
async get(id) {
|
|
1076
|
-
|
|
1276
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
1277
|
+
return this.http.get(`/api/platform/forms/${validId}`);
|
|
1077
1278
|
}
|
|
1078
1279
|
/**
|
|
1079
1280
|
* Create a new form
|
|
@@ -1085,7 +1286,8 @@ var FormsModule = class {
|
|
|
1085
1286
|
* Update a form
|
|
1086
1287
|
*/
|
|
1087
1288
|
async update(id, data) {
|
|
1088
|
-
|
|
1289
|
+
const validId = this.http.validatePathParam(id, "id");
|
|
1290
|
+
return this.http.patch(`/api/platform/forms/${validId}`, data);
|
|
1089
1291
|
}
|
|
1090
1292
|
/**
|
|
1091
1293
|
* Delete a form
|
|
@@ -1107,15 +1309,25 @@ var FormsModule = class {
|
|
|
1107
1309
|
}
|
|
1108
1310
|
/**
|
|
1109
1311
|
* Submit data to a published form (Publishing API - public access)
|
|
1312
|
+
* Validates data before submission to prevent XSS and malicious content
|
|
1110
1313
|
*/
|
|
1111
1314
|
async submitForm(slug, data) {
|
|
1112
|
-
|
|
1315
|
+
const validSlug = this.http.validatePathParam(slug, "form slug");
|
|
1316
|
+
this.validateFormData(data);
|
|
1317
|
+
return this.http.post(`/api/publish/forms/${validSlug}/submit`, {
|
|
1113
1318
|
dataJson: data
|
|
1114
1319
|
});
|
|
1115
1320
|
}
|
|
1116
1321
|
};
|
|
1117
1322
|
|
|
1118
1323
|
// src/platform/client.ts
|
|
1324
|
+
function createPlatformConfigFromEnv(overrides) {
|
|
1325
|
+
return {
|
|
1326
|
+
apiUrl: overrides?.apiUrl || process.env.MARVIN_API_URL || "http://localhost:8080",
|
|
1327
|
+
userToken: overrides?.userToken || process.env.MARVIN_USER_TOKEN,
|
|
1328
|
+
credentials: overrides?.credentials || "include"
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1119
1331
|
var PlatformClient = class extends HttpClient {
|
|
1120
1332
|
entries;
|
|
1121
1333
|
collections;
|
|
@@ -1142,7 +1354,7 @@ var PlatformClient = class extends HttpClient {
|
|
|
1142
1354
|
adminMaintenance;
|
|
1143
1355
|
constructor(config) {
|
|
1144
1356
|
const httpConfig = {
|
|
1145
|
-
baseUrl: config.apiUrl,
|
|
1357
|
+
baseUrl: config.apiUrl || "http://localhost:8080",
|
|
1146
1358
|
auth: new SessionAuth(config.userToken),
|
|
1147
1359
|
credentials: config.credentials || "include"
|
|
1148
1360
|
};
|
|
@@ -1172,7 +1384,8 @@ var PlatformClient = class extends HttpClient {
|
|
|
1172
1384
|
}
|
|
1173
1385
|
};
|
|
1174
1386
|
function createPlatformClient(config) {
|
|
1175
|
-
|
|
1387
|
+
const finalConfig = createPlatformConfigFromEnv(config);
|
|
1388
|
+
return new PlatformClient(finalConfig);
|
|
1176
1389
|
}
|
|
1177
1390
|
export {
|
|
1178
1391
|
APIClientsModule,
|
|
@@ -1198,5 +1411,6 @@ export {
|
|
|
1198
1411
|
WebhooksModule,
|
|
1199
1412
|
WorkspaceMembersModule,
|
|
1200
1413
|
WorkspacesModule,
|
|
1201
|
-
createPlatformClient
|
|
1414
|
+
createPlatformClient,
|
|
1415
|
+
createPlatformConfigFromEnv
|
|
1202
1416
|
};
|