@marvalt/madapter 1.0.14 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var reactQuery = require('@tanstack/react-query');
4
3
  var fs = require('fs');
5
4
  var path = require('path');
6
5
  var React = require('react');
6
+ var reactQuery = require('@tanstack/react-query');
7
7
 
8
8
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
9
9
  /**
@@ -25,68 +25,16 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
25
25
  class MauticClient {
26
26
  constructor(config) {
27
27
  this.config = config;
28
- if (config.authMode === 'cloudflare_proxy' && config.cloudflareWorkerUrl && config.appId && config.workerSecret) {
29
- // Cloudflare Worker proxy (recommended - uses Secrets Store)
30
- this.proxyUrl = config.cloudflareWorkerUrl;
31
- this.appId = config.appId;
32
- this.workerSecret = config.workerSecret;
33
- this.useProxy = true;
34
- this.isConfigured = true;
35
- }
36
- else if (config.authMode === 'direct' && config.apiUrl) {
37
- // Direct Mautic API access (legacy - for local development only)
38
- this.baseUrl = config.apiUrl;
39
- this.useProxy = false;
40
- this.isConfigured = true;
41
- }
42
- else {
43
- // Fallback or error state
44
- this.useProxy = false;
45
- this.isConfigured = false;
46
- }
47
- }
48
- validateConfiguration() {
49
- if (!this.isConfigured) {
50
- throw new Error('Mautic service not properly configured. Check your configuration.');
51
- }
52
- }
53
- getAuthHeaders() {
54
- if (this.useProxy) {
55
- return {
56
- 'x-app-id': this.appId || '',
57
- 'x-app-secret': this.workerSecret || '',
58
- };
59
- }
60
- else {
61
- return {};
62
- }
28
+ // Convention-based: Always use /api/mautic-submit unless explicitly overridden
29
+ this.proxyEndpoint = config.proxyEndpoint || '/api/mautic-submit';
63
30
  }
64
31
  async makeRequest(endpoint, options = {}) {
65
- this.validateConfiguration();
66
- let url;
67
- if (this.useProxy) {
68
- // Use Cloudflare Worker proxy
69
- const proxyEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
70
- url = `${this.proxyUrl}?endpoint=${encodeURIComponent(proxyEndpoint)}`;
71
- }
72
- else {
73
- // Use direct Mautic API
74
- // Determine if this is a form submission endpoint (should not use /api prefix)
75
- const isFormSubmission = endpoint.startsWith('/form/submit') ||
76
- (endpoint.startsWith('/forms/') && endpoint.includes('/submit'));
77
- if (isFormSubmission) {
78
- // Form submissions go directly to the form endpoint, not the API
79
- url = `${this.baseUrl}${endpoint}`;
80
- }
81
- else {
82
- // API calls use the /api prefix
83
- url = `${this.baseUrl}/api${endpoint}`;
84
- }
85
- }
32
+ // Always use proxy endpoint (convention-based)
33
+ const proxyEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
34
+ const url = `${this.proxyEndpoint}?endpoint=${encodeURIComponent(proxyEndpoint)}`;
86
35
  const isFormSubmission = endpoint.startsWith('/form/submit') ||
87
36
  (endpoint.startsWith('/forms/') && endpoint.includes('/submit'));
88
37
  const headers = {
89
- ...this.getAuthHeaders(),
90
38
  ...options.headers,
91
39
  };
92
40
  // Default JSON only for non-form submissions when a body exists and no explicit content-type provided
@@ -290,6 +238,7 @@ class MauticClient {
290
238
  */
291
239
  /**
292
240
  * Create Mautic configuration from environment variables
241
+ * Convention-based: Uses /api/mautic-submit by default
293
242
  */
294
243
  const createMauticConfig = (overrides = {}) => {
295
244
  // Use import.meta.env for Vite compatibility in browser
@@ -316,32 +265,12 @@ const createMauticConfig = (overrides = {}) => {
316
265
  return undefined;
317
266
  };
318
267
  const merged = {
319
- authMode: getEnvVar('VITE_AUTH_MODE') || getEnvVar('VITE_MAUTIC_AUTH_MODE') || 'cloudflare_proxy',
320
268
  apiUrl: getEnvVar('VITE_MAUTIC_URL'),
321
- cloudflareWorkerUrl: getEnvVar('VITE_MAUTIC_PROXY_URL'),
322
- appId: getEnvVar('VITE_FRONTEND_ID'),
323
- workerSecret: getEnvVar('VITE_FRONTEND_SECRET'),
269
+ proxyEndpoint: getEnvVar('VITE_MAUTIC_PROXY_ENDPOINT') || '/api/mautic-submit',
324
270
  timeout: parseInt(getEnvVar('VITE_MAUTIC_TIMEOUT') || '30000'),
325
271
  retries: parseInt(getEnvVar('VITE_MAUTIC_RETRIES') || '3'),
326
272
  ...overrides,
327
273
  };
328
- // Enforce required fields depending on authMode
329
- const errors = [];
330
- if (merged.authMode === 'cloudflare_proxy') {
331
- if (!merged.cloudflareWorkerUrl)
332
- errors.push('cloudflareWorkerUrl is required for cloudflare_proxy mode');
333
- if (!merged.appId)
334
- errors.push('appId is required for cloudflare_proxy mode');
335
- if (!merged.workerSecret)
336
- errors.push('workerSecret is required for cloudflare_proxy mode');
337
- }
338
- if (merged.authMode === 'direct') {
339
- if (!merged.apiUrl)
340
- errors.push('apiUrl is required for direct mode');
341
- }
342
- if (errors.length) {
343
- throw new Error(`Invalid Mautic configuration: ${errors.join('; ')}`);
344
- }
345
274
  return merged;
346
275
  };
347
276
  /**
@@ -349,25 +278,6 @@ const createMauticConfig = (overrides = {}) => {
349
278
  */
