@marvalt/wadapter 2.3.6 → 2.3.9

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.
@@ -0,0 +1,792 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+
6
+ /**
7
+ * @license GPL-3.0-or-later
8
+ *
9
+ * This file is part of the MarVAlt Open SDK.
10
+ * Copyright (c) 2025 Vibune Pty Ltd.
11
+ *
12
+ * This program is free software: you can redistribute it and/or modify
13
+ * it under the terms of the GNU General Public License as published by
14
+ * the Free Software Foundation, either version 3 of the License, or
15
+ * (at your option) any later version.
16
+ *
17
+ * This program is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
+ * See the GNU General Public License for more details.
21
+ */
22
+ class WordPressClient {
23
+ constructor(config) {
24
+ this.config = config;
25
+ }
26
+ async makeRequest(endpoint, params = {}, options = {}) {
27
+ const baseUrl = this.getBaseUrl();
28
+ const queryString = new URLSearchParams();
29
+ Object.entries(params).forEach(([key, value]) => {
30
+ if (value !== undefined && value !== null) {
31
+ if (Array.isArray(value)) {
32
+ value.forEach(v => queryString.append(key, v.toString()));
33
+ }
34
+ else {
35
+ queryString.append(key, value.toString());
36
+ }
37
+ }
38
+ });
39
+ // Construct URL based on auth mode
40
+ let url;
41
+ if (this.config.authMode === 'cloudflare_proxy') {
42
+ // For proxy modes, pass the full REST path including /wp-json as query parameter
43
+ const fullEndpoint = `/wp-json${endpoint}?${queryString.toString()}`;
44
+ url = `${baseUrl}?endpoint=${encodeURIComponent(fullEndpoint)}`;
45
+ }
46
+ else {
47
+ // For direct mode, construct the full WordPress API URL
48
+ url = `${baseUrl}/wp-json${endpoint}?${queryString.toString()}`;
49
+ }
50
+ const headers = {
51
+ 'Content-Type': 'application/json',
52
+ };
53
+ // Add authentication based on mode
54
+ if (this.config.authMode === 'cloudflare_proxy') ;
55
+ else {
56
+ // Direct mode - use basic auth
57
+ if (this.config.username && this.config.password) {
58
+ const credentials = btoa(`${this.config.username}:${this.config.password}`);
59
+ headers.Authorization = `Basic ${credentials}`;
60
+ }
61
+ }
62
+ console.log('🔍 WordPress API Request Debug:', {
63
+ url,
64
+ method: options.method || 'GET',
65
+ authMode: this.config.authMode,
66
+ headers: Object.keys(headers),
67
+ });
68
+ const response = await fetch(url, {
69
+ method: options.method || 'GET',
70
+ headers,
71
+ body: options.body,
72
+ signal: AbortSignal.timeout(this.config.timeout || 30000),
73
+ ...options,
74
+ });
75
+ if (!response.ok) {
76
+ console.error('❌ WordPress API Error:', {
77
+ status: response.status,
78
+ statusText: response.statusText,
79
+ url,
80
+ headers: Object.fromEntries(response.headers.entries()),
81
+ });
82
+ throw new Error(`WordPress API request failed: ${response.status} ${response.statusText}`);
83
+ }
84
+ const responseText = await response.text();
85
+ console.log('🔍 WordPress API Response:', {
86
+ status: response.status,
87
+ responseLength: responseText.length,
88
+ responsePreview: responseText.substring(0, 200),
89
+ });
90
+ if (!responseText) {
91
+ // Return empty array for empty responses (common for endpoints with no data)
92
+ return [];
93
+ }
94
+ try {
95
+ return JSON.parse(responseText);
96
+ }
97
+ catch (error) {
98
+ const message = error instanceof Error ? error.message : String(error);
99
+ console.error('❌ JSON Parse Error:', {
100
+ error: message,
101
+ responseText: responseText.substring(0, 500),
102
+ });
103
+ throw new Error(`Failed to parse JSON response: ${message}`);
104
+ }
105
+ }
106
+ getBaseUrl() {
107
+ switch (this.config.authMode) {
108
+ case 'cloudflare_proxy':
109
+ return this.config.cloudflareWorkerUrl || '';
110
+ default:
111
+ return this.config.apiUrl || '';
112
+ }
113
+ }
114
+ async getPosts(params = {}) {
115
+ return this.makeRequest('/wp/v2/posts', params);
116
+ }
117
+ async getPost(id) {
118
+ return this.makeRequest(`/wp/v2/posts/${id}`);
119
+ }
120
+ async getPages(params = {}) {
121
+ return this.makeRequest('/wp/v2/pages', params);
122
+ }
123
+ async getPage(id) {
124
+ return this.makeRequest(`/wp/v2/pages/${id}`);
125
+ }
126
+ async getMedia(params = {}) {
127
+ return this.makeRequest('/wp/v2/media', params);
128
+ }
129
+ async getMediaItem(id) {
130
+ return this.makeRequest(`/wp/v2/media/${id}`);
131
+ }
132
+ async getCategories(params = {}) {
133
+ return this.makeRequest('/wp/v2/categories', params);
134
+ }
135
+ async getTags(params = {}) {
136
+ return this.makeRequest('/wp/v2/tags', params);
137
+ }
138
+ // Gravity Forms endpoints
139
+ async getGravityForms() {
140
+ return this.makeRequest('/gf-api/v1/forms');
141
+ }
142
+ async getGravityForm(formId) {
143
+ return this.makeRequest(`/gf-api/v1/forms/${formId}/config`);
144
+ }
145
+ async submitGravityForm(formId, entry) {
146
+ return this.makeRequest(`/gf-api/v1/forms/${formId}/submit`, {}, {
147
+ method: 'POST',
148
+ body: JSON.stringify(entry),
149
+ });
150
+ }
151
+ // Static data endpoints
152
+ async getStaticDataNonce() {
153
+ return this.makeRequest('/static-data/v1/nonce');
154
+ }
155
+ async getAllData(params = {}) {
156
+ const [posts, pages, media, categories, tags] = await Promise.all([
157
+ this.getPosts(params),
158
+ this.getPages(params),
159
+ this.getMedia(params),
160
+ this.getCategories(params),
161
+ this.getTags(params),
162
+ ]);
163
+ return { posts, pages, media, categories, tags };
164
+ }
165
+ }
166
+
167
+ let document;
168
+ let offset;
169
+ let output;
170
+ let stack;
171
+ const tokenizer = /<!--\s+(\/)?wp:([a-z][a-z0-9_-]*\/)?([a-z][a-z0-9_-]*)\s+({(?:(?=([^}]+|}+(?=})|(?!}\s+\/?-->)[^])*)\5|[^]*?)}\s+)?(\/)?-->/g;
172
+ function Block(blockName, attrs, innerBlocks, innerHTML, innerContent) {
173
+ return {
174
+ blockName,
175
+ attrs,
176
+ innerBlocks,
177
+ innerHTML,
178
+ innerContent
179
+ };
180
+ }
181
+ function Freeform(innerHTML) {
182
+ return Block(null, {}, [], innerHTML, [innerHTML]);
183
+ }
184
+ function Frame(block, tokenStart, tokenLength, prevOffset, leadingHtmlStart) {
185
+ return {
186
+ block,
187
+ tokenStart,
188
+ tokenLength,
189
+ prevOffset: prevOffset || tokenStart + tokenLength,
190
+ leadingHtmlStart
191
+ };
192
+ }
193
+ const parse = (doc) => {
194
+ document = doc;
195
+ offset = 0;
196
+ output = [];
197
+ stack = [];
198
+ tokenizer.lastIndex = 0;
199
+ do {
200
+ } while (proceed());
201
+ return output;
202
+ };
203
+ function proceed() {
204
+ const stackDepth = stack.length;
205
+ const next = nextToken();
206
+ const [tokenType, blockName, attrs, startOffset, tokenLength] = next;
207
+ const leadingHtmlStart = startOffset > offset ? offset : null;
208
+ switch (tokenType) {
209
+ case "no-more-tokens":
210
+ if (0 === stackDepth) {
211
+ addFreeform();
212
+ return false;
213
+ }
214
+ if (1 === stackDepth) {
215
+ addBlockFromStack();
216
+ return false;
217
+ }
218
+ while (0 < stack.length) {
219
+ addBlockFromStack();
220
+ }
221
+ return false;
222
+ case "void-block":
223
+ if (0 === stackDepth) {
224
+ if (null !== leadingHtmlStart) {
225
+ output.push(
226
+ Freeform(
227
+ document.substr(
228
+ leadingHtmlStart,
229
+ startOffset - leadingHtmlStart
230
+ )
231
+ )
232
+ );
233
+ }
234
+ output.push(Block(blockName, attrs, [], "", []));
235
+ offset = startOffset + tokenLength;
236
+ return true;
237
+ }
238
+ addInnerBlock(
239
+ Block(blockName, attrs, [], "", []),
240
+ startOffset,
241
+ tokenLength
242
+ );
243
+ offset = startOffset + tokenLength;
244
+ return true;
245
+ case "block-opener":
246
+ stack.push(
247
+ Frame(
248
+ Block(blockName, attrs, [], "", []),
249
+ startOffset,
250
+ tokenLength,
251
+ startOffset + tokenLength,
252
+ leadingHtmlStart
253
+ )
254
+ );
255
+ offset = startOffset + tokenLength;
256
+ return true;
257
+ case "block-closer":
258
+ if (0 === stackDepth) {
259
+ addFreeform();
260
+ return false;
261
+ }
262
+ if (1 === stackDepth) {
263
+ addBlockFromStack(startOffset);
264
+ offset = startOffset + tokenLength;
265
+ return true;
266
+ }
267
+ const stackTop = stack.pop();
268
+ const html = document.substr(
269
+ stackTop.prevOffset,
270
+ startOffset - stackTop.prevOffset
271
+ );
272
+ stackTop.block.innerHTML += html;
273
+ stackTop.block.innerContent.push(html);
274
+ stackTop.prevOffset = startOffset + tokenLength;
275
+ addInnerBlock(
276
+ stackTop.block,
277
+ stackTop.tokenStart,
278
+ stackTop.tokenLength,
279
+ startOffset + tokenLength
280
+ );
281
+ offset = startOffset + tokenLength;
282
+ return true;
283
+ default:
284
+ addFreeform();
285
+ return false;
286
+ }
287
+ }
288
+ function parseJSON(input) {
289
+ try {
290
+ return JSON.parse(input);
291
+ } catch (e) {
292
+ return null;
293
+ }
294
+ }
295
+ function nextToken() {
296
+ const matches = tokenizer.exec(document);
297
+ if (null === matches) {
298
+ return ["no-more-tokens", "", null, 0, 0];
299
+ }
300
+ const startedAt = matches.index;
301
+ const [
302
+ match,
303
+ closerMatch,
304
+ namespaceMatch,
305
+ nameMatch,
306
+ attrsMatch,
307
+ ,
308
+ voidMatch
309
+ ] = matches;
310
+ const length = match.length;
311
+ const isCloser = !!closerMatch;
312
+ const isVoid = !!voidMatch;
313
+ const namespace = namespaceMatch || "core/";
314
+ const name = namespace + nameMatch;
315
+ const hasAttrs = !!attrsMatch;
316
+ const attrs = hasAttrs ? parseJSON(attrsMatch) : {};
317
+ if (isVoid) {
318
+ return ["void-block", name, attrs, startedAt, length];
319
+ }
320
+ if (isCloser) {
321
+ return ["block-closer", name, null, startedAt, length];
322
+ }
323
+ return ["block-opener", name, attrs, startedAt, length];
324
+ }
325
+ function addFreeform(rawLength) {
326
+ const length = document.length - offset;
327
+ if (0 === length) {
328
+ return;
329
+ }
330
+ output.push(Freeform(document.substr(offset, length)));
331
+ }
332
+ function addInnerBlock(block, tokenStart, tokenLength, lastOffset) {
333
+ const parent = stack[stack.length - 1];
334
+ parent.block.innerBlocks.push(block);
335
+ const html = document.substr(
336
+ parent.prevOffset,
337
+ tokenStart - parent.prevOffset
338
+ );
339
+ if (html) {
340
+ parent.block.innerHTML += html;
341
+ parent.block.innerContent.push(html);
342
+ }
343
+ parent.block.innerContent.push(null);
344
+ parent.prevOffset = lastOffset ? lastOffset : tokenStart + tokenLength;
345
+ }
346
+ function addBlockFromStack(endOffset) {
347
+ const { block, leadingHtmlStart, prevOffset, tokenStart } = stack.pop();
348
+ const html = endOffset ? document.substr(prevOffset, endOffset - prevOffset) : document.substr(prevOffset);
349
+ if (html) {
350
+ block.innerHTML += html;
351
+ block.innerContent.push(html);
352
+ }
353
+ if (null !== leadingHtmlStart) {
354
+ output.push(
355
+ Freeform(
356
+ document.substr(
357
+ leadingHtmlStart,
358
+ tokenStart - leadingHtmlStart
359
+ )
360
+ )
361
+ );
362
+ }
363
+ output.push(block);
364
+ }
365
+
366
+ /**
367
+ * @license GPL-3.0-or-later
368
+ *
369
+ * This file is part of the MarVAlt Open SDK.
370
+ * Copyright (c) 2025 Vibune Pty Ltd.
371
+ *
372
+ * This program is free software: you can redistribute it and/or modify
373
+ * it under the terms of the GNU General Public License as published by
374
+ * the Free Software Foundation, either version 3 of the License, or
375
+ * (at your option) any later version.
376
+ *
377
+ * This program is distributed in the hope that it will be useful,
378
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
379
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
380
+ * See the GNU General Public License for more details.
381
+ */
382
+ // WordPress Static Data Generator
383
+ class WordPressGenerator {
384
+ constructor(config) {
385
+ this.config = config;
386
+ }
387
+ async generateStaticData() {
388
+ const client = new WordPressClient(this.config);
389
+ console.log('🚀 Starting WordPress static data generation...');
390
+ const frontendId = this.config.frontendId || 'default-frontend';
391
+ const frontendName = this.config.frontendName || 'Default Frontend';
392
+ const postTypes = this.config.postTypes || ['posts', 'pages', 'case_studies', 'projects', 'members', 'testimonial', 'faqs', 'products', 'services', 'events'];
393
+ // Use 'edit' context only when credentials are provided; otherwise fall back to 'view'
394
+ const hasBasicAuth = !!(this.config.username && this.config.password);
395
+ const data = await client.getAllData({
396
+ per_page: this.config.maxItems || 100,
397
+ _embed: this.config.includeEmbedded || false,
398
+ context: hasBasicAuth ? 'edit' : 'view',
399
+ });
400
+ // Create the static data structure matching the existing format
401
+ const staticData = {
402
+ generated_at: new Date().toISOString(),
403
+ frontend_id: frontendId,
404
+ frontend_name: frontendName,
405
+ config: {
406
+ frontend_id: frontendId,
407
+ frontend_name: frontendName,
408
+ post_types: postTypes.reduce((acc, postType) => {
409
+ acc[postType] = {
410
+ max_items: this.config.maxItems || 100,
411
+ include_embedded: this.config.includeEmbedded || true,
412
+ orderby: 'date',
413
+ order: 'desc',
414
+ };
415
+ return acc;
416
+ }, {}),
417
+ },
418
+ // Map the data to the expected structure
419
+ posts: data.posts,
420
+ pages: (data.pages || []).map((p) => {
421
+ let blocks = [];
422
+ try {
423
+ const raw = p?.content?.raw || '';
424
+ blocks = raw ? parse(raw) : [];
425
+ }
426
+ catch (e) {
427
+ console.warn('⚠️ Failed to parse Gutenberg blocks for page', p?.id, e);
428
+ }
429
+ return { ...p, blocks };
430
+ }),
431
+ media: data.media,
432
+ categories: data.categories,
433
+ tags: data.tags,
434
+ };
435
+ // Write to file
436
+ this.writeStaticData(staticData);
437
+ console.log('✅ WordPress static data generation completed');
438
+ console.log(`📊 Generated data: ${data.posts.length} posts, ${data.pages.length} pages, ${data.media.length} media items`);
439
+ return staticData;
440
+ }
441
+ writeStaticData(data) {
442
+ try {
443
+ const outputPath = path.join(process.cwd(), this.config.outputPath);
444
+ fs.writeFileSync(outputPath, JSON.stringify(data, null, 2));
445
+ console.log(`📁 Static data written to: ${outputPath}`);
446
+ }
447
+ catch (error) {
448
+ console.error('❌ Error writing static data:', error);
449
+ throw error;
450
+ }
451
+ }
452
+ async generatePostsOnly() {
453
+ const client = new WordPressClient(this.config);
454
+ return client.getPosts({ per_page: 100 });
455
+ }
456
+ async generatePagesOnly() {
457
+ const client = new WordPressClient(this.config);
458
+ return client.getPages({ per_page: 100 });
459
+ }
460
+ async generateMediaOnly() {
461
+ const client = new WordPressClient(this.config);
462
+ return client.getMedia({ per_page: 100 });
463
+ }
464
+ }
465
+ // Convenience function for easy usage
466
+ async function generateWordPressData(config) {
467
+ const generator = new WordPressGenerator(config);
468
+ return generator.generateStaticData();
469
+ }
470
+
471
+ /**
472
+ * @license GPL-3.0-or-later
473
+ *
474
+ * This file is part of the MarVAlt Open SDK.
475
+ * Copyright (c) 2025 Vibune Pty Ltd.
476
+ *
477
+ * This program is free software: you can redistribute it and/or modify
478
+ * it under the terms of the GNU General Public License as published by
479
+ * the Free Software Foundation, either version 3 of the License, or
480
+ * (at your option) any later version.
481
+ *
482
+ * This program is distributed in the hope that it will be useful,
483
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
484
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
485
+ * See the GNU General Public License for more details.
486
+ */
487
+ class GravityFormsClient {
488
+ constructor(config) {
489
+ this.config = config;
490
+ // Determine mode: direct for generators (Node.js), proxy for browser
491
+ const hasCredentials = !!(config.username && config.password && config.username.length > 0 && config.password.length > 0);
492
+ this.useDirectMode = config.authMode === 'direct' && hasCredentials;
493
+ // Debug logging for Node.js environments (generators)
494
+ if (typeof process !== 'undefined' && process.env) {
495
+ console.log('🔍 GravityFormsClient Configuration:', {
496
+ authMode: config.authMode,
497
+ hasUsername: !!config.username,
498
+ hasPassword: !!config.password,
499
+ hasApiUrl: !!config.apiUrl,
500
+ useDirectMode: this.useDirectMode,
501
+ });
502
+ }
503
+ // Convention-based: Always use /api/gravity-forms-submit unless explicitly overridden
504
+ this.proxyEndpoint = config.proxyEndpoint || '/api/gravity-forms-submit';
505
+ }
506
+ async makeRequest(endpoint, options = {}) {
507
+ let url;
508
+ const headers = {
509
+ 'Content-Type': 'application/json',
510
+ ...options.headers,
511
+ };
512
+ if (this.useDirectMode) {
513
+ // Direct mode: Call WordPress API directly (for generators/Node.js)
514
+ url = `${this.config.apiUrl}${endpoint}`;
515
+ // Add Basic Auth
516
+ if (this.config.username && this.config.password) {
517
+ const credentials = btoa(`${this.config.username}:${this.config.password}`);
518
+ headers['Authorization'] = `Basic ${credentials}`;
519
+ }
520
+ // Add CF Access headers if provided
521
+ if (this.config.cfAccessClientId && this.config.cfAccessClientSecret) {
522
+ headers['CF-Access-Client-Id'] = this.config.cfAccessClientId;
523
+ headers['CF-Access-Client-Secret'] = this.config.cfAccessClientSecret;
524
+ }
525
+ }
526
+ else {
527
+ // Proxy mode: Use Pages Function (for browser)
528
+ const proxyEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
529
+ url = `${this.proxyEndpoint}?endpoint=${encodeURIComponent(proxyEndpoint)}`;
530
+ }
531
+ const response = await fetch(url, {
532
+ ...options,
533
+ headers,
534
+ signal: AbortSignal.timeout(this.config.timeout || 30000),
535
+ });
536
+ if (!response.ok) {
537
+ throw new Error(`Gravity Forms API request failed: ${response.status} ${response.statusText}`);
538
+ }
539
+ const responseText = await response.text();
540
+ if (!responseText) {
541
+ return [];
542
+ }
543
+ try {
544
+ return JSON.parse(responseText);
545
+ }
546
+ catch (error) {
547
+ console.error('❌ Gravity Forms JSON Parse Error:', {
548
+ error: error.message,
549
+ responseText: responseText.substring(0, 500),
550
+ });
551
+ throw new Error(`Failed to parse JSON response: ${error.message}`);
552
+ }
553
+ }
554
+ async getForm(id) {
555
+ // Always use custom gf-api/v1 endpoint (from custom plugin)
556
+ const endpoint = `/forms/${id}`;
557
+ return this.makeRequest(endpoint);
558
+ }
559
+ async getForms() {
560
+ // Always use custom gf-api/v1 endpoint (from custom plugin)
561
+ const endpoint = '/forms';
562
+ return this.makeRequest(endpoint);
563
+ }
564
+ async getFormConfig(id) {
565
+ // Always use custom gf-api/v1 endpoint (from custom plugin)
566
+ const endpoint = `/forms/${id}/config`;
567
+ return this.makeRequest(endpoint);
568
+ }
569
+ async submitForm(formId, submission) {
570
+ // Always use custom gf-api/v1 submit endpoint (from custom plugin)
571
+ const endpoint = `/forms/${formId}/submit`;
572
+ // Merge custom headers (e.g., Turnstile token) with default headers
573
+ const headers = {
574
+ 'Content-Type': 'application/json',
575
+ ...(submission.headers || {}),
576
+ };
577
+ return this.makeRequest(endpoint, {
578
+ method: 'POST',
579
+ headers,
580
+ body: JSON.stringify(submission.field_values),
581
+ });
582
+ }
583
+ async getHealth() {
584
+ const endpoint = '/health';
585
+ return this.makeRequest(endpoint);
586
+ }
587
+ }
588
+
589
+ /**
590
+ * @license GPL-3.0-or-later
591
+ *
592
+ * This file is part of the MarVAlt Open SDK.
593
+ * Copyright (c) 2025 Vibune Pty Ltd.
594
+ *
595
+ * This program is free software: you can redistribute it and/or modify
596
+ * it under the terms of the GNU General Public License as published by
597
+ * the Free Software Foundation, either version 3 of the License, or
598
+ * (at your option) any later version.
599
+ *
600
+ * This program is distributed in the hope that it will be useful,
601
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
602
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
603
+ * See the GNU General Public License for more details.
604
+ */
605
+ // Gravity Forms Static Data Generator
606
+ class GravityFormsGenerator {
607
+ constructor(config) {
608
+ this.config = config;
609
+ }
610
+ async generateStaticData() {
611
+ const client = new GravityFormsClient(this.config);
612
+ console.log('🚀 Starting Gravity Forms static data generation...');
613
+ let forms = [];
614
+ if (this.config.formIds && this.config.formIds.length > 0) {
615
+ // Generate specific forms
616
+ for (const formId of this.config.formIds) {
617
+ try {
618
+ const form = await client.getFormConfig(formId);
619
+ if (this.config.includeInactive || form.is_active) {
620
+ forms.push(form);
621
+ }
622
+ }
623
+ catch (error) {
624
+ console.warn(`⚠️ Could not fetch form ${formId}:`, error);
625
+ }
626
+ }
627
+ }
628
+ else {
629
+ // Generate all forms
630
+ const formsList = await client.getForms();
631
+ console.log('🔍 Gravity Forms Response:', {
632
+ formsType: typeof formsList,
633
+ isArray: Array.isArray(formsList),
634
+ formsLength: formsList?.length,
635
+ formsPreview: formsList ? JSON.stringify(formsList).substring(0, 200) : 'null',
636
+ });
637
+ let formsMetadata = [];
638
+ if (Array.isArray(formsList)) {
639
+ formsMetadata = formsList;
640
+ }
641
+ else if (formsList && typeof formsList === 'object' && Array.isArray(formsList.forms)) {
642
+ formsMetadata = formsList.forms;
643
+ }
644
+ else if (formsList) {
645
+ formsMetadata = [formsList];
646
+ }
647
+ if (!this.config.includeInactive) {
648
+ formsMetadata = formsMetadata.filter(form => form.is_active === '1' || form.is_active === true);
649
+ }
650
+ // Fetch full form configuration for each form (schema lives under /config)
651
+ console.log(`🔄 Fetching full data for ${formsMetadata.length} forms...`);
652
+ forms = [];
653
+ for (const formMetadata of formsMetadata) {
654
+ try {
655
+ const formId = Number(formMetadata.id);
656
+ const fullForm = await client.getFormConfig(formId);
657
+ forms.push(fullForm);
658
+ console.log(`✅ Fetched form: ${fullForm.title} (${fullForm.fields?.length || 0} fields)`);
659
+ }
660
+ catch (error) {
661
+ console.error(`❌ Failed to fetch form ${formMetadata.id} config:`, error);
662
+ throw error;
663
+ }
664
+ }
665
+ }
666
+ const staticData = {
667
+ generated_at: new Date().toISOString(),
668
+ total_forms: forms.length,
669
+ forms,
670
+ };
671
+ // Write to file
672
+ this.writeStaticData(staticData);
673
+ console.log('✅ Gravity Forms static data generation completed');
674
+ console.log(`📊 Generated data: ${forms.length} forms`);
675
+ return staticData;
676
+ }
677
+ writeStaticData(data) {
678
+ try {
679
+ const outputPath = path.join(process.cwd(), this.config.outputPath);
680
+ fs.writeFileSync(outputPath, JSON.stringify(data, null, 2));
681
+ console.log(`📁 Static data written to: ${outputPath}`);
682
+ }
683
+ catch (error) {
684
+ console.error('❌ Error writing static data:', error);
685
+ throw error;
686
+ }
687
+ }
688
+ async generateFormConfig(formId) {
689
+ const client = new GravityFormsClient(this.config);
690
+ return client.getFormConfig(formId);
691
+ }
692
+ }
693
+ // Convenience function for easy usage
694
+ async function generateGravityFormsData(config) {
695
+ const generator = new GravityFormsGenerator(config);
696
+ return generator.generateStaticData();
697
+ }
698
+
699
+ /**
700
+ * @license GPL-3.0-or-later
701
+ *
702
+ * This file is part of the MarVAlt Open SDK.
703
+ * Copyright (c) 2025 Vibune Pty Ltd.
704
+ *
705
+ * This program is free software: you can redistribute it and/or modify
706
+ * it under the terms of the GNU General Public License as published by
707
+ * the Free Software Foundation, either version 3 of the License, or
708
+ * (at your option) any later version.
709
+ *
710
+ * This program is distributed in the hope that it will be useful,
711
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
712
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
713
+ * See the GNU General Public License for more details.
714
+ */
715
+ class FormProtectionGenerator {
716
+ constructor(config) {
717
+ this.config = config;
718
+ }
719
+ async generateStaticData() {
720
+ console.log('🚀 Starting Form Protection static data generation...');
721
+ const forms = this.config.forms.map(form => ({
722
+ id: form.id,
723
+ name: form.name,
724
+ protectionLevel: form.protectionLevel,
725
+ emailVerification: this.config.emailVerification,
726
+ spamProtection: this.config.spamProtection,
727
+ rateLimiting: this.config.rateLimiting,
728
+ }));
729
+ const staticData = {
730
+ config: {
731
+ enabled: this.config.enabled,
732
+ secret: this.config.secret,
733
+ emailVerification: this.config.emailVerification,
734
+ spamProtection: this.config.spamProtection,
735
+ rateLimiting: this.config.rateLimiting,
736
+ maxSubmissionsPerHour: this.config.maxSubmissionsPerHour,
737
+ maxSubmissionsPerDay: this.config.maxSubmissionsPerDay,
738
+ },
739
+ forms,
740
+ generatedAt: new Date().toISOString(),
741
+ };
742
+ // Write to file
743
+ this.writeStaticData(staticData);
744
+ console.log('✅ Form Protection static data generation completed');
745
+ console.log(`📊 Generated data: ${forms.length} protected forms`);
746
+ return staticData;
747
+ }
748
+ writeStaticData(data) {
749
+ try {
750
+ const outputPath = path.join(process.cwd(), this.config.outputPath);
751
+ fs.writeFileSync(outputPath, JSON.stringify(data, null, 2));
752
+ console.log(`📁 Static data written to: ${outputPath}`);
753
+ }
754
+ catch (error) {
755
+ console.error('❌ Error writing static data:', error);
756
+ throw error;
757
+ }
758
+ }
759
+ validateFormProtection(formData) {
760
+ // Basic validation logic for form protection
761
+ const errors = [];
762
+ if (this.config.emailVerification && !formData.email) {
763
+ errors.push('Email verification is required');
764
+ }
765
+ if (this.config.spamProtection) {
766
+ // Basic spam detection logic
767
+ if (formData.message && formData.message.length < 10) {
768
+ errors.push('Message too short');
769
+ }
770
+ }
771
+ return {
772
+ success: errors.length === 0,
773
+ protected: this.config.enabled,
774
+ message: errors.length > 0 ? errors.join(', ') : 'Form is protected',
775
+ verificationRequired: this.config.emailVerification,
776
+ spamDetected: errors.length > 0,
777
+ };
778
+ }
779
+ }
780
+ // Convenience function for easy usage
781
+ async function generateFormProtectionData(config) {
782
+ const generator = new FormProtectionGenerator(config);
783
+ return generator.generateStaticData();
784
+ }
785
+
786
+ exports.FormProtectionGenerator = FormProtectionGenerator;
787
+ exports.GravityFormsGenerator = GravityFormsGenerator;
788
+ exports.WordPressGenerator = WordPressGenerator;
789
+ exports.generateFormProtectionData = generateFormProtectionData;
790
+ exports.generateGravityFormsData = generateGravityFormsData;
791
+ exports.generateWordPressData = generateWordPressData;
792
+ //# sourceMappingURL=generators.cjs.map