350
279
  const validateMauticConfig = (config) => {
351
280
  const errors = [];
352
- if (!config.authMode) {
353
- errors.push('authMode is required');
354
- }
355
- if (config.authMode === 'cloudflare_proxy') {
356
- if (!config.cloudflareWorkerUrl) {
357
- errors.push('cloudflareWorkerUrl is required for cloudflare_proxy mode');
358
- }
359
- if (!config.appId) {
360
- errors.push('appId is required for cloudflare_proxy mode');
361
- }
362
- if (!config.workerSecret) {
363
- errors.push('workerSecret is required for cloudflare_proxy mode');
364
- }
365
- }
366
- else if (config.authMode === 'direct') {
367
- if (!config.apiUrl) {
368
- errors.push('apiUrl is required for direct mode');
369
- }
370
- }
371
281
  if (config.timeout && config.timeout < 1000) {
372
282
  errors.push('timeout must be at least 1000ms');
373
283
  }
@@ -384,7 +294,7 @@ const validateMauticConfig = (config) => {
384
294
  */
385
295
  const getDefaultMauticConfig = () => {
386
296
  return {
387
- authMode: 'cloudflare_proxy',
297
+ proxyEndpoint: '/api/mautic-submit',
388
298
  timeout: 30000,
389
299
  retries: 3,
390
300
  };
@@ -402,339 +312,21 @@ const mergeMauticConfig = (base, overrides) => {
402
312
  * Check if Mautic is enabled based on configuration
403
313
  */
404
314
  const isMauticEnabled = (config) => {
405
- if (config.authMode === 'cloudflare_proxy') {
406
- return !!(config.cloudflareWorkerUrl && config.appId && config.workerSecret);
407
- }
408
- else if (config.authMode === 'direct') {
409
- return !!config.apiUrl;
410
- }
411
- return false;
315
+ return !!config.apiUrl;
412
316
  };
413
317
  /**
414
318
  * Get Mautic configuration summary (for debugging)
415
319
  */
416
320
  const getMauticConfigSummary = (config) => {
417
321
  return {
418
- authMode: config.authMode,
419
322
  hasApiUrl: !!config.apiUrl,
420
- hasCloudflareWorkerUrl: !!config.cloudflareWorkerUrl,
421
- hasAppId: !!config.appId,
422
- hasWorkerSecret: !!config.workerSecret,
323
+ proxyEndpoint: config.proxyEndpoint || '/api/mautic-submit',
423
324
  timeout: config.timeout,
424
325
  retries: config.retries,
425
326
  isEnabled: isMauticEnabled(config),
426
327
  };
427
328
  };
428
329
 
429
- /**
430
- * @license GPL-3.0-or-later
431
- *
432
- * This file is part of the MarVAlt Open SDK.
433
- * Copyright (c) 2025 Vibune Pty Ltd.
434
- *
435
- * This program is free software: you can redistribute it and/or modify
436
- * it under the terms of the GNU General Public License as published by
437
- * the Free Software Foundation, either version 3 of the License, or
438
- * (at your option) any later version.
439
- *
440
- * This program is distributed in the hope that it will be useful,
441
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
442
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
443
- * See the GNU General Public License for more details.
444
- */
445
- // Create a default client instance (will be overridden by provider)
446
- let defaultClient = null;
447
- /**
448
- * Set the default Mautic client for hooks
449
- */
450
- const setMauticClient = (client) => {
451
- defaultClient = client;
452
- };
453
- /**
454
- * Get the current Mautic client
455
- */
456
- const getMauticClient = () => {
457
- if (!defaultClient) {
458
- // Lazy initialize the client from environment if not set by provider/service
459
- const config = createMauticConfig();
460
- defaultClient = new MauticClient(config);
461
- }
462
- return defaultClient;
463
- };
464
- /**
465
- * Custom hook for submitting forms to Mautic
466
- */
467
- const useMauticFormSubmission = () => {
468
- const queryClient = reactQuery.useQueryClient();
469
- return reactQuery.useMutation({
470
- mutationFn: (submission) => {
471
- const client = getMauticClient();
472
- return client.submitForm(submission.formId, submission);
473
- },
474
- onSuccess: (data, variables) => {
475
- if (undefined?.DEV) {
476
- console.log('Mautic form submission successful:', data);
477
- }
478
- // Invalidate related queries
479
- queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
480
- // Track the submission event
481
- if (variables.contact?.email) {
482
- const client = getMauticClient();
483
- client.trackEvent('form_submission', {
484
- formId: variables.formId,
485
- email: variables.contact.email
486
- }).catch(console.error);
487
- }
488
- },
489
- onError: (error) => {
490
- console.error('Mautic form submission failed:', error);
491
- },
492
- });
493
- };
494
- /**
495
- * Custom hook for creating Mautic contacts
496
- */
497
- const useMauticCreateContact = () => {
498
- const queryClient = reactQuery.useQueryClient();
499
- return reactQuery.useMutation({
500
- mutationFn: (contact) => {
501
- const client = getMauticClient();
502
- return client.createContact(contact);
503
- },
504
- onSuccess: (data, variables) => {
505
- if (undefined?.DEV) {
506
- console.log('Mautic contact created:', data);
507
- }
508
- // Invalidate contacts queries
509
- queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
510
- queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.email] });
511
- },
512
- onError: (error) => {
513
- console.error('Failed to create Mautic contact:', error);
514
- },
515
- });
516
- };
517
- /**
518
- * Custom hook for updating Mautic contacts
519
- */
520
- const useMauticUpdateContact = () => {
521
- const queryClient = reactQuery.useQueryClient();
522
- return reactQuery.useMutation({
523
- mutationFn: ({ contactId, contact }) => {
524
- const client = getMauticClient();
525
- return client.updateContact(contactId, contact);
526
- },
527
- onSuccess: (data, variables) => {
528
- if (undefined?.DEV) {
529
- console.log('Mautic contact updated:', data);
530
- }
531
- // Invalidate related queries
532
- queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
533
- queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
534
- },
535
- onError: (error) => {
536
- console.error('Failed to update Mautic contact:', error);
537
- },
538
- });
539
- };
540
- /**
541
- * Custom hook for fetching Mautic contact by email
542
- */
543
- const useMauticContactByEmail = (email) => {
544
- return reactQuery.useQuery({
545
- queryKey: ['mautic-contact', email],
546
- queryFn: () => {
547
- const client = getMauticClient();
548
- return client.getContactByEmail(email);
549
- },
550
- enabled: !!email && email.includes('@'),
551
- staleTime: 5 * 60 * 1000, // 5 minutes
552
- gcTime: 10 * 60 * 1000,
553
- });
554
- };
555
- /**
556
- * Custom hook for fetching Mautic forms
557
- */
558
- const useMauticForms = () => {
559
- return reactQuery.useQuery({
560
- queryKey: ['mautic-forms'],
561
- queryFn: () => {
562
- const client = getMauticClient();
563
- return client.getForms();
564
- },
565
- staleTime: 30 * 60 * 1000, // Forms don't change often
566
- gcTime: 60 * 60 * 1000,
567
- });
568
- };
569
- /**
570
- * Custom hook for fetching a specific Mautic form
571
- */
572
- const useMauticForm = (formId) => {
573
- return reactQuery.useQuery({
574
- queryKey: ['mautic-form', formId],
575
- queryFn: () => {
576
- const client = getMauticClient();
577
- return client.getForm(formId);
578
- },
579
- enabled: !!formId,
580
- staleTime: 30 * 60 * 1000,
581
- gcTime: 60 * 60 * 1000,
582
- });
583
- };
584
- /**
585
- * Custom hook for tracking Mautic events
586
- */
587
- const useMauticEventTracking = () => {
588
- return reactQuery.useMutation({
589
- mutationFn: ({ email, eventName, eventData }) => {
590
- const client = getMauticClient();
591
- return client.trackEvent(eventName, {
592
- email,
593
- ...(eventData || {})
594
- });
595
- },
596
- onSuccess: (data, variables) => {
597
- if (undefined?.DEV) {
598
- console.log('Mautic event tracked successfully:', variables.eventName);
599
- }
600
- },
601
- onError: (error, variables) => {
602
- console.error('Failed to track Mautic event:', variables.eventName, error);
603
- },
604
- });
605
- };
606
- /**
607
- * Custom hook for adding tags to contacts
608
- */
609
- const useMauticAddTags = () => {
610
- const queryClient = reactQuery.useQueryClient();
611
- return reactQuery.useMutation({
612
- mutationFn: ({ contactId, tags }) => {
613
- const client = getMauticClient();
614
- return client.addTagToContact(contactId, tags);
615
- },
616
- onSuccess: (data, variables) => {
617
- if (undefined?.DEV) {
618
- console.log('Tags added to Mautic contact:', variables.tags);
619
- }
620
- // Invalidate contact queries
621
- queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
622
- },
623
- onError: (error) => {
624
- console.error('Failed to add tags to Mautic contact:', error);
625
- },
626
- });
627
- };
628
- /**
629
- * Custom hook for removing tags from contacts
630
- */
631
- const useMauticRemoveTags = () => {
632
- const queryClient = reactQuery.useQueryClient();
633
- return reactQuery.useMutation({
634
- mutationFn: ({ contactId, tags }) => {
635
- const client = getMauticClient();
636
- return client.removeTagFromContact(contactId, tags);
637
- },
638
- onSuccess: (data, variables) => {
639
- if (undefined?.DEV) {
640
- console.log('Tags removed from Mautic contact:', variables.tags);
641
- }
642
- // Invalidate contact queries
643
- queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
644
- },
645
- onError: (error) => {
646
- console.error('Failed to remove tags from Mautic contact:', error);
647
- },
648
- });
649
- };
650
- /**
651
- * Custom hook for getting contact tags
652
- */
653
- const useMauticContactTags = (contactId) => {
654
- return reactQuery.useQuery({
655
- queryKey: ['mautic-contact-tags', contactId],
656
- queryFn: () => {
657
- const client = getMauticClient();
658
- return client.getContactTags(contactId);
659
- },
660
- enabled: !!contactId,
661
- staleTime: 5 * 60 * 1000,
662
- gcTime: 10 * 60 * 1000,
663
- });
664
- };
665
- /**
666
- * Custom hook for getting all tags
667
- */
668
- const useMauticTags = () => {
669
- return reactQuery.useQuery({
670
- queryKey: ['mautic-tags'],
671
- queryFn: () => {
672
- const client = getMauticClient();
673
- return client.getTags();
674
- },
675
- staleTime: 30 * 60 * 1000,
676
- gcTime: 60 * 60 * 1000,
677
- });
678
- };
679
- /**
680
- * Custom hook for getting segments
681
- */
682
- const useMauticSegments = () => {
683
- return reactQuery.useQuery({
684
- queryKey: ['mautic-segments'],
685
- queryFn: () => {
686
- const client = getMauticClient();
687
- return client.getSegments();
688
- },
689
- staleTime: 30 * 60 * 1000,
690
- gcTime: 60 * 60 * 1000,
691
- });
692
- };
693
- /**
694
- * Custom hook for adding contact to segment
695
- */
696
- const useMauticAddToSegment = () => {
697
- const queryClient = reactQuery.useQueryClient();
698
- return reactQuery.useMutation({
699
- mutationFn: ({ contactId, segmentId }) => {
700
- const client = getMauticClient();
701
- return client.addContactToSegment(contactId, segmentId);
702
- },
703
- onSuccess: (data, variables) => {
704
- if (undefined?.DEV) {
705
- console.log('Contact added to segment:', variables.segmentId);
706
- }
707
- // Invalidate contact queries
708
- queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
709
- },
710
- onError: (error) => {
711
- console.error('Failed to add contact to segment:', error);
712
- },
713
- });
714
- };
715
- /**
716
- * Custom hook for removing contact from segment
717
- */
718
- const useMauticRemoveFromSegment = () => {
719
- const queryClient = reactQuery.useQueryClient();
720
- return reactQuery.useMutation({
721
- mutationFn: ({ contactId, segmentId }) => {
722
- const client = getMauticClient();
723
- return client.removeContactFromSegment(contactId, segmentId);
724
- },
725
- onSuccess: (data, variables) => {
726
- if (undefined?.DEV) {
727
- console.log('Contact removed from segment:', variables.segmentId);
728
- }
729
- // Invalidate contact queries
730
- queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
731
- },
732
- onError: (error) => {
733
- console.error('Failed to remove contact from segment:', error);
734
- },
735
- });
736
- };
737
-
738
330
  /**
739
331
  * @license GPL-3.0-or-later
740
332
  *
@@ -836,9 +428,19 @@ class MauticService {
836
428
  return this.client.trackEvent(eventName, eventData);
837
429
  }
838
430
  }
839
- // Export configured instance
840
- const mauticService = new MauticService();
841
- setMauticClient(mauticService['client']);
431
+ // Export configured instance (guarded to avoid throwing at import time)
432
+ exports.mauticService = null;
433
+ try {
434
+ exports.mauticService = new MauticService();
435
+ // Initialize the default client for hooks
436
+ // Lazy import to avoid circular/side-effect issues in some environments
437
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
438
+ const { setMauticClient } = require('../react/hooks/useMautic');
439
+ setMauticClient(exports.mauticService['client']);
440
+ }
441
+ catch {
442
+ // Silently skip initialization when environment is not configured
443
+ }
842
444
 
843
445
  /**
844
446
  * @license GPL-3.0-or-later
@@ -858,17 +460,54 @@ setMauticClient(mauticService['client']);
858
460
  */
859
461
  class MauticGenerator {
860
462
  constructor(config) {
463
+ this.cachedToken = null;
861
464
  this.config = config;
465
+ // Generator uses direct OAuth2 calls, not the client
466
+ // Client is only used for proxy mode (cloudflare_proxy)
862
467
  this.client = new MauticClient({
863
- authMode: config.authMode,
864
468
  apiUrl: config.apiUrl,
865
- cloudflareWorkerUrl: config.cloudflareWorkerUrl,
866
- appId: config.appId,
867
- workerSecret: config.workerSecret,
469
+ proxyEndpoint: config.cloudflareWorkerUrl,
868
470
  timeout: config.timeout,
869
471
  retries: config.retries,
870
472
  });
871
473
  }
474
+ /**
475
+ * Get OAuth2 token for direct mode API calls
476
+ */
477
+ async getOAuth2Token() {
478
+ if (this.cachedToken && this.cachedToken.expires_at > Date.now() + 300000) {
479
+ console.log('🔑 Using cached OAuth2 token');
480
+ return this.cachedToken.access_token;
481
+ }
482
+ if (!this.config.clientId || !this.config.clientSecret) {
483
+ throw new Error('OAuth2 credentials (clientId, clientSecret) required for direct mode');
484
+ }
485
+ console.log('🔑 Fetching new OAuth2 token...');
486
+ const tokenUrl = `${this.config.apiUrl}/oauth/v2/token`;
487
+ const body = new URLSearchParams({
488
+ grant_type: 'client_credentials',
489
+ client_id: this.config.clientId,
490
+ client_secret: this.config.clientSecret,
491
+ });
492
+ const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
493
+ if (this.config.cfAccessClientId && this.config.cfAccessClientSecret) {
494
+ headers['CF-Access-Client-Id'] = this.config.cfAccessClientId;
495
+ headers['CF-Access-Client-Secret'] = this.config.cfAccessClientSecret;
496
+ console.log('🔐 Added CF Access headers to OAuth2 token request');
497
+ }
498
+ const resp = await fetch(tokenUrl, { method: 'POST', headers, body: body.toString() });
499
+ if (!resp.ok) {
500
+ const errText = await resp.text();
501
+ throw new Error(`OAuth2 token failed: ${resp.status} ${errText}`);
502
+ }
503
+ const data = await resp.json();
504
+ this.cachedToken = {
505
+ access_token: data.access_token,
506
+ expires_at: Date.now() + (data.expires_in * 1000),
507
+ };
508
+ console.log('✅ OAuth2 token cached');
509
+ return this.cachedToken.access_token;
510
+ }
872
511
  /**
873
512
  * Generate static data for Mautic forms
874
513
  */
@@ -902,7 +541,7 @@ class MauticGenerator {
902
541
  // Include other important form properties
903
542
  formType: formDetails.formType || 'standalone',
904
543
  // Extract fields with validation and properties
905
- fields: (formDetails.fields || []).map(field => ({
544
+ fields: (formDetails.fields || []).map((field) => ({
906
545
  id: field.id,
907
546
  label: field.label,
908
547
  alias: field.alias,
@@ -921,7 +560,7 @@ class MauticGenerator {
921
560
  }
922
561
  })),
923
562
  // Extract actions
924
- actions: (formDetails.actions || []).map(action => ({
563
+ actions: (formDetails.actions || []).map((action) => ({
925
564
  id: action.id,
926
565
  name: action.name,
927
566
  type: action.type,
@@ -945,10 +584,10 @@ class MauticGenerator {
945
584
  const staticData = {
946
585
  generated_at: new Date().toISOString(),
947
586
  total_forms: forms.length,
948
- forms: forms.map(form => ({
587
+ forms: forms.map((form) => ({
949
588
  id: parseInt(form.id),
950
589
  name: form.name,
951
- alias: form.alias || form.name.toLowerCase().replace(/\s+/g, '-'),
590
+ alias: String(form.alias ?? (form.name || '').toLowerCase().replace(/\s+/g, '-')),
952
591
  description: form.description,
953
592
  isPublished: form.isPublished,
954
593
  fields: form.fields,
@@ -995,49 +634,44 @@ class MauticGenerator {
995
634
  * Fetch list of forms from Mautic
996
635
  */
997
636
  async fetchMauticFormsList() {
998
- const baseUrl = this.config.cloudflareWorkerUrl;
999
- if (!baseUrl)
1000
- throw new Error('VITE_MAUTIC_PROXY_URL not configured');
1001
- const url = `${baseUrl}?endpoint=/forms`;
1002
- const headers = {
1003
- 'Content-Type': 'application/json',
1004
- };
1005
- if (this.config.appId) {
1006
- headers['x-app-id'] = this.config.appId;
1007
- }
1008
- if (this.config.workerSecret) {
1009
- headers['x-app-secret'] = this.config.workerSecret;
637
+ if (this.config.authMode === 'cloudflare_proxy') {
638
+ const forms = await this.client.getForms();
639
+ return Array.isArray(forms) ? forms : [];
1010
640
  }
1011
- const response = await fetch(url, { headers });
1012
- if (!response.ok) {
1013
- throw new Error(`Failed to fetch forms list: ${response.status} ${response.statusText}`);
641
+ // Direct mode with OAuth2
642
+ const token = await this.getOAuth2Token();
643
+ const url = `${this.config.apiUrl}/api/forms`;
644
+ const headers = { 'Authorization': `Bearer ${token}` };
645
+ if (this.config.cfAccessClientId && this.config.cfAccessClientSecret) {
646
+ headers['CF-Access-Client-Id'] = this.config.cfAccessClientId;
647
+ headers['CF-Access-Client-Secret'] = this.config.cfAccessClientSecret;
1014
648
  }
1015
- const data = await response.json().catch(() => ({}));
1016
- const forms = data.forms;
1017
- return Array.isArray(forms) ? forms : [];
649
+ const resp = await fetch(url, { headers });
650
+ if (!resp.ok)
651
+ throw new Error(`Failed to fetch forms: ${resp.status}`);
652
+ const data = await resp.json();
653
+ return Array.isArray(data.forms) ? data.forms : [];
1018
654
  }
1019
655
  /**
1020
656
  * Fetch individual form details from Mautic
1021
657
  */
1022
658
  async fetchMauticForm(formId) {
1023
- const baseUrl = this.config.cloudflareWorkerUrl;
1024
- if (!baseUrl)
1025
- throw new Error('VITE_MAUTIC_PROXY_URL not configured');
1026
- const url = `${baseUrl}?endpoint=/forms/${formId}`;
1027
- const headers = {
1028
- 'Content-Type': 'application/json',
1029
- };
1030
- if (this.config.appId) {
1031
- headers['x-app-id'] = this.config.appId;
1032
- }
1033
- if (this.config.workerSecret) {
1034
- headers['x-app-secret'] = this.config.workerSecret;
659
+ if (this.config.authMode === 'cloudflare_proxy') {
660
+ return this.client.getForm(parseInt(formId, 10));
1035
661
  }
1036
- const response = await fetch(url, { headers });
1037
- if (!response.ok) {
1038
- throw new Error(`Failed to fetch form ${formId}: ${response.status} ${response.statusText}`);
662
+ // Direct mode with OAuth2
663
+ const token = await this.getOAuth2Token();
664
+ const url = `${this.config.apiUrl}/api/forms/${formId}`;
665
+ const headers = { 'Authorization': `Bearer ${token}` };
666
+ if (this.config.cfAccessClientId && this.config.cfAccessClientSecret) {
667
+ headers['CF-Access-Client-Id'] = this.config.cfAccessClientId;
668
+ headers['CF-Access-Client-Secret'] = this.config.cfAccessClientSecret;
1039
669
  }
1040
- return response.json().catch(() => ({}));
670
+ const resp = await fetch(url, { headers });
671
+ if (!resp.ok)
672
+ throw new Error(`Failed to fetch form ${formId}: ${resp.status}`);
673
+ const data = await resp.json();
674
+ return data.form || data;
1041
675
  }
1042
676
  }
1043
677
  /**
@@ -2403,6 +2037,315 @@ if (process.env.NODE_ENV === 'production') {
2403
2037
 
2404
2038
  var jsxRuntimeExports = jsxRuntime.exports;
2405
2039
 
2040
+ /**
2041
+ * @license GPL-3.0-or-later
2042
+ *
2043
+ * This file is part of the MarVAlt Open SDK.
2044
+ * Copyright (c) 2025 Vibune Pty Ltd.
2045
+ *
2046
+ * This program is free software: you can redistribute it and/or modify
2047
+ * it under the terms of the GNU General Public License as published by
2048
+ * the Free Software Foundation, either version 3 of the License, or
2049
+ * (at your option) any later version.
2050
+ *
2051
+ * This program is distributed in the hope that it will be useful,
2052
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2053
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
2054
+ * See the GNU General Public License for more details.
2055
+ */
2056
+ // Create a default client instance (will be overridden by provider)
2057
+ let defaultClient = null;
2058
+ /**
2059
+ * Set the default Mautic client for hooks
2060
+ */
2061
+ const setMauticClient = (client) => {
2062
+ defaultClient = client;
2063
+ };
2064
+ /**
2065
+ * Get the current Mautic client
2066
+ */
2067
+ const getMauticClient = () => {
2068
+ if (!defaultClient) {
2069
+ // Lazy initialize the client from environment if not set by provider/service
2070
+ const config = createMauticConfig();
2071
+ defaultClient = new MauticClient(config);
2072
+ }
2073
+ return defaultClient;
2074
+ };
2075
+ /**
2076
+ * Custom hook for submitting forms to Mautic
2077
+ */
2078
+ const useMauticFormSubmission = () => {
2079
+ const queryClient = reactQuery.useQueryClient();
2080
+ return reactQuery.useMutation({
2081
+ mutationFn: (submission) => {
2082
+ const client = getMauticClient();
2083
+ return client.submitForm(submission.formId, submission);
2084
+ },
2085
+ onSuccess: (data, variables) => {
2086
+ if (process.env.NODE_ENV === 'development') {
2087
+ console.log('Mautic form submission successful:', data);
2088
+ }
2089
+ // Invalidate related queries
2090
+ queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
2091
+ // Track the submission event
2092
+ if (variables.contact?.email) {
2093
+ const client = getMauticClient();
2094
+ client.trackEvent('form_submission', {
2095
+ formId: variables.formId,
2096
+ email: variables.contact.email
2097
+ }).catch(console.error);
2098
+ }
2099
+ },
2100
+ onError: (error) => {
2101
+ console.error('Mautic form submission failed:', error);
2102
+ },
2103
+ });
2104
+ };
2105
+ /**
2106
+ * Custom hook for creating Mautic contacts
2107
+ */
2108
+ const useMauticCreateContact = () => {
2109
+ const queryClient = reactQuery.useQueryClient();
2110
+ return reactQuery.useMutation({
2111
+ mutationFn: (contact) => {
2112
+ const client = getMauticClient();
2113
+ return client.createContact(contact);
2114
+ },
2115
+ onSuccess: (data, variables) => {
2116
+ if (process.env.NODE_ENV === 'development') {
2117
+ console.log('Mautic contact created:', data);
2118
+ }
2119
+ // Invalidate contacts queries
2120
+ queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
2121
+ queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.email] });
2122
+ },
2123
+ onError: (error) => {
2124
+ console.error('Failed to create Mautic contact:', error);
2125
+ },
2126
+ });
2127
+ };
2128
+ /**
2129
+ * Custom hook for updating Mautic contacts
2130
+ */
2131
+ const useMauticUpdateContact = () => {
2132
+ const queryClient = reactQuery.useQueryClient();
2133
+ return reactQuery.useMutation({
2134
+ mutationFn: ({ contactId, contact }) => {
2135
+ const client = getMauticClient();
2136
+ return client.updateContact(contactId, contact);
2137
+ },
2138
+ onSuccess: (data, variables) => {
2139
+ if (process.env.NODE_ENV === 'development') {
2140
+ console.log('Mautic contact updated:', data);
2141
+ }
2142
+ // Invalidate related queries
2143
+ queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
2144
+ queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
2145
+ },
2146
+ onError: (error) => {
2147
+ console.error('Failed to update Mautic contact:', error);
2148
+ },
2149
+ });
2150
+ };
2151
+ /**
2152
+ * Custom hook for fetching Mautic contact by email
2153
+ */
2154
+ const useMauticContactByEmail = (email) => {
2155
+ return reactQuery.useQuery({
2156
+ queryKey: ['mautic-contact', email],
2157
+ queryFn: () => {
2158
+ const client = getMauticClient();
2159
+ return client.getContactByEmail(email);
2160
+ },
2161
+ enabled: !!email && email.includes('@'),
2162
+ staleTime: 5 * 60 * 1000, // 5 minutes
2163
+ gcTime: 10 * 60 * 1000,
2164
+ });
2165
+ };
2166
+ /**
2167
+ * Custom hook for fetching Mautic forms
2168
+ */
2169
+ const useMauticForms = () => {
2170
+ return reactQuery.useQuery({
2171
+ queryKey: ['mautic-forms'],
2172
+ queryFn: () => {
2173
+ const client = getMauticClient();
2174
+ return client.getForms();
2175
+ },
2176
+ staleTime: 30 * 60 * 1000, // Forms don't change often
2177
+ gcTime: 60 * 60 * 1000,
2178
+ });
2179
+ };
2180
+ /**
2181
+ * Custom hook for fetching a specific Mautic form
2182
+ */
2183
+ const useMauticForm = (formId) => {
2184
+ return reactQuery.useQuery({
2185
+ queryKey: ['mautic-form', formId],
2186
+ queryFn: () => {
2187
+ const client = getMauticClient();
2188
+ return client.getForm(formId);
2189
+ },
2190
+ enabled: !!formId,
2191
+ staleTime: 30 * 60 * 1000,
2192
+ gcTime: 60 * 60 * 1000,
2193
+ });
2194
+ };
2195
+ /**
2196
+ * Custom hook for tracking Mautic events
2197
+ */
2198
+ const useMauticEventTracking = () => {
2199
+ return reactQuery.useMutation({
2200
+ mutationFn: ({ email, eventName, eventData }) => {
2201
+ const client = getMauticClient();
2202
+ return client.trackEvent(eventName, {
2203
+ email,
2204
+ ...(eventData || {})
2205
+ });
2206
+ },
2207
+ onSuccess: (data, variables) => {
2208
+ if (process.env.NODE_ENV === 'development') {
2209
+ console.log('Mautic event tracked successfully:', variables.eventName);
2210
+ }
2211
+ },
2212
+ onError: (error, variables) => {
2213
+ console.error('Failed to track Mautic event:', variables.eventName, error);
2214
+ },
2215
+ });
2216
+ };
2217
+ /**
2218
+ * Custom hook for adding tags to contacts
2219
+ */
2220
+ const useMauticAddTags = () => {
2221
+ const queryClient = reactQuery.useQueryClient();
2222
+ return reactQuery.useMutation({
2223
+ mutationFn: ({ contactId, tags }) => {
2224
+ const client = getMauticClient();
2225
+ return client.addTagToContact(contactId, tags);
2226
+ },
2227
+ onSuccess: (data, variables) => {
2228
+ if (process.env.NODE_ENV === 'development') {
2229
+ console.log('Tags added to Mautic contact:', variables.tags);
2230
+ }
2231
+ // Invalidate contact queries
2232
+ queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
2233
+ },
2234
+ onError: (error) => {
2235
+ console.error('Failed to add tags to Mautic contact:', error);
2236
+ },
2237
+ });
2238
+ };
2239
+ /**
2240
+ * Custom hook for removing tags from contacts
2241
+ */
2242
+ const useMauticRemoveTags = () => {
2243
+ const queryClient = reactQuery.useQueryClient();
2244
+ return reactQuery.useMutation({
2245
+ mutationFn: ({ contactId, tags }) => {
2246
+ const client = getMauticClient();
2247
+ return client.removeTagFromContact(contactId, tags);
2248
+ },
2249
+ onSuccess: (data, variables) => {
2250
+ if (process.env.NODE_ENV === 'development') {
2251
+ console.log('Tags removed from Mautic contact:', variables.tags);
2252
+ }
2253
+ // Invalidate contact queries
2254
+ queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
2255
+ },
2256
+ onError: (error) => {
2257
+ console.error('Failed to remove tags from Mautic contact:', error);
2258
+ },
2259
+ });
2260
+ };
2261
+ /**
2262
+ * Custom hook for getting contact tags
2263
+ */
2264
+ const useMauticContactTags = (contactId) => {
2265
+ return reactQuery.useQuery({
2266
+ queryKey: ['mautic-contact-tags', contactId],
2267
+ queryFn: () => {
2268
+ const client = getMauticClient();
2269
+ return client.getContactTags(contactId);
2270
+ },
2271
+ enabled: !!contactId,
2272
+ staleTime: 5 * 60 * 1000,
2273
+ gcTime: 10 * 60 * 1000,
2274
+ });
2275
+ };
2276
+ /**
2277
+ * Custom hook for getting all tags
2278
+ */
2279
+ const useMauticTags = () => {
2280
+ return reactQuery.useQuery({
2281
+ queryKey: ['mautic-tags'],
2282
+ queryFn: () => {
2283
+ const client = getMauticClient();
2284
+ return client.getTags();
2285
+ },
2286
+ staleTime: 30 * 60 * 1000,
2287
+ gcTime: 60 * 60 * 1000,
2288
+ });
2289
+ };
2290
+ /**
2291
+ * Custom hook for getting segments
2292
+ */
2293
+ const useMauticSegments = () => {
2294
+ return reactQuery.useQuery({
2295
+ queryKey: ['mautic-segments'],
2296
+ queryFn: () => {
2297
+ const client = getMauticClient();
2298
+ return client.getSegments();
2299
+ },
2300
+ staleTime: 30 * 60 * 1000,
2301
+ gcTime: 60 * 60 * 1000,
2302
+ });
2303
+ };
2304
+ /**
2305
+ * Custom hook for adding contact to segment
2306
+ */
2307
+ const useMauticAddToSegment = () => {
2308
+ const queryClient = reactQuery.useQueryClient();
2309
+ return reactQuery.useMutation({
2310
+ mutationFn: ({ contactId, segmentId }) => {
2311
+ const client = getMauticClient();
2312
+ return client.addContactToSegment(contactId, segmentId);
2313
+ },
2314
+ onSuccess: (data, variables) => {
2315
+ if (process.env.NODE_ENV === 'development') {
2316
+ console.log('Contact added to segment:', variables.segmentId);
2317
+ }
2318
+ // Invalidate contact queries
2319
+ queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
2320
+ },
2321
+ onError: (error) => {
2322
+ console.error('Failed to add contact to segment:', error);
2323
+ },
2324
+ });
2325
+ };
2326
+ /**
2327
+ * Custom hook for removing contact from segment
2328
+ */
2329
+ const useMauticRemoveFromSegment = () => {
2330
+ const queryClient = reactQuery.useQueryClient();
2331
+ return reactQuery.useMutation({
2332
+ mutationFn: ({ contactId, segmentId }) => {
2333
+ const client = getMauticClient();
2334
+ return client.removeContactFromSegment(contactId, segmentId);
2335
+ },
2336
+ onSuccess: (data, variables) => {
2337
+ if (process.env.NODE_ENV === 'development') {
2338
+ console.log('Contact removed from segment:', variables.segmentId);
2339
+ }
2340
+ // Invalidate contact queries
2341
+ queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
2342
+ },
2343
+ onError: (error) => {
2344
+ console.error('Failed to remove contact from segment:', error);
2345
+ },
2346
+ });
2347
+ };
2348
+
2406
2349
  const MauticForm = ({ formId, title, description, className = '', form, onSubmit, onSuccess, onError, }) => {
2407
2350
  const [formData, setFormData] = React.useState({});
2408
2351
  const [errors, setErrors] = React.useState({});
@@ -2575,21 +2518,19 @@ const MauticForm = ({ formId, title, description, className = '', form, onSubmit
2575
2518
  .map((field) => (jsxRuntimeExports.jsxs("div", { className: `form-field form-field-${field.type}`, children: [field.showLabel !== false && (jsxRuntimeExports.jsxs("label", { htmlFor: field.alias, className: "field-label", children: [field.label, field.isRequired && jsxRuntimeExports.jsx("span", { className: "required", children: "*" })] })), renderField(field), field.properties?.helpText && (jsxRuntimeExports.jsx("p", { className: "field-help", children: field.properties.helpText })), errors[field.alias] && (jsxRuntimeExports.jsx("p", { className: "field-error", children: errors[field.alias] }))] }, field.id))) }), jsxRuntimeExports.jsx("div", { className: "form-actions", children: jsxRuntimeExports.jsx("button", { type: "submit", disabled: isSubmitting, className: "submit-button", children: isSubmitting ? 'Submitting...' : 'Submit' }) }), submitMutation.error && (jsxRuntimeExports.jsx("div", { className: "form-error", children: jsxRuntimeExports.jsx("p", { children: "There was an error submitting the form. Please try again." }) }))] }));
2576
2519
  };
2577
2520
 
2578
- const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, children }) => {
2521
+ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, children }) => {
2579
2522
  React.useEffect(() => {
2580
2523
  // Determine if tracking is enabled
2581
- const isEnabled = enabled ?? (!!proxyUrl && !!appId && !!workerSecret);
2524
+ const isEnabled = enabled ?? !!proxyUrl;
2582
2525
  const trackingProxyUrl = proxyUrl;
2583
2526
  // Debug logging to understand configuration
2584
- if (undefined?.DEV) {
2527
+ if (process.env.NODE_ENV === 'development') {
2585
2528
  console.log('🔍 Mautic Tracking Debug:', {
2586
2529
  isEnabled,
2587
2530
  mauticUrl,
2588
2531
  trackingProxyUrl,
2589
- hasAppId: !!appId,
2590
- hasWorkerSecret: !!workerSecret,
2591
- isDev: undefined?.DEV,
2592
- isProd: undefined?.PROD
2532
+ isDev: true,
2533
+ isProd: false
2593
2534
  });
2594
2535
  }
2595
2536
  // Check if tracking is enabled
@@ -2598,7 +2539,7 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
2598
2539
  return;
2599
2540
  }
2600
2541
  // Check if we're in development mode - skip tracking in dev
2601
- if (undefined?.DEV) {
2542
+ if (process.env.NODE_ENV === 'development') {
2602
2543
  console.log('Mautic tracking disabled in development mode');
2603
2544
  return;
2604
2545
  }
@@ -2609,6 +2550,8 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
2609
2550
  }
2610
2551
  console.log('✅ Mautic tracking enabled for production');
2611
2552
  // Initialize Mautic tracking with error handling
2553
+ // Capture original fetch for cleanup
2554
+ let originalFetch;
2612
2555
  try {
2613
2556
  // Pre-initialize tracking object and queue (standard Mautic snippet behavior)
2614
2557
  window.MauticTrackingObject = 'mt';
@@ -2616,20 +2559,12 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
2616
2559
  (window.mt.q = window.mt.q || []).push(arguments);
2617
2560
  };
2618
2561
  // Intercept Mautic tracking API calls to use our proxy (generic /mtc/ matcher)
2619
- const originalFetch = window.fetch.bind(window);
2562
+ originalFetch = window.fetch.bind(window);
2620
2563
  const interceptFetch = function (url, options) {
2621
2564
  if (typeof url === 'string' && /\/mtc\//.test(url)) {
2622
2565
  try {
2623
2566
  const proxyUrl = url.replace(/https?:\/\/[^/]+\/mtc\//, `${trackingProxyUrl}?endpoint=/mtc/`);
2624
- const proxyOptions = {
2625
- ...options,
2626
- headers: {
2627
- ...options?.headers,
2628
- 'Content-Type': 'application/json',
2629
- 'x-app-id': appId || '',
2630
- ...(workerSecret ? { 'x-app-secret': workerSecret } : {})
2631
- }
2632
- };
2567
+ const proxyOptions = { ...options };
2633
2568
  return originalFetch(proxyUrl, proxyOptions);
2634
2569
  }
2635
2570
  catch (e) {
@@ -2639,16 +2574,9 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
2639
2574
  return originalFetch(url, options);
2640
2575
  };
2641
2576
  window.fetch = interceptFetch;
2642
- // Fetch the tracking script through the proxy with required headers
2577
+ // Fetch the tracking script through the proxy
2643
2578
  const trackingScriptUrl = `${trackingProxyUrl}?endpoint=/mtc.js`;
2644
- fetch(trackingScriptUrl, {
2645
- method: 'GET',
2646
- headers: {
2647
- 'Content-Type': 'application/javascript',
2648
- 'x-app-id': appId || '',
2649
- ...(workerSecret ? { 'x-app-secret': workerSecret } : {})
2650
- }
2651
- })
2579
+ fetch(trackingScriptUrl, { method: 'GET' })
2652
2580
  .then(response => {
2653
2581
  if (!response.ok) {
2654
2582
  throw new Error(`Failed to load Mautic tracking script: ${response.status}`);
@@ -2676,12 +2604,11 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
2676
2604
  }
2677
2605
  // Cleanup function
2678
2606
  return () => {
2679
- // Restore original fetch if needed
2680
- if (window.fetch !== originalFetch) {
2607
+ if (originalFetch && window.fetch !== originalFetch) {
2681
2608
  window.fetch = originalFetch;
2682
2609
  }
2683
2610
  };
2684
- }, [enabled, mauticUrl, proxyUrl, appId, workerSecret]);
2611
+ }, [enabled, mauticUrl, proxyUrl]);
2685
2612
  // Render children if provided, otherwise render nothing
2686
2613
  return children ? jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: children }) : null;
2687
2614
  };
@@ -3056,30 +2983,33 @@ const validateField = (field, value) => {
3056
2983
  const validateCustomRule = (rule, value, field) => {
3057
2984
  switch (rule) {
3058
2985
  case 'min_length':
3059
- if (typeof value === 'string' && value.length < (field.properties?.minLength || 0)) {
3060
- return `Minimum length is ${field.properties?.minLength} characters`;
2986
+ if (typeof value === 'string' && value.length < Number(field.properties?.minLength ?? 0)) {
2987
+ return `Minimum length is ${Number(field.properties?.minLength ?? 0)} characters`;
3061
2988
  }
3062
2989
  break;
3063
2990
  case 'max_length':
3064
- if (typeof value === 'string' && value.length > (field.properties?.maxLength || 1000)) {
3065
- return `Maximum length is ${field.properties?.maxLength} characters`;
2991
+ if (typeof value === 'string' && value.length > Number(field.properties?.maxLength ?? 1000)) {
2992
+ return `Maximum length is ${Number(field.properties?.maxLength ?? 1000)} characters`;
3066
2993
  }
3067
2994
  break;
3068
2995
  case 'min_value':
3069
- if (typeof value === 'number' && value < (field.properties?.minValue || 0)) {
3070
- return `Minimum value is ${field.properties?.minValue}`;
2996
+ if (typeof value === 'number' && value < Number(field.properties?.minValue ?? 0)) {
2997
+ return `Minimum value is ${Number(field.properties?.minValue ?? 0)}`;
3071
2998
  }
3072
2999
  break;
3073
3000
  case 'max_value':
3074
- if (typeof value === 'number' && value > (field.properties?.maxValue || 1000000)) {
3075
- return `Maximum value is ${field.properties?.maxValue}`;
3001
+ if (typeof value === 'number' && value > Number(field.properties?.maxValue ?? 1000000)) {
3002
+ return `Maximum value is ${Number(field.properties?.maxValue ?? 1000000)}`;
3076
3003
  }
3077
3004
  break;
3078
3005
  case 'pattern':
3079
- if (field.properties?.pattern) {
3080
- const regex = new RegExp(field.properties.pattern);
3081
- if (!regex.test(value)) {
3082
- return 'Invalid format';
3006
+ {
3007
+ const pattern = field.properties?.pattern;
3008
+ if (pattern) {
3009
+ const regex = new RegExp(String(pattern));
3010
+ if (!regex.test(value)) {
3011
+ return 'Invalid format';
3012
+ }
3083
3013
  }
3084
3014
  }
3085
3015
  break;
@@ -3169,7 +3099,6 @@ exports.isValidEmail = isValidEmail;
3169
3099
  exports.isValidPhone = isValidPhone;
3170
3100
  exports.isValidUrl = isValidUrl;
3171
3101
  exports.loadMauticData = loadMauticData;
3172
- exports.mauticService = mauticService;
3173
3102
  exports.mergeMauticConfig = mergeMauticConfig;
3174
3103
  exports.sanitizeFormData = sanitizeFormData;
3175
3104
  exports.searchForms = searchForms;