@amplifyup/sdk 0.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/react.js ADDED
@@ -0,0 +1,1966 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ /* @amplifyup/sdk - https://amplifyup.ai */
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+
14
+ // src/adapters/aws.ts
15
+ var AWSAdapter = class {
16
+ constructor() {
17
+ this.initialized = false;
18
+ this.config = null;
19
+ this.apiGatewayUrl = "";
20
+ }
21
+ async init(config) {
22
+ if (typeof window === "undefined") {
23
+ console.warn("AmplifyUp SDK: Window object not available (SSR)");
24
+ return;
25
+ }
26
+ if (this.initialized) {
27
+ return;
28
+ }
29
+ this.config = {
30
+ trackingId: config.trackingId,
31
+ debug: config.debug,
32
+ dev: config.dev
33
+ };
34
+ this.apiGatewayUrl = config.apiGatewayUrl || "";
35
+ if (!this.apiGatewayUrl) {
36
+ console.warn("AmplifyUp SDK: AWS API Gateway URL not configured. Tracking disabled.");
37
+ return;
38
+ }
39
+ this.initialized = true;
40
+ if (config.debug) {
41
+ console.log("AmplifyUp SDK: AWS adapter initialized", {
42
+ apiGatewayUrl: this.apiGatewayUrl
43
+ });
44
+ }
45
+ }
46
+ isInitialized() {
47
+ return this.initialized;
48
+ }
49
+ async sendEvent(endpoint, eventData) {
50
+ if (!this.apiGatewayUrl) {
51
+ console.warn("AmplifyUp SDK: AWS configuration missing");
52
+ return;
53
+ }
54
+ const url = `${this.apiGatewayUrl}${endpoint}`;
55
+ try {
56
+ const response = await fetch(url, {
57
+ method: "POST",
58
+ headers: {
59
+ "Content-Type": "application/json"
60
+ },
61
+ body: JSON.stringify(eventData)
62
+ });
63
+ if (!response.ok) {
64
+ if (this.config.debug) {
65
+ console.warn(`AmplifyUp SDK: Event tracking failed: ${response.status} ${response.statusText}`);
66
+ }
67
+ } else if (this.config.debug) {
68
+ const result = await response.json().catch(() => ({}));
69
+ console.log("AmplifyUp SDK: Event tracked", result);
70
+ }
71
+ } catch (error) {
72
+ if (this.config.debug) {
73
+ console.error("AmplifyUp SDK: Event tracking error:", error);
74
+ }
75
+ }
76
+ }
77
+ page(properties) {
78
+ if (!this.initialized) {
79
+ return;
80
+ }
81
+ if (typeof window !== "undefined") {
82
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
83
+ return;
84
+ }
85
+ if (window.location.pathname.includes("/api/pages/proxy")) {
86
+ return;
87
+ }
88
+ const urlParams = new URLSearchParams(window.location.search);
89
+ if (urlParams.get("preview") === "true" || urlParams.get("visualEditor") === "true") {
90
+ return;
91
+ }
92
+ if (window.self !== window.top) {
93
+ return;
94
+ }
95
+ if (window.location.href.includes("preview=true") || window.location.href.includes("visualEditor=true")) {
96
+ return;
97
+ }
98
+ }
99
+ const eventData = {
100
+ type: "page",
101
+ anonymousId: properties.instanceId,
102
+ userId: properties.userId,
103
+ properties: {
104
+ path: properties.path || properties.url,
105
+ referrer: properties.referrer,
106
+ ...properties
107
+ },
108
+ context: {
109
+ page: {
110
+ path: properties.path || properties.url,
111
+ referrer: properties.referrer
112
+ }
113
+ },
114
+ timestamp: properties.timestamp || (/* @__PURE__ */ new Date()).toISOString()
115
+ };
116
+ this.sendEvent("/v1/page", eventData);
117
+ }
118
+ track(eventName, properties) {
119
+ if (!this.initialized) {
120
+ return;
121
+ }
122
+ if (typeof window !== "undefined") {
123
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
124
+ return;
125
+ }
126
+ if (window.location.pathname.includes("/api/pages/proxy")) {
127
+ return;
128
+ }
129
+ const urlParams = new URLSearchParams(window.location.search);
130
+ if (urlParams.get("preview") === "true" || urlParams.get("visualEditor") === "true") {
131
+ return;
132
+ }
133
+ if (window.self !== window.top) {
134
+ return;
135
+ }
136
+ if (window.location.href.includes("preview=true") || window.location.href.includes("visualEditor=true")) {
137
+ return;
138
+ }
139
+ }
140
+ const eventData = {
141
+ type: "track",
142
+ event: eventName,
143
+ anonymousId: properties.instanceId,
144
+ userId: properties.userId,
145
+ properties: {
146
+ ...properties
147
+ },
148
+ context: {
149
+ url: properties.url || (typeof window !== "undefined" ? window.location.href : "")
150
+ },
151
+ timestamp: properties.timestamp || (/* @__PURE__ */ new Date()).toISOString()
152
+ };
153
+ this.sendEvent("/v1/track", eventData);
154
+ }
155
+ identify(userId, traits) {
156
+ if (!this.initialized) {
157
+ console.warn("AmplifyUp SDK: Not initialized. Call init() first.");
158
+ return;
159
+ }
160
+ const eventData = {
161
+ type: "identify",
162
+ userId,
163
+ anonymousId: traits.instanceId,
164
+ traits: {
165
+ ...traits
166
+ },
167
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
168
+ };
169
+ this.sendEvent("/v1/track", eventData);
170
+ }
171
+ reset() {
172
+ if (this.config?.debug) {
173
+ console.log("AmplifyUp SDK: Reset called (identity cleared)");
174
+ }
175
+ }
176
+ };
177
+
178
+ // src/storage.ts
179
+ var INSTANCE_ID_KEY = "amplifyup_instance_id";
180
+ var USER_ID_KEY = "amplifyup_user_id";
181
+ function getInstanceId() {
182
+ if (typeof window === "undefined") {
183
+ return "";
184
+ }
185
+ const stored = localStorage.getItem(INSTANCE_ID_KEY);
186
+ if (stored) {
187
+ return stored;
188
+ }
189
+ const instanceId = `anon_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
190
+ localStorage.setItem(INSTANCE_ID_KEY, instanceId);
191
+ return instanceId;
192
+ }
193
+ function getUserId() {
194
+ if (typeof window === "undefined") {
195
+ return null;
196
+ }
197
+ return localStorage.getItem(USER_ID_KEY);
198
+ }
199
+ function setUserId(userId) {
200
+ if (typeof window === "undefined") {
201
+ return;
202
+ }
203
+ localStorage.setItem(USER_ID_KEY, userId);
204
+ }
205
+ function clearIds() {
206
+ if (typeof window === "undefined") {
207
+ return;
208
+ }
209
+ localStorage.removeItem(INSTANCE_ID_KEY);
210
+ localStorage.removeItem(USER_ID_KEY);
211
+ }
212
+
213
+ // src/index.ts
214
+ var PROD_API_URL = "https://api.amplifyup.ai" ;
215
+ var PROD_ORCHESTRATOR_URL = "https://amplifyup.ai" ;
216
+ var AmplifyUpSDK = class {
217
+ constructor(adapter) {
218
+ this.config = null;
219
+ // Internal URLs - set during init, not part of user config
220
+ this.apiGatewayUrl = "";
221
+ this.orchestratorUrl = "";
222
+ this.decisionApiUrl = "";
223
+ this.initialized = false;
224
+ this.highlightingEnabled = false;
225
+ this.componentRegistry = [];
226
+ this.messageListener = null;
227
+ this.mutationObserver = null;
228
+ this.isVisualEditorMode = false;
229
+ this.eventSelectionMode = false;
230
+ this.eventSelectionClickHandler = null;
231
+ this.eventTrackingConfig = [];
232
+ this.attachedEventListeners = [];
233
+ this.trackingIntervals = [];
234
+ this.trackingCleanup = null;
235
+ this.adapter = adapter || new AWSAdapter();
236
+ if (typeof window !== "undefined") {
237
+ this.isVisualEditorMode = this.detectVisualEditorMode();
238
+ if (this.isVisualEditorMode) {
239
+ this.setupHighlightingListener();
240
+ console.log("[AmplifyUp SDK] Visual editor mode detected - message listener enabled");
241
+ }
242
+ }
243
+ }
244
+ /**
245
+ * Detect if we're running in visual editor mode
246
+ * Returns true if:
247
+ * 1. Preview mode flag is set by proxy
248
+ * 2. We're in an iframe AND parent is from orchestrator domain
249
+ * 3. OR visualEditor/preview query parameter is present
250
+ */
251
+ detectVisualEditorMode() {
252
+ if (typeof window === "undefined") return false;
253
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
254
+ return true;
255
+ }
256
+ const urlParams = new URLSearchParams(window.location.search);
257
+ if (urlParams.get("visualEditor") === "true" || urlParams.get("preview") === "true") {
258
+ return true;
259
+ }
260
+ const inIframe = window.self !== window.top;
261
+ if (!inIframe) return false;
262
+ try {
263
+ if (document.referrer) {
264
+ const referrerUrl = new URL(document.referrer);
265
+ if (referrerUrl.hostname === "localhost" && referrerUrl.port === "5001" || referrerUrl.pathname.includes("/dashboard/composer") || referrerUrl.pathname.includes("/dashboard/routes/")) {
266
+ return true;
267
+ }
268
+ }
269
+ } catch (e) {
270
+ }
271
+ return false;
272
+ }
273
+ /**
274
+ * Check if we're in preview/composer mode (should disable tracking)
275
+ */
276
+ isPreviewMode() {
277
+ if (typeof window === "undefined") return false;
278
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
279
+ return true;
280
+ }
281
+ if (window.location.pathname.includes("/api/pages/proxy")) {
282
+ return true;
283
+ }
284
+ const urlParams = new URLSearchParams(window.location.search);
285
+ if (urlParams.get("preview") === "true" || urlParams.get("visualEditor") === "true") {
286
+ return true;
287
+ }
288
+ const fullUrl = window.location.href;
289
+ if (fullUrl.includes("preview=true") || fullUrl.includes("visualEditor=true")) {
290
+ return true;
291
+ }
292
+ if (window.self !== window.top) {
293
+ if (document.referrer) {
294
+ try {
295
+ const referrerUrl = new URL(document.referrer);
296
+ if (referrerUrl.hostname === "localhost" && referrerUrl.port === "5001" || referrerUrl.pathname.includes("/dashboard/composer") || referrerUrl.pathname.includes("/dashboard/routes/")) {
297
+ return true;
298
+ }
299
+ } catch {
300
+ }
301
+ }
302
+ return true;
303
+ }
304
+ return false;
305
+ }
306
+ /**
307
+ * Get production URLs
308
+ *
309
+ * URLs are baked in at build time via tsup.config.ts
310
+ *
311
+ * API Gateway endpoints:
312
+ * - /v1/track - Event tracking (ingestion)
313
+ * - /v1/page - Page view tracking (ingestion)
314
+ * - /v1/personalize - Decision engine (personalization)
315
+ */
316
+ getProductionUrls() {
317
+ return {
318
+ apiGatewayUrl: PROD_API_URL,
319
+ orchestratorUrl: PROD_ORCHESTRATOR_URL,
320
+ decisionApiUrl: `${PROD_API_URL}/v1/personalize`
321
+ };
322
+ }
323
+ /**
324
+ * Initialize the SDK
325
+ * Automatically reads env vars and sets up tracking
326
+ */
327
+ async init(config) {
328
+ if (!this.adapter) {
329
+ this.adapter = new AWSAdapter();
330
+ }
331
+ const isLocalhost = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
332
+ if (isLocalhost || config.dev === true) {
333
+ this.apiGatewayUrl = "http://localhost:5001/api";
334
+ this.orchestratorUrl = "http://localhost:5001";
335
+ this.decisionApiUrl = "http://localhost:5001/api/v1/personalize";
336
+ } else {
337
+ const urls = this.getProductionUrls();
338
+ this.apiGatewayUrl = urls.apiGatewayUrl;
339
+ this.orchestratorUrl = urls.orchestratorUrl;
340
+ this.decisionApiUrl = urls.decisionApiUrl;
341
+ }
342
+ this.config = {
343
+ trackingId: config.trackingId,
344
+ debug: config.debug ?? true,
345
+ // Enable debug by default
346
+ dev: config.dev
347
+ };
348
+ try {
349
+ const adapterConfig = {
350
+ ...this.config,
351
+ apiGatewayUrl: this.apiGatewayUrl,
352
+ orchestratorUrl: this.orchestratorUrl,
353
+ decisionApiUrl: this.decisionApiUrl
354
+ };
355
+ await this.adapter.init(adapterConfig);
356
+ this.initialized = this.adapter.isInitialized();
357
+ if (typeof window !== "undefined") {
358
+ window.AmplifyUp = {
359
+ getInstanceId: () => this.getInstanceId(),
360
+ getUserId: () => this.getUserId(),
361
+ getTrackingId: () => this.getTrackingId(),
362
+ getDiagnostics: () => this.getDiagnostics(),
363
+ getStatus: () => {
364
+ console.log(this.getStatus());
365
+ return this.getDiagnostics();
366
+ },
367
+ testTracking: () => this.testTracking(),
368
+ verifyAWSConnection: () => {
369
+ const result = this.verifyAWSConnection();
370
+ console.log("AWS Connection Verification:", result.message);
371
+ console.log("Full details:", result);
372
+ return result;
373
+ },
374
+ isInitialized: () => this.isInitialized(),
375
+ // Re-export main tracking methods for convenience
376
+ page: (url, properties) => this.page(url, properties),
377
+ track: (eventName, properties) => this.track(eventName, properties),
378
+ identify: (userId, traits) => this.identify(userId, traits)
379
+ };
380
+ if (this.config?.debug) {
381
+ console.log("AmplifyUp SDK: Diagnostic methods available on window.AmplifyUp");
382
+ console.log("Try: AmplifyUp.getStatus() or AmplifyUp.verifyAWSConnection()");
383
+ }
384
+ }
385
+ this.clearTrackingIntervals();
386
+ if (this.isPreviewMode()) {
387
+ return;
388
+ }
389
+ if (typeof window !== "undefined" && this.initialized) {
390
+ await new Promise((resolve) => setTimeout(resolve, 200));
391
+ if (this.isPreviewMode()) {
392
+ return;
393
+ }
394
+ const url = window.location.pathname;
395
+ this.page(url);
396
+ }
397
+ if (this.config?.debug && this.initialized) {
398
+ console.log("AmplifyUp SDK: Initialized successfully", { trackingId: config.trackingId });
399
+ }
400
+ } catch (error) {
401
+ console.error("AmplifyUp SDK: Initialization failed", error);
402
+ throw error;
403
+ }
404
+ }
405
+ /**
406
+ * Check if SDK is initialized
407
+ */
408
+ isInitialized() {
409
+ return this.initialized && this.adapter.isInitialized();
410
+ }
411
+ /**
412
+ * Clear all tracking intervals and event listeners
413
+ */
414
+ clearTrackingIntervals() {
415
+ if (this.trackingCleanup) {
416
+ this.trackingCleanup();
417
+ this.trackingCleanup = null;
418
+ }
419
+ this.trackingIntervals.forEach((id) => clearInterval(id));
420
+ this.trackingIntervals = [];
421
+ }
422
+ /**
423
+ * Track a page view
424
+ */
425
+ page(url, properties) {
426
+ if (this.isPreviewMode()) {
427
+ return;
428
+ }
429
+ if (!this.isInitialized()) {
430
+ return;
431
+ }
432
+ const instanceId = getInstanceId();
433
+ const pageProperties = {
434
+ name: url,
435
+ path: url,
436
+ url: typeof window !== "undefined" ? window.location.href : url,
437
+ referrer: typeof document !== "undefined" ? document.referrer : void 0,
438
+ instanceId,
439
+ trackingId: this.config?.trackingId,
440
+ // Timestamp is included in event payload
441
+ ...properties
442
+ };
443
+ this.adapter.page(pageProperties);
444
+ }
445
+ /**
446
+ * Track an event
447
+ */
448
+ track(eventName, properties) {
449
+ if (this.isPreviewMode()) {
450
+ return;
451
+ }
452
+ if (!this.isInitialized()) {
453
+ return;
454
+ }
455
+ const instanceId = getInstanceId();
456
+ const trackProperties = {
457
+ instanceId,
458
+ trackingId: this.config?.trackingId,
459
+ url: typeof window !== "undefined" ? window.location.href : void 0,
460
+ // Timestamp is included in event payload
461
+ ...properties
462
+ };
463
+ this.adapter.track(eventName, trackProperties);
464
+ }
465
+ /**
466
+ * Track a button click or CTA interaction
467
+ */
468
+ trackClick(elementId, elementText, properties) {
469
+ this.track("Button Click", {
470
+ elementId,
471
+ elementText,
472
+ ...properties
473
+ });
474
+ }
475
+ /**
476
+ * Track form submission
477
+ */
478
+ trackFormSubmit(formId, formData) {
479
+ this.track("Form Submit", {
480
+ formId,
481
+ ...formData
482
+ });
483
+ }
484
+ /**
485
+ * Track form field interaction
486
+ */
487
+ trackFormFieldInteraction(fieldName, fieldValue) {
488
+ this.track("Form Field Interaction", {
489
+ fieldName,
490
+ fieldValue: fieldValue ? fieldValue.substring(0, 100) : void 0
491
+ // Truncate for privacy
492
+ });
493
+ }
494
+ /**
495
+ * Track product/content view
496
+ */
497
+ trackContentView(contentId, contentType, properties) {
498
+ this.track("Content View", {
499
+ contentId,
500
+ contentType,
501
+ ...properties
502
+ });
503
+ }
504
+ /**
505
+ * Identify user (when they sign up or log in)
506
+ */
507
+ identify(userId, traits) {
508
+ if (this.isPreviewMode()) {
509
+ if (this.config?.debug) {
510
+ console.log("AmplifyUp SDK: Identify tracking disabled in preview mode", { userId });
511
+ }
512
+ return;
513
+ }
514
+ if (!this.isInitialized()) {
515
+ if (this.config?.debug) {
516
+ console.warn("AmplifyUp SDK: Not initialized. Identify not called.");
517
+ }
518
+ return;
519
+ }
520
+ const instanceId = getInstanceId();
521
+ const identifyTraits = {
522
+ instanceId,
523
+ trackingId: this.config?.trackingId,
524
+ ...traits
525
+ };
526
+ this.adapter.identify(userId, identifyTraits);
527
+ setUserId(userId);
528
+ if (this.config?.debug) {
529
+ console.log("AmplifyUp SDK: User identified", { userId, instanceId });
530
+ }
531
+ }
532
+ /**
533
+ * Track content engagement (when user interacts with personalized content)
534
+ */
535
+ trackContentEngagement(contentId, engagementType) {
536
+ this.track("Content Engagement", {
537
+ contentId,
538
+ engagementType
539
+ });
540
+ if (this.orchestratorUrl) {
541
+ const instanceId = getInstanceId();
542
+ fetch(`${this.orchestratorUrl}/api/personalization/feedback`, {
543
+ method: "POST",
544
+ headers: { "Content-Type": "application/json" },
545
+ body: JSON.stringify({
546
+ instanceId,
547
+ trackingId: this.config?.trackingId,
548
+ contentId,
549
+ engagementType,
550
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
551
+ })
552
+ }).catch((err) => {
553
+ if (this.config?.debug) {
554
+ console.error("AmplifyUp SDK: Failed to send engagement feedback:", err);
555
+ }
556
+ });
557
+ }
558
+ }
559
+ /**
560
+ * Reset user identity (logout)
561
+ */
562
+ reset() {
563
+ this.adapter.reset();
564
+ clearIds();
565
+ if (this.config?.debug) {
566
+ console.log("AmplifyUp SDK: User identity reset");
567
+ }
568
+ }
569
+ /**
570
+ * Get instance ID (anonymous ID)
571
+ */
572
+ getInstanceId() {
573
+ return getInstanceId();
574
+ }
575
+ /**
576
+ * Get user ID
577
+ */
578
+ getUserId() {
579
+ return getUserId();
580
+ }
581
+ /**
582
+ * Get tracking ID
583
+ */
584
+ getTrackingId() {
585
+ return this.config?.trackingId;
586
+ }
587
+ /**
588
+ * Get diagnostic information
589
+ * Useful for debugging from browser console
590
+ */
591
+ getDiagnostics() {
592
+ const instanceId = getInstanceId();
593
+ const userId = getUserId();
594
+ return {
595
+ initialized: this.initialized,
596
+ instanceId,
597
+ userId,
598
+ trackingId: this.config?.trackingId,
599
+ config: {
600
+ apiGatewayUrl: this.apiGatewayUrl,
601
+ decisionApiUrl: this.decisionApiUrl,
602
+ orchestratorUrl: this.orchestratorUrl,
603
+ debug: this.config?.debug
604
+ }
605
+ };
606
+ }
607
+ /**
608
+ * Test tracking by sending a test event
609
+ */
610
+ testTracking() {
611
+ if (!this.initialized) {
612
+ console.warn("AmplifyUp SDK: Not initialized. Cannot test tracking.");
613
+ return;
614
+ }
615
+ const testEvent = {
616
+ eventName: "SDK Test Event",
617
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
618
+ test: true
619
+ };
620
+ console.log("AmplifyUp SDK: Sending test event...", testEvent);
621
+ this.track("SDK Test Event", {
622
+ ...testEvent,
623
+ instanceId: getInstanceId(),
624
+ trackingId: this.config?.trackingId
625
+ });
626
+ console.log("AmplifyUp SDK: Test event sent. Check AWS API Gateway to verify it was received.");
627
+ console.log("AmplifyUp SDK: Check Network tab for requests to:", this.apiGatewayUrl);
628
+ }
629
+ /**
630
+ * Verify AWS API Gateway connectivity
631
+ * Checks if AWS adapter is initialized and can send events
632
+ */
633
+ verifyAWSConnection() {
634
+ if (!this.apiGatewayUrl) {
635
+ return {
636
+ connected: false,
637
+ message: "AWS API Gateway URL not configured"
638
+ };
639
+ }
640
+ if (!this.initialized) {
641
+ return {
642
+ connected: false,
643
+ message: "SDK not initialized"
644
+ };
645
+ }
646
+ return {
647
+ connected: true,
648
+ message: "AWS adapter is connected and ready",
649
+ details: {
650
+ apiGatewayUrl: this.apiGatewayUrl,
651
+ decisionApiUrl: this.decisionApiUrl
652
+ }
653
+ };
654
+ }
655
+ /**
656
+ * Get status summary (for console debugging)
657
+ */
658
+ getStatus() {
659
+ const diag = this.getDiagnostics();
660
+ return `
661
+ AmplifyUp SDK Status:
662
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
663
+ Initialized: ${diag.initialized ? "\u2705 Yes" : "\u274C No"}
664
+ Instance ID: ${diag.instanceId}
665
+ User ID: ${diag.userId || "Not identified"}
666
+ Tracking ID: ${diag.trackingId || "Not set"}
667
+
668
+ Adapter:
669
+ Type: AWS (Pure AWS Architecture)
670
+
671
+ Config:
672
+ API Gateway: ${this.apiGatewayUrl || "Not set (required)"}
673
+ Decision API: ${this.decisionApiUrl || "Not set (using Orchestrator fallback)"}
674
+ Orchestrator: ${this.orchestratorUrl || "Not set"}
675
+ Debug: ${diag.config.debug ? "Enabled" : "Disabled"}
676
+
677
+ Available Console Commands:
678
+ AmplifyUp.getStatus() - Show this status
679
+ AmplifyUp.getDiagnostics() - Get full diagnostic object
680
+ AmplifyUp.getInstanceId() - Get instance ID (browser/session ID)
681
+ AmplifyUp.getUserId() - Get user ID if identified
682
+ AmplifyUp.testTracking() - Send a test event
683
+ AmplifyUp.verifyAWSConnection() - Verify AWS API Gateway connectivity
684
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
685
+ `.trim();
686
+ }
687
+ /**
688
+ * Get orchestrator URL
689
+ */
690
+ getOrchestratorUrl() {
691
+ return this.orchestratorUrl || void 0;
692
+ }
693
+ /**
694
+ * Get decision API URL
695
+ */
696
+ getDecisionApiUrl() {
697
+ return this.decisionApiUrl || void 0;
698
+ }
699
+ /**
700
+ * Build API URL for fetching page config from the Layout Provider
701
+ */
702
+ buildLayoutProviderUrl(route, preview = false) {
703
+ const orchestratorUrl = this.getOrchestratorUrl();
704
+ const trackingId = this.getTrackingId();
705
+ const instanceId = getInstanceId();
706
+ if (!orchestratorUrl || !trackingId) {
707
+ throw new Error("SDK not initialized. Call init() first.");
708
+ }
709
+ const isOnOrchestratorOrigin = typeof window !== "undefined" && window.location.origin === new URL(orchestratorUrl).origin;
710
+ const params = new URLSearchParams({
711
+ route,
712
+ trackingId,
713
+ instanceId
714
+ });
715
+ if (preview) {
716
+ params.set("preview", "true");
717
+ }
718
+ if (isOnOrchestratorOrigin) {
719
+ return `/api/layout/provider?${params.toString()}`;
720
+ }
721
+ return `${orchestratorUrl}/api/layout/provider?${params.toString()}`;
722
+ }
723
+ /**
724
+ * Fetch page configuration from orchestrator's Layout Provider
725
+ */
726
+ async fetchPageConfig(route, preview = false) {
727
+ const url = this.buildLayoutProviderUrl(route, preview);
728
+ const response = await fetch(url);
729
+ if (!response.ok) {
730
+ if (response.status === 404) {
731
+ return null;
732
+ }
733
+ throw new Error(`Failed to fetch page: ${response.statusText}`);
734
+ }
735
+ const data = await response.json();
736
+ const pageConfig = data.page;
737
+ if (pageConfig?.eventTracking) {
738
+ this.setupEventTracking(pageConfig.eventTracking);
739
+ }
740
+ return pageConfig;
741
+ }
742
+ /**
743
+ * Set up view-level event tracking based on configuration
744
+ */
745
+ setupEventTracking(eventConfig) {
746
+ if (typeof document === "undefined") return;
747
+ this.cleanupEventTracking();
748
+ this.eventTrackingConfig = eventConfig;
749
+ if (this.config?.debug) {
750
+ console.log("[AmplifyUp SDK] Setting up event tracking:", eventConfig.length, "events");
751
+ }
752
+ if (document.readyState === "loading") {
753
+ document.addEventListener("DOMContentLoaded", () => this.attachEventListeners());
754
+ } else {
755
+ setTimeout(() => this.attachEventListeners(), 100);
756
+ }
757
+ }
758
+ /**
759
+ * Attach event listeners to page elements based on view-level configuration
760
+ */
761
+ attachEventListeners() {
762
+ if (typeof document === "undefined") return;
763
+ this.eventTrackingConfig.forEach((eventConfig) => {
764
+ if (eventConfig.enabled === false) return;
765
+ let targetElements = [];
766
+ if (eventConfig.selector) {
767
+ try {
768
+ targetElements = document.querySelectorAll(eventConfig.selector);
769
+ } catch (e) {
770
+ if (this.config?.debug) {
771
+ console.warn(`[AmplifyUp SDK] Invalid selector: ${eventConfig.selector}`, e);
772
+ }
773
+ return;
774
+ }
775
+ } else {
776
+ if (this.config?.debug) {
777
+ console.warn(`[AmplifyUp SDK] Event ${eventConfig.eventName} has no selector, skipping`);
778
+ }
779
+ return;
780
+ }
781
+ if (targetElements.length === 0) {
782
+ if (this.config?.debug) {
783
+ console.log(`[AmplifyUp SDK] No elements found for selector: ${eventConfig.selector}`);
784
+ }
785
+ return;
786
+ }
787
+ targetElements.forEach((targetEl) => {
788
+ const htmlTargetEl = targetEl;
789
+ const domEventType = this.mapEventType(eventConfig.eventType);
790
+ const handler = (e) => {
791
+ this.handleTrackedEvent(e, eventConfig, htmlTargetEl);
792
+ };
793
+ if (eventConfig.eventType === "view") {
794
+ this.setupViewTracking(htmlTargetEl, eventConfig);
795
+ } else {
796
+ htmlTargetEl.addEventListener(domEventType, handler);
797
+ this.attachedEventListeners.push({
798
+ element: htmlTargetEl,
799
+ eventType: domEventType,
800
+ handler
801
+ });
802
+ }
803
+ if (this.config?.debug) {
804
+ console.log(`[AmplifyUp SDK] Attached ${eventConfig.eventType} listener for ${eventConfig.eventName} on selector: ${eventConfig.selector}`);
805
+ }
806
+ });
807
+ });
808
+ }
809
+ /**
810
+ * Map event type from config to DOM event type
811
+ */
812
+ mapEventType(eventType) {
813
+ const eventMap = {
814
+ "click": "click",
815
+ "submit": "submit",
816
+ "focus": "focus",
817
+ "blur": "blur",
818
+ "hover": "mouseenter",
819
+ "change": "change",
820
+ "scroll": "scroll",
821
+ "view": "view",
822
+ // Handled separately with IntersectionObserver
823
+ "custom": "custom"
824
+ };
825
+ return eventMap[eventType] || eventType;
826
+ }
827
+ /**
828
+ * Handle a tracked event and send to AWS API Gateway
829
+ */
830
+ handleTrackedEvent(event, config, element) {
831
+ const properties = {
832
+ eventType: config.eventType,
833
+ selector: config.selector
834
+ };
835
+ if (config.properties && config.properties.length > 0) {
836
+ config.properties.forEach((prop) => {
837
+ const dataAttrValue = element.getAttribute(`data-${prop}`);
838
+ if (dataAttrValue) {
839
+ properties[prop] = dataAttrValue;
840
+ return;
841
+ }
842
+ if (prop in element) {
843
+ const value = element[prop];
844
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
845
+ properties[prop] = value;
846
+ }
847
+ }
848
+ switch (prop) {
849
+ case "buttonText":
850
+ case "text":
851
+ properties[prop] = element.textContent?.trim().substring(0, 100);
852
+ break;
853
+ case "linkUrl":
854
+ case "href":
855
+ properties[prop] = element.getAttribute("href");
856
+ break;
857
+ case "formId":
858
+ case "id":
859
+ properties[prop] = element.id || element.getAttribute("name");
860
+ break;
861
+ case "value":
862
+ properties[prop] = element.value;
863
+ break;
864
+ case "tagName":
865
+ properties[prop] = element.tagName.toLowerCase();
866
+ break;
867
+ case "className":
868
+ properties[prop] = element.className;
869
+ break;
870
+ }
871
+ });
872
+ }
873
+ this.track(config.eventName, properties);
874
+ if (this.config?.debug) {
875
+ console.log(`[AmplifyUp SDK] Tracked event: ${config.eventName}`, properties);
876
+ }
877
+ }
878
+ /**
879
+ * Set up view tracking using IntersectionObserver
880
+ */
881
+ setupViewTracking(element, config) {
882
+ if (typeof IntersectionObserver === "undefined") return;
883
+ const observer = new IntersectionObserver(
884
+ (entries) => {
885
+ entries.forEach((entry) => {
886
+ if (entry.isIntersecting) {
887
+ this.handleTrackedEvent(
888
+ new CustomEvent("view"),
889
+ config,
890
+ element
891
+ );
892
+ observer.unobserve(element);
893
+ }
894
+ });
895
+ },
896
+ {
897
+ threshold: 0.5
898
+ // 50% visible
899
+ }
900
+ );
901
+ observer.observe(element);
902
+ element._amplifyupViewObserver = observer;
903
+ }
904
+ /**
905
+ * Clean up all attached event listeners
906
+ */
907
+ cleanupEventTracking() {
908
+ this.attachedEventListeners.forEach(({ element, eventType, handler }) => {
909
+ element.removeEventListener(eventType, handler);
910
+ });
911
+ this.attachedEventListeners = [];
912
+ if (typeof document !== "undefined") {
913
+ document.querySelectorAll("*").forEach((el) => {
914
+ const observer = el._amplifyupViewObserver;
915
+ if (observer) {
916
+ observer.disconnect();
917
+ delete el._amplifyupViewObserver;
918
+ }
919
+ });
920
+ }
921
+ this.eventTrackingConfig = [];
922
+ if (this.config?.debug) {
923
+ console.log("[AmplifyUp SDK] Cleaned up event tracking");
924
+ }
925
+ }
926
+ /**
927
+ * Manually trigger event tracking setup (useful after dynamic content loads)
928
+ */
929
+ refreshEventTracking() {
930
+ if (this.eventTrackingConfig.length > 0) {
931
+ this.cleanupEventTracking();
932
+ const config = [...this.eventTrackingConfig];
933
+ this.eventTrackingConfig = config;
934
+ this.attachEventListeners();
935
+ }
936
+ }
937
+ /**
938
+ * Set up postMessage listener for highlighting commands from orchestrator
939
+ */
940
+ setupHighlightingListener() {
941
+ if (this.messageListener) {
942
+ console.log("[AmplifyUp SDK] Message listener already set up");
943
+ return;
944
+ }
945
+ console.log("[AmplifyUp SDK] Setting up message listener...");
946
+ this.messageListener = (event) => {
947
+ console.log("[AmplifyUp SDK] Received message:", {
948
+ origin: event.origin,
949
+ currentOrigin: window.location.origin,
950
+ data: event.data
951
+ });
952
+ const allowedOrigins = [
953
+ "http://localhost:5001",
954
+ "http://localhost:3001",
955
+ "https://orchestrator.amplifyup.com"
956
+ ];
957
+ const isSameOrigin = event.origin === window.location.origin;
958
+ const isAllowedOrigin = allowedOrigins.some((origin) => event.origin.startsWith(origin));
959
+ if (!isSameOrigin && !isAllowedOrigin) {
960
+ console.warn("[AmplifyUp SDK] Rejected message from unauthorized origin:", {
961
+ eventOrigin: event.origin,
962
+ currentOrigin: window.location.origin,
963
+ allowedOrigins
964
+ });
965
+ return;
966
+ }
967
+ try {
968
+ if (event.data?.type === "COMPONENT_DELETABLE_STATUS") {
969
+ const { componentId, deletable, nodeId } = event.data;
970
+ const element = document.querySelector(`[data-component-id="${componentId}"]`);
971
+ if (element) {
972
+ const deleteButton = element._deleteButton;
973
+ if (deleteButton) {
974
+ if (deletable && nodeId) {
975
+ deleteButton.disabled = false;
976
+ deleteButton.style.opacity = "1";
977
+ deleteButton.style.cursor = "pointer";
978
+ element.setAttribute("data-node-id", nodeId);
979
+ } else {
980
+ deleteButton.disabled = true;
981
+ deleteButton.style.opacity = "0.5";
982
+ deleteButton.style.cursor = "not-allowed";
983
+ }
984
+ }
985
+ }
986
+ return;
987
+ }
988
+ if (event.data?.type === "DELETE_COMPONENT_REQUEST") {
989
+ if (window.parent && window.parent !== window) {
990
+ window.parent.postMessage(event.data, "*");
991
+ }
992
+ return;
993
+ }
994
+ if (event.data?.type === "GET_ELEMENT_POSITION") {
995
+ const { componentId, slotName } = event.data;
996
+ const selector = slotName ? `[data-amplifyup-slot="${slotName}"]` : `[data-amplifyup-component="${componentId}"], [data-component-id="${componentId}"]`;
997
+ console.log("[AmplifyUp SDK] Finding element position:", { selector, componentId, slotName });
998
+ const element = document.querySelector(selector);
999
+ if (element) {
1000
+ const rect = element.getBoundingClientRect();
1001
+ const position = {
1002
+ x: rect.left + rect.width / 2,
1003
+ // Center of element
1004
+ y: rect.top - 50,
1005
+ // Position above element
1006
+ width: rect.width,
1007
+ height: rect.height
1008
+ };
1009
+ console.log("[AmplifyUp SDK] Element position calculated:", { rect, position });
1010
+ if (window.parent && window.parent !== window) {
1011
+ window.parent.postMessage({
1012
+ type: "ELEMENT_POSITION",
1013
+ componentId,
1014
+ slotName,
1015
+ position
1016
+ }, "*");
1017
+ console.log("[AmplifyUp SDK] Sent ELEMENT_POSITION message");
1018
+ }
1019
+ } else {
1020
+ console.warn("[AmplifyUp SDK] Element not found for selector:", selector);
1021
+ }
1022
+ return;
1023
+ }
1024
+ const message = event.data;
1025
+ if (!message || typeof message !== "object" || !message.type) {
1026
+ console.log("[AmplifyUp SDK] Message is not a highlighting message:", message);
1027
+ return;
1028
+ }
1029
+ console.log("[AmplifyUp SDK] Processing highlight message:", message.type);
1030
+ switch (message.type) {
1031
+ case "ENABLE_HIGHLIGHTING":
1032
+ case "UPDATE_HIGHLIGHTING":
1033
+ console.log("[AmplifyUp SDK] Enabling highlighting with registry:", message.componentRegistry?.length || 0, "components");
1034
+ this.enableHighlighting(
1035
+ message.componentRegistry || [],
1036
+ message.highlightComponents ?? true,
1037
+ message.showZones ?? false
1038
+ );
1039
+ break;
1040
+ case "DISABLE_HIGHLIGHTING":
1041
+ console.log("[AmplifyUp SDK] Disabling highlighting");
1042
+ this.disableHighlighting();
1043
+ break;
1044
+ case "SHOW_COMPONENT_TOOLTIP":
1045
+ case "HIDE_ALL_TOOLTIPS":
1046
+ break;
1047
+ case "ENABLE_EVENT_SELECTION":
1048
+ console.log("[AmplifyUp SDK] Enable event selection mode:", message.enabled);
1049
+ this.setEventSelectionMode(message.enabled || false);
1050
+ break;
1051
+ }
1052
+ } catch (error) {
1053
+ console.error("[AmplifyUp SDK] Error handling highlight message:", error);
1054
+ }
1055
+ };
1056
+ window.addEventListener("message", this.messageListener);
1057
+ console.log("[AmplifyUp SDK] Message listener attached to window");
1058
+ }
1059
+ /**
1060
+ * Enable/disable event selection mode
1061
+ * When enabled, clicks on elements send element info to parent for event configuration
1062
+ */
1063
+ setEventSelectionMode(enabled) {
1064
+ if (typeof document === "undefined") return;
1065
+ this.eventSelectionMode = enabled;
1066
+ if (this.eventSelectionClickHandler) {
1067
+ document.removeEventListener("click", this.eventSelectionClickHandler, true);
1068
+ this.eventSelectionClickHandler = null;
1069
+ }
1070
+ if (enabled) {
1071
+ this.eventSelectionClickHandler = (e) => {
1072
+ const target = e.target;
1073
+ if (!target) return;
1074
+ const selector = this.generateSelectorForElement(target);
1075
+ const tagName = target.tagName.toLowerCase();
1076
+ const text = target.textContent?.trim().substring(0, 50) || "";
1077
+ if (window.parent && window.parent !== window) {
1078
+ window.parent.postMessage({
1079
+ type: "ELEMENT_CLICKED_FOR_EVENT",
1080
+ selector,
1081
+ tagName,
1082
+ text
1083
+ }, "*");
1084
+ }
1085
+ if (tagName === "a") {
1086
+ e.preventDefault();
1087
+ }
1088
+ };
1089
+ document.addEventListener("click", this.eventSelectionClickHandler, true);
1090
+ if (this.config?.debug) {
1091
+ console.log("[AmplifyUp SDK] Event selection mode enabled - clicks will be captured");
1092
+ }
1093
+ } else {
1094
+ if (this.config?.debug) {
1095
+ console.log("[AmplifyUp SDK] Event selection mode disabled");
1096
+ }
1097
+ }
1098
+ }
1099
+ /**
1100
+ * Generate a CSS selector for an element
1101
+ */
1102
+ generateSelectorForElement(el) {
1103
+ if (el.id) {
1104
+ return "#" + el.id;
1105
+ }
1106
+ const dataAttrs = Array.from(el.attributes).filter((a) => a.name.startsWith("data-"));
1107
+ if (dataAttrs.length > 0) {
1108
+ const attr = dataAttrs[0];
1109
+ return "[" + attr.name + '="' + attr.value + '"]';
1110
+ }
1111
+ if (el.className && typeof el.className === "string") {
1112
+ const classes = el.className.split(" ").filter(
1113
+ (c) => c && !c.startsWith("has-") && !c.includes("selectable") && !c.includes("selected")
1114
+ );
1115
+ if (classes.length > 0) {
1116
+ return el.tagName.toLowerCase() + "." + classes[0];
1117
+ }
1118
+ }
1119
+ if (el.type) {
1120
+ return el.tagName.toLowerCase() + '[type="' + el.type + '"]';
1121
+ }
1122
+ if (el.name) {
1123
+ return el.tagName.toLowerCase() + '[name="' + el.name + '"]';
1124
+ }
1125
+ return el.tagName.toLowerCase();
1126
+ }
1127
+ /**
1128
+ * Enable component highlighting
1129
+ */
1130
+ enableHighlighting(componentRegistry, highlightComponents, showZones) {
1131
+ if (typeof document === "undefined") return;
1132
+ this.componentRegistry = componentRegistry;
1133
+ this.highlightingEnabled = true;
1134
+ this.disableHighlighting();
1135
+ if (highlightComponents) {
1136
+ this.applyComponentHighlights();
1137
+ this.setupMutationObserver();
1138
+ }
1139
+ }
1140
+ /**
1141
+ * Set up MutationObserver to watch for new components being added
1142
+ */
1143
+ setupMutationObserver() {
1144
+ if (typeof document === "undefined" || !window.MutationObserver) return;
1145
+ if (this.mutationObserver) {
1146
+ this.mutationObserver.disconnect();
1147
+ this.mutationObserver = null;
1148
+ }
1149
+ this.mutationObserver = new MutationObserver((mutations) => {
1150
+ let shouldReapply = false;
1151
+ mutations.forEach((mutation) => {
1152
+ mutation.addedNodes.forEach((node) => {
1153
+ if (node.nodeType === Node.ELEMENT_NODE) {
1154
+ const el = node;
1155
+ if (el.hasAttribute("data-amplifyup-component") || el.hasAttribute("data-component-id") || el.hasAttribute("data-amplifyup-slot") || el.querySelector("[data-amplifyup-component], [data-component-id], [data-amplifyup-slot]")) {
1156
+ shouldReapply = true;
1157
+ }
1158
+ }
1159
+ });
1160
+ if (mutation.type === "attributes") {
1161
+ const target = mutation.target;
1162
+ if (target.hasAttribute("data-amplifyup-slot") || target.hasAttribute("data-slot-empty")) {
1163
+ shouldReapply = true;
1164
+ }
1165
+ }
1166
+ });
1167
+ if (shouldReapply && this.highlightingEnabled) {
1168
+ setTimeout(() => {
1169
+ if (this.highlightingEnabled) {
1170
+ this.applyComponentHighlights();
1171
+ }
1172
+ }, 100);
1173
+ }
1174
+ });
1175
+ this.mutationObserver.observe(document.body, {
1176
+ childList: true,
1177
+ subtree: true,
1178
+ attributes: true,
1179
+ attributeFilter: ["data-amplifyup-component", "data-component-id", "data-amplifyup-slot", "data-slot-empty"]
1180
+ });
1181
+ }
1182
+ /**
1183
+ * Disable component highlighting
1184
+ */
1185
+ disableHighlighting() {
1186
+ if (typeof document === "undefined") return;
1187
+ this.highlightingEnabled = false;
1188
+ if (this.mutationObserver) {
1189
+ this.mutationObserver.disconnect();
1190
+ this.mutationObserver = null;
1191
+ }
1192
+ document.querySelectorAll(".amplifyup-component-highlight").forEach((el) => {
1193
+ const htmlEl = el;
1194
+ const handlers = htmlEl._amplifyupHandlers;
1195
+ if (handlers?.handleClick) {
1196
+ htmlEl.removeEventListener("click", handlers.handleClick);
1197
+ }
1198
+ delete htmlEl._amplifyupHandlers;
1199
+ htmlEl.classList.remove("amplifyup-component-highlight");
1200
+ });
1201
+ document.querySelectorAll(".amplifyup-slot-highlight").forEach((el) => {
1202
+ const htmlEl = el;
1203
+ const handlers = htmlEl._amplifyupSlotHandlers;
1204
+ if (handlers) {
1205
+ if (handlers.handleClick) htmlEl.removeEventListener("click", handlers.handleClick);
1206
+ if (handlers.handleDragOver) htmlEl.removeEventListener("dragover", handlers.handleDragOver);
1207
+ if (handlers.handleDrop) htmlEl.removeEventListener("drop", handlers.handleDrop);
1208
+ }
1209
+ delete htmlEl._amplifyupSlotHandlers;
1210
+ htmlEl.classList.remove("amplifyup-slot-highlight");
1211
+ });
1212
+ }
1213
+ /**
1214
+ * Build component hierarchy for ancestor selection
1215
+ */
1216
+ buildComponentHierarchy(element) {
1217
+ const hierarchy = [];
1218
+ let current = element;
1219
+ while (current) {
1220
+ if (current.hasAttribute("data-amplifyup-component") || current.hasAttribute("data-component-id") || current.hasAttribute("data-amplifyup-slot")) {
1221
+ hierarchy.push(current);
1222
+ }
1223
+ current = current.parentElement;
1224
+ }
1225
+ return hierarchy.reverse();
1226
+ }
1227
+ /**
1228
+ * Send selection message to orchestrator (includes element bounds for overlay positioning).
1229
+ */
1230
+ sendSelectionMessage(element, hierarchy, isEmpty) {
1231
+ if (!this.isVisualEditorMode) return;
1232
+ const componentId = element.getAttribute("data-amplifyup-component") || element.getAttribute("data-component-id");
1233
+ const slotName = element.getAttribute("data-amplifyup-slot");
1234
+ const componentName = element.getAttribute("data-component-name");
1235
+ const hierarchyInfo = hierarchy.map((el, index) => {
1236
+ const elComponentId = el.getAttribute("data-amplifyup-component") || el.getAttribute("data-component-id");
1237
+ const elSlotName = el.getAttribute("data-amplifyup-slot");
1238
+ const elComponentName = el.getAttribute("data-component-name");
1239
+ return {
1240
+ index,
1241
+ componentId: elComponentId,
1242
+ componentName: elComponentName,
1243
+ slotName: elSlotName,
1244
+ isSlot: !!elSlotName
1245
+ };
1246
+ });
1247
+ const rect = element.getBoundingClientRect ? element.getBoundingClientRect() : null;
1248
+ const bounds = rect ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height } : void 0;
1249
+ const message = {
1250
+ type: "COMPONENT_SELECTED",
1251
+ componentId,
1252
+ componentName,
1253
+ slotName,
1254
+ hierarchy: hierarchyInfo,
1255
+ selectedIndex: hierarchy.length - 1,
1256
+ ...bounds && { bounds },
1257
+ ...isEmpty !== void 0 && { isEmpty }
1258
+ };
1259
+ if (window.parent && window.parent !== window) {
1260
+ window.parent.postMessage(message, "*");
1261
+ console.log("[AmplifyUp SDK] Sent selection message:", message);
1262
+ }
1263
+ }
1264
+ /**
1265
+ * Send component tree to orchestrator
1266
+ * Note: Component tree is now sent from AmplifyRenderer based on layoutTree from pageConfig
1267
+ * This method is kept for backwards compatibility but is no longer used
1268
+ */
1269
+ sendComponentTree() {
1270
+ console.log("[AmplifyUp SDK] sendComponentTree called but component tree is now sent from AmplifyRenderer");
1271
+ }
1272
+ /**
1273
+ * Apply component highlights
1274
+ */
1275
+ applyComponentHighlights() {
1276
+ if (typeof document === "undefined") return;
1277
+ if (!this.isVisualEditorMode) {
1278
+ return;
1279
+ }
1280
+ const doc = document;
1281
+ const win = window;
1282
+ const componentElements = doc.querySelectorAll("[data-amplifyup-component], [data-component-id]");
1283
+ console.log("[AmplifyUp SDK] Found", componentElements.length, "components to highlight");
1284
+ this.sendComponentTree();
1285
+ const slotElements = doc.querySelectorAll("[data-amplifyup-slot]");
1286
+ console.log("[AmplifyUp SDK] Found", slotElements.length, "slots to highlight");
1287
+ componentElements.forEach((el) => {
1288
+ const htmlEl = el;
1289
+ const componentId = htmlEl.getAttribute("data-amplifyup-component") || htmlEl.getAttribute("data-component-id");
1290
+ if (!componentId) return;
1291
+ if (htmlEl.classList.contains("amplifyup-component-highlight")) return;
1292
+ const component = this.componentRegistry.find((c) => c.component_id === componentId);
1293
+ htmlEl.classList.add("amplifyup-component-highlight");
1294
+ htmlEl.setAttribute("data-component-id", componentId);
1295
+ htmlEl.setAttribute("data-component-name", component?.component_name || componentId);
1296
+ const hierarchy = this.buildComponentHierarchy(htmlEl);
1297
+ const handleClick = (e) => {
1298
+ e.stopPropagation();
1299
+ this.sendSelectionMessage(htmlEl, hierarchy);
1300
+ if (window.parent && window.parent !== window) {
1301
+ window.parent.postMessage({ type: "CHECK_COMPONENT_DELETABLE", componentId }, "*");
1302
+ }
1303
+ };
1304
+ htmlEl.addEventListener("click", handleClick);
1305
+ htmlEl._amplifyupHandlers = { handleClick };
1306
+ });
1307
+ this.componentRegistry.forEach((component) => {
1308
+ if (!component.data_attribute) return;
1309
+ const elements = doc.querySelectorAll(`[${component.data_attribute}]`);
1310
+ elements.forEach((el) => {
1311
+ const htmlEl = el;
1312
+ if (htmlEl.classList.contains("amplifyup-component-highlight")) return;
1313
+ htmlEl.classList.add("amplifyup-component-highlight");
1314
+ htmlEl.setAttribute("data-component-id", component.component_id);
1315
+ htmlEl.setAttribute("data-component-name", component.component_name);
1316
+ const hierarchy = this.buildComponentHierarchy(htmlEl);
1317
+ const handleClick = (e) => {
1318
+ e.stopPropagation();
1319
+ this.sendSelectionMessage(htmlEl, hierarchy);
1320
+ if (window.parent && window.parent !== window) {
1321
+ window.parent.postMessage({ type: "CHECK_COMPONENT_DELETABLE", componentId: component.component_id }, "*");
1322
+ }
1323
+ };
1324
+ htmlEl.addEventListener("click", handleClick);
1325
+ htmlEl._amplifyupHandlers = { handleClick };
1326
+ });
1327
+ });
1328
+ slotElements.forEach((el) => {
1329
+ const htmlEl = el;
1330
+ const slotName = htmlEl.getAttribute("data-amplifyup-slot");
1331
+ const isEmpty = htmlEl.getAttribute("data-slot-empty") === "true";
1332
+ if (!slotName) return;
1333
+ if (htmlEl.classList.contains("amplifyup-slot-highlight")) return;
1334
+ htmlEl.classList.add("amplifyup-slot-highlight");
1335
+ const hierarchy = this.buildComponentHierarchy(htmlEl);
1336
+ const handleClick = (e) => {
1337
+ e.stopPropagation();
1338
+ this.sendSelectionMessage(htmlEl, hierarchy, isEmpty);
1339
+ };
1340
+ const handleDrop = (e) => {
1341
+ e.preventDefault();
1342
+ e.stopPropagation();
1343
+ try {
1344
+ const data = JSON.parse(e.dataTransfer.getData("application/json"));
1345
+ if (data.type === "component" && win.parent && win.parent !== win) {
1346
+ win.parent.postMessage({
1347
+ type: "COMPONENT_DROPPED",
1348
+ componentId: data.componentId,
1349
+ componentName: data.componentName,
1350
+ slotName,
1351
+ slotElement: { path: htmlEl.getAttribute("data-slot-path"), isEmpty }
1352
+ }, "*");
1353
+ }
1354
+ } catch (_err) {
1355
+ }
1356
+ };
1357
+ const handleDragOver = (e) => {
1358
+ e.preventDefault();
1359
+ e.stopPropagation();
1360
+ e.dataTransfer.dropEffect = "copy";
1361
+ };
1362
+ htmlEl.addEventListener("click", handleClick);
1363
+ htmlEl.addEventListener("dragover", handleDragOver);
1364
+ htmlEl.addEventListener("drop", handleDrop);
1365
+ htmlEl._amplifyupSlotHandlers = { handleClick, handleDragOver, handleDrop };
1366
+ });
1367
+ }
1368
+ /**
1369
+ * Cleanup: Remove message listener and observers
1370
+ */
1371
+ cleanup() {
1372
+ if (this.messageListener) {
1373
+ window.removeEventListener("message", this.messageListener);
1374
+ this.messageListener = null;
1375
+ }
1376
+ this.disableHighlighting();
1377
+ }
1378
+ };
1379
+ var sdkInstance = null;
1380
+ function getSDK(adapter) {
1381
+ if (!sdkInstance) {
1382
+ sdkInstance = new AmplifyUpSDK(adapter);
1383
+ }
1384
+ return sdkInstance;
1385
+ }
1386
+ var trackContentView = (contentId, contentType, properties) => getSDK().trackContentView(contentId, contentType, properties);
1387
+ getSDK();
1388
+ function AmplifyRenderer({
1389
+ renderComponent,
1390
+ pageConfig: serverPageConfig,
1391
+ loadingComponent,
1392
+ errorComponent,
1393
+ emptyComponent
1394
+ }) {
1395
+ const clientHook = useAmplifyUp();
1396
+ const pageConfig = serverPageConfig || clientHook.pageConfig || null;
1397
+ const loading = serverPageConfig ? false : clientHook.loading;
1398
+ const error = serverPageConfig ? null : clientHook.error;
1399
+ const [variantCache, setVariantCache] = react.useState({});
1400
+ react.useEffect(() => {
1401
+ if (typeof window !== "undefined") {
1402
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
1403
+ return;
1404
+ }
1405
+ if (window.location.pathname.includes("/api/pages/proxy")) {
1406
+ return;
1407
+ }
1408
+ if (window.self !== window.top) {
1409
+ return;
1410
+ }
1411
+ const urlParams = new URLSearchParams(window.location.search);
1412
+ if (urlParams.get("preview") === "true" || urlParams.get("visualEditor") === "true") {
1413
+ return;
1414
+ }
1415
+ if (window.location.href.includes("preview=true") || window.location.href.includes("visualEditor=true")) {
1416
+ return;
1417
+ }
1418
+ }
1419
+ if (pageConfig && pageConfig.id !== "default") {
1420
+ trackContentView(pageConfig.id, "page", {
1421
+ route: pageConfig.route,
1422
+ title: pageConfig.title
1423
+ });
1424
+ }
1425
+ }, [pageConfig]);
1426
+ react.useEffect(() => {
1427
+ const isVisualEditorMode = () => {
1428
+ if (typeof window === "undefined") return false;
1429
+ const urlParams = new URLSearchParams(window.location.search);
1430
+ if (urlParams.get("visualEditor") === "true" || urlParams.get("preview") === "true") {
1431
+ return true;
1432
+ }
1433
+ const inIframe = window.self !== window.top;
1434
+ if (!inIframe) return false;
1435
+ try {
1436
+ if (document.referrer) {
1437
+ const referrerUrl = new URL(document.referrer);
1438
+ if (referrerUrl.hostname === "localhost" && referrerUrl.port === "5001") {
1439
+ return true;
1440
+ }
1441
+ }
1442
+ } catch (e) {
1443
+ }
1444
+ return false;
1445
+ };
1446
+ if (!isVisualEditorMode()) {
1447
+ return;
1448
+ }
1449
+ if (!pageConfig) {
1450
+ return;
1451
+ }
1452
+ if (!pageConfig.layoutTree || Array.isArray(pageConfig.layoutTree) && pageConfig.layoutTree.length === 0) {
1453
+ if (typeof window !== "undefined" && window.parent && window.parent !== window) {
1454
+ window.parent.postMessage({
1455
+ type: "COMPONENT_TREE",
1456
+ tree: []
1457
+ }, "*");
1458
+ }
1459
+ return;
1460
+ }
1461
+ const convertLayoutTreeToComponentTree = (nodes, parentId = null) => {
1462
+ const result = [];
1463
+ nodes.forEach((node) => {
1464
+ const treeNode = {
1465
+ id: node.id,
1466
+ componentId: node.componentId,
1467
+ componentName: node.componentId,
1468
+ // Will be enhanced with actual name if available
1469
+ parentId,
1470
+ children: []
1471
+ };
1472
+ if (node.slots && node.slots.length > 0) {
1473
+ node.slots.forEach((slot) => {
1474
+ if (slot.children && slot.children.length > 0) {
1475
+ const slotChildren = convertLayoutTreeToComponentTree(slot.children, node.id);
1476
+ treeNode.children.push(...slotChildren);
1477
+ }
1478
+ });
1479
+ }
1480
+ result.push(treeNode);
1481
+ });
1482
+ return result;
1483
+ };
1484
+ const layoutTree2 = pageConfig.layoutTree;
1485
+ const componentTree = convertLayoutTreeToComponentTree(layoutTree2);
1486
+ const describeLayoutTree = (nodes, depth = 0) => {
1487
+ if (!nodes?.length) return "";
1488
+ const indent = " ".repeat(depth);
1489
+ return nodes.map((n) => {
1490
+ const slotsDesc = (n.slots || []).map((s) => {
1491
+ const childCount = Array.isArray(s.children) ? s.children.length : 0;
1492
+ const childrenDesc = childCount > 0 ? describeLayoutTree(s.children || [], depth + 1) : "";
1493
+ return `${indent} slot "${s.slotId || s.slotName || "?"}" (${childCount} children)${childrenDesc ? "\n" + childrenDesc : ""}`;
1494
+ }).join("\n");
1495
+ return `${indent}- ${n.id} (${n.componentId || "?"})${slotsDesc ? "\n" + slotsDesc : ""}`;
1496
+ }).join("\n");
1497
+ };
1498
+ console.log("[AmplifyRenderer] layoutTree received:\n%s", describeLayoutTree(layoutTree2));
1499
+ if (typeof window !== "undefined" && window.parent && window.parent !== window) {
1500
+ const message = {
1501
+ type: "COMPONENT_TREE",
1502
+ tree: componentTree
1503
+ };
1504
+ window.parent.postMessage(message, "*");
1505
+ console.log("[AmplifyRenderer] Sent component tree from layoutTree:", componentTree.length, "root components");
1506
+ }
1507
+ }, [pageConfig]);
1508
+ react.useEffect(() => {
1509
+ if (!pageConfig?.layoutTree || !pageConfig.layoutId) return;
1510
+ const sdk = getSDK();
1511
+ const instanceId = sdk.getInstanceId();
1512
+ const decisionApiUrl = sdk.getDecisionApiUrl();
1513
+ const orchestratorUrl = sdk.getOrchestratorUrl();
1514
+ if (!decisionApiUrl && !orchestratorUrl || !instanceId) return;
1515
+ const fetchVariants = async () => {
1516
+ const nodes = pageConfig.layoutTree;
1517
+ const newCache = {};
1518
+ const findZonedSlots = (nodeList) => {
1519
+ const results = [];
1520
+ for (const node of nodeList) {
1521
+ if (node.slots) {
1522
+ for (const slot of node.slots) {
1523
+ if (slot.zoneId && slot.variants) {
1524
+ const key = `${node.id}-${slot.slotId}`;
1525
+ results.push({ node, slot, key });
1526
+ }
1527
+ }
1528
+ }
1529
+ }
1530
+ return results;
1531
+ };
1532
+ const zonedSlots = findZonedSlots(nodes);
1533
+ await Promise.all(
1534
+ zonedSlots.map(async ({ node, slot, key }) => {
1535
+ try {
1536
+ let personalizationUrl;
1537
+ if (decisionApiUrl) {
1538
+ const url = new URL(decisionApiUrl);
1539
+ url.searchParams.set("instanceId", instanceId);
1540
+ const zoneId = slot.zoneId;
1541
+ if (zoneId) {
1542
+ url.searchParams.set("zoneId", zoneId);
1543
+ }
1544
+ const campaignId = slot.campaignId;
1545
+ if (campaignId) {
1546
+ url.searchParams.set("campaignId", campaignId);
1547
+ }
1548
+ personalizationUrl = url.toString();
1549
+ } else if (orchestratorUrl) {
1550
+ personalizationUrl = `${orchestratorUrl}/api/personalization/variant?instanceId=${encodeURIComponent(instanceId)}&layoutId=${encodeURIComponent(pageConfig.layoutId)}&slotId=${encodeURIComponent(slot.slotId)}&nodeId=${encodeURIComponent(node.id)}`;
1551
+ } else {
1552
+ return;
1553
+ }
1554
+ const response = await fetch(personalizationUrl);
1555
+ if (response.ok) {
1556
+ const data = await response.json();
1557
+ if (data.variantId && data.componentTree) {
1558
+ newCache[key] = {
1559
+ variantId: data.variantId,
1560
+ children: data.componentTree
1561
+ };
1562
+ if (typeof window !== "undefined") {
1563
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
1564
+ return;
1565
+ }
1566
+ if (window.location.pathname.includes("/api/pages/proxy")) {
1567
+ return;
1568
+ }
1569
+ if (window.self !== window.top) {
1570
+ return;
1571
+ }
1572
+ }
1573
+ sdk.track("Zone Impression", {
1574
+ zoneId: slot.zoneId,
1575
+ variantId: data.variantId,
1576
+ layoutId: pageConfig.layoutId,
1577
+ route: pageConfig.route
1578
+ });
1579
+ } else {
1580
+ const defaultVariant = slot.variants?.find((v) => v.isDefault);
1581
+ if (defaultVariant) {
1582
+ newCache[key] = {
1583
+ variantId: defaultVariant.variantId,
1584
+ children: defaultVariant.children
1585
+ };
1586
+ }
1587
+ }
1588
+ }
1589
+ } catch (err) {
1590
+ console.error(`Error fetching variant for slot ${key}:`, err);
1591
+ const defaultVariant = slot.variants?.find((v) => v.isDefault);
1592
+ if (defaultVariant) {
1593
+ newCache[key] = {
1594
+ variantId: defaultVariant.variantId,
1595
+ children: defaultVariant.children
1596
+ };
1597
+ }
1598
+ }
1599
+ })
1600
+ );
1601
+ setVariantCache(newCache);
1602
+ };
1603
+ fetchVariants();
1604
+ }, [pageConfig?.layoutTree, pageConfig?.layoutId, pageConfig?.route]);
1605
+ if (loading) {
1606
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
1607
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-gray-200 rounded w-1/4 mb-4" }),
1608
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-gray-200 rounded w-1/2 mb-8" }),
1609
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
1610
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-64 bg-gray-200 rounded" }),
1611
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-32 bg-gray-200 rounded" })
1612
+ ] })
1613
+ ] }) }) }) });
1614
+ }
1615
+ if (error) {
1616
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: errorComponent ? errorComponent(error) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-red-50 border border-red-200 rounded-lg p-6", children: [
1617
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold text-red-900 mb-2", children: "Error Loading Page" }),
1618
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-700", children: error })
1619
+ ] }) }) }) });
1620
+ }
1621
+ if (!pageConfig) {
1622
+ return null;
1623
+ }
1624
+ pageConfig.layout?.header !== false;
1625
+ pageConfig.layout?.footer !== false;
1626
+ const renderNode = (node) => {
1627
+ let slotChildren = {};
1628
+ console.log(`[AmplifyRenderer] Rendering node ${node.id} (${node.componentId})`, {
1629
+ hasSlots: !!(node.slots && node.slots.length > 0),
1630
+ slotCount: node.slots?.length || 0,
1631
+ slots: node.slots?.map((s) => ({ slotId: s.slotId, zoneId: s.zoneId, childrenCount: s.children?.length || 0 }))
1632
+ });
1633
+ if (node.slots && node.slots.length > 0) {
1634
+ for (const slot of node.slots) {
1635
+ const slotKey = `${node.id}-${slot.slotId}`;
1636
+ let childrenToRender = [];
1637
+ console.log(`[AmplifyRenderer] Processing slot ${slot.slotId} for node ${node.id}`, {
1638
+ slotKey,
1639
+ hasZoneId: !!slot.zoneId,
1640
+ hasVariants: !!slot.variants,
1641
+ hasVariantCache: !!variantCache[slotKey],
1642
+ directChildrenCount: slot.children?.length || 0
1643
+ });
1644
+ if (slot.zoneId && slot.variants && variantCache[slotKey]) {
1645
+ childrenToRender = variantCache[slotKey].children;
1646
+ console.log(`[AmplifyRenderer] Using cached variant for slot ${slot.slotId}`, {
1647
+ variantId: variantCache[slotKey].variantId,
1648
+ childrenCount: childrenToRender.length
1649
+ });
1650
+ } else if (slot.zoneId && slot.variants) {
1651
+ const defaultVariant = slot.variants.find((v) => v.isDefault);
1652
+ childrenToRender = defaultVariant?.children || [];
1653
+ console.log(`[AmplifyRenderer] Using default variant for slot ${slot.slotId}`, {
1654
+ childrenCount: childrenToRender.length
1655
+ });
1656
+ } else {
1657
+ childrenToRender = slot.children;
1658
+ console.log(`[AmplifyRenderer] Using direct children for slot ${slot.slotId}`, {
1659
+ childrenCount: childrenToRender.length
1660
+ });
1661
+ }
1662
+ slotChildren[slot.slotId] = /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot-id": slot.slotId, "data-zone-id": slot.zoneId || void 0, children: childrenToRender.map((child) => renderNode(child)) }, slot.slotId);
1663
+ console.log(`[AmplifyRenderer] Created slot content for ${slot.slotId}`, {
1664
+ slotId: slot.slotId,
1665
+ renderedChildrenCount: childrenToRender.length
1666
+ });
1667
+ }
1668
+ }
1669
+ const slotsToPass = Object.keys(slotChildren).length > 0 ? slotChildren : void 0;
1670
+ console.log(`[AmplifyRenderer] Calling renderComponent for ${node.componentId}`, {
1671
+ hasSlots: !!slotsToPass,
1672
+ slotKeys: slotsToPass ? Object.keys(slotsToPass) : []
1673
+ });
1674
+ return renderComponent(
1675
+ node.componentId,
1676
+ node.props || {},
1677
+ slotsToPass
1678
+ );
1679
+ };
1680
+ const layoutTree = pageConfig.layoutTree;
1681
+ if (!layoutTree || layoutTree.length === 0) {
1682
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { children: emptyComponent || /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No content configured for this page." }) }) });
1683
+ }
1684
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { children: layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderNode(node) }, node.id)) });
1685
+ }
1686
+
1687
+ // src/server.tsx
1688
+ async function fetchPageConfigServer(route, trackingId, preview = false) {
1689
+ const sdk = getSDK();
1690
+ const orchestratorUrl = typeof process !== "undefined" && process.env.NEXT_PUBLIC_ORCHESTRATOR_URL || "http://localhost:5001";
1691
+ const originalConfig = sdk.config;
1692
+ sdk.config = {
1693
+ trackingId,
1694
+ orchestratorUrl
1695
+ };
1696
+ try {
1697
+ const page = await sdk.fetchPageConfig(route, preview);
1698
+ return page;
1699
+ } finally {
1700
+ sdk.config = originalConfig;
1701
+ }
1702
+ }
1703
+ var ComponentContext = react.createContext(null);
1704
+ function ComponentContextProvider({
1705
+ children,
1706
+ slots,
1707
+ props
1708
+ }) {
1709
+ console.log("[ComponentContextProvider] Providing context", {
1710
+ hasSlots: !!slots,
1711
+ slotKeys: slots ? Object.keys(slots) : [],
1712
+ slotDetails: slots ? Object.keys(slots).map((key) => ({
1713
+ key,
1714
+ hasContent: !!slots[key],
1715
+ contentType: slots[key] ? typeof slots[key] : "null"
1716
+ })) : [],
1717
+ hasProps: !!props,
1718
+ propKeys: props ? Object.keys(props) : []
1719
+ });
1720
+ return /* @__PURE__ */ jsxRuntime.jsx(ComponentContext.Provider, { value: { slots, props }, children });
1721
+ }
1722
+ function Slot({
1723
+ name,
1724
+ className
1725
+ }) {
1726
+ const context = react.useContext(ComponentContext);
1727
+ console.log(`[Slot:${name}] Rendering slot`, {
1728
+ hasContext: !!context,
1729
+ availableSlotKeys: context?.slots ? Object.keys(context.slots) : [],
1730
+ slotContent: context?.slots?.[name] ? "EXISTS" : "MISSING",
1731
+ contextSlots: context?.slots
1732
+ });
1733
+ if (!context) {
1734
+ console.warn(`[AmplifyUp SDK] Slot "${name}" used outside ComponentContextProvider`);
1735
+ return null;
1736
+ }
1737
+ const slotContent = context.slots?.[name];
1738
+ const isEmpty = !slotContent;
1739
+ console.log(`[Slot:${name}] Slot state`, {
1740
+ isEmpty,
1741
+ hasSlotContent: !!slotContent,
1742
+ slotContentType: slotContent ? typeof slotContent : "null"
1743
+ });
1744
+ const isEditorMode = typeof window !== "undefined" && // Check for preview mode flag set by proxy
1745
+ (window.__AMPLIFYUP_PREVIEW_MODE__ === true || // Check URL query params
1746
+ new URLSearchParams(window.location.search).get("preview") === "true" || new URLSearchParams(window.location.search).get("visualEditor") === "true" || // Check if in iframe from orchestrator (check referrer hostname/port)
1747
+ window.self !== window.top && document.referrer && (() => {
1748
+ try {
1749
+ const referrerUrl = new URL(document.referrer);
1750
+ return referrerUrl.hostname === "localhost" && referrerUrl.port === "5001" || referrerUrl.pathname.includes("/dashboard/composer") || referrerUrl.pathname.includes("/dashboard/routes/");
1751
+ } catch {
1752
+ return false;
1753
+ }
1754
+ })());
1755
+ console.log(`[Slot:${name}] Editor mode detection`, {
1756
+ isEditorMode,
1757
+ isWindowDefined: typeof window !== "undefined",
1758
+ previewModeFlag: typeof window !== "undefined" ? window.__AMPLIFYUP_PREVIEW_MODE__ : void 0,
1759
+ urlParams: typeof window !== "undefined" ? {
1760
+ preview: new URLSearchParams(window.location.search).get("preview"),
1761
+ visualEditor: new URLSearchParams(window.location.search).get("visualEditor")
1762
+ } : void 0,
1763
+ inIframe: typeof window !== "undefined" ? window.self !== window.top : false,
1764
+ referrer: typeof window !== "undefined" && document.referrer ? document.referrer : void 0
1765
+ });
1766
+ return /* @__PURE__ */ jsxRuntime.jsx(
1767
+ "div",
1768
+ {
1769
+ className,
1770
+ "data-slot-name": name,
1771
+ "data-amplifyup-slot": name,
1772
+ "data-slot-empty": isEmpty ? "true" : void 0,
1773
+ style: {
1774
+ // Only add minimum height when empty (for drop target visibility)
1775
+ minHeight: isEmpty ? "60px" : void 0,
1776
+ // Only add position relative for overlay positioning in editor mode
1777
+ position: isEditorMode ? "relative" : void 0
1778
+ },
1779
+ children: isEmpty ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1780
+ // Only style the empty state indicator, not the container
1781
+ minHeight: "60px",
1782
+ color: "#9ca3af",
1783
+ fontSize: "14px",
1784
+ pointerEvents: "none"
1785
+ }, children: isEditorMode ? `[Slot: ${name}]` : `Empty slot: ${name}` }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1786
+ isEditorMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1787
+ position: "absolute",
1788
+ top: "4px",
1789
+ left: "4px",
1790
+ fontSize: "10px",
1791
+ color: "#9ca3af",
1792
+ pointerEvents: "none",
1793
+ zIndex: 1
1794
+ }, children: [
1795
+ "Slot: ",
1796
+ name
1797
+ ] }),
1798
+ slotContent
1799
+ ] })
1800
+ }
1801
+ );
1802
+ }
1803
+ function Field({
1804
+ name,
1805
+ fallback,
1806
+ className,
1807
+ render
1808
+ }) {
1809
+ const context = react.useContext(ComponentContext);
1810
+ if (!context) {
1811
+ console.warn(`[AmplifyUp SDK] Field "${name}" used outside ComponentContextProvider`);
1812
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
1813
+ }
1814
+ const fieldValue = context.props?.[name];
1815
+ if (fieldValue === void 0 || fieldValue === null) {
1816
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback }) : null;
1817
+ }
1818
+ if (render) {
1819
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: render(fieldValue) });
1820
+ }
1821
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: String(fieldValue) });
1822
+ }
1823
+ var usePathname = null;
1824
+ if (typeof window !== "undefined") {
1825
+ try {
1826
+ const nextNav = __require("next/navigation");
1827
+ if (nextNav.usePathname) {
1828
+ usePathname = nextNav.usePathname;
1829
+ }
1830
+ } catch {
1831
+ }
1832
+ }
1833
+ var AmplifyUpContext = react.createContext(null);
1834
+ function AmplifyUpProvider({ children, config }) {
1835
+ const [initialized, setInitialized] = react.useState(false);
1836
+ const [pageConfig, setPageConfig] = react.useState(null);
1837
+ const [loading, setLoading] = react.useState(true);
1838
+ const [error, setError] = react.useState(null);
1839
+ const getActualRoute = () => {
1840
+ if (typeof window === "undefined") return "";
1841
+ const currentPath = window.location.pathname;
1842
+ const currentSearch = window.location.search;
1843
+ const urlParams = new URLSearchParams(currentSearch);
1844
+ const routeFromQuery = urlParams.get("route");
1845
+ if (routeFromQuery) {
1846
+ return routeFromQuery;
1847
+ }
1848
+ const actualRoute = window.__AMPLIFYUP_ACTUAL_ROUTE__;
1849
+ if (actualRoute && typeof actualRoute === "string" && (currentPath === "/api/pages/proxy" || currentPath.startsWith("/api/pages/proxy"))) {
1850
+ return actualRoute;
1851
+ }
1852
+ return currentPath;
1853
+ };
1854
+ const nextPathname = usePathname ? usePathname() : null;
1855
+ const [pathname, setPathname] = react.useState(() => {
1856
+ if (typeof window !== "undefined") {
1857
+ return getActualRoute();
1858
+ }
1859
+ return "";
1860
+ });
1861
+ react.useEffect(() => {
1862
+ if (typeof window !== "undefined") {
1863
+ const actualRoute = getActualRoute();
1864
+ if (window.location.pathname === "/api/pages/proxy" || window.location.pathname.startsWith("/api/pages/proxy")) {
1865
+ setPathname(actualRoute);
1866
+ } else if (nextPathname) {
1867
+ setPathname(nextPathname);
1868
+ } else {
1869
+ setPathname(actualRoute);
1870
+ }
1871
+ const timeout = setTimeout(() => {
1872
+ if (window.location.pathname === "/api/pages/proxy" || window.location.pathname.startsWith("/api/pages/proxy")) {
1873
+ const newRoute = getActualRoute();
1874
+ setPathname(newRoute);
1875
+ }
1876
+ }, 200);
1877
+ const handleRouteChange = () => {
1878
+ setPathname(getActualRoute());
1879
+ };
1880
+ window.addEventListener("popstate", handleRouteChange);
1881
+ return () => {
1882
+ clearTimeout(timeout);
1883
+ window.removeEventListener("popstate", handleRouteChange);
1884
+ };
1885
+ }
1886
+ }, [nextPathname]);
1887
+ react.useEffect(() => {
1888
+ const sdk = getSDK();
1889
+ sdk.init(config).then(() => {
1890
+ setInitialized(true);
1891
+ }).catch((err) => {
1892
+ console.error("Failed to initialize AmplifyUp SDK:", err);
1893
+ setError("Failed to initialize tracking");
1894
+ setLoading(false);
1895
+ });
1896
+ }, []);
1897
+ react.useEffect(() => {
1898
+ if (!initialized) return;
1899
+ const fetchConfig = async () => {
1900
+ try {
1901
+ setLoading(true);
1902
+ setError(null);
1903
+ const actualRoute = getActualRoute();
1904
+ if (!actualRoute) {
1905
+ setLoading(false);
1906
+ return;
1907
+ }
1908
+ const sdk = getSDK();
1909
+ const isPreviewContext = (() => {
1910
+ if (typeof window === "undefined") return false;
1911
+ if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) return true;
1912
+ const params = new URLSearchParams(window.location.search);
1913
+ if (params.get("preview") === "true" || params.get("visualEditor") === "true") return true;
1914
+ if (window.self !== window.top) {
1915
+ if (document.referrer) {
1916
+ try {
1917
+ const refUrl = new URL(document.referrer);
1918
+ if (refUrl.hostname === "localhost" && refUrl.port === "5001" || refUrl.pathname.includes("/dashboard/composer") || refUrl.pathname.includes("/dashboard/routes/")) {
1919
+ return true;
1920
+ }
1921
+ } catch {
1922
+ }
1923
+ }
1924
+ }
1925
+ return false;
1926
+ })();
1927
+ const page = await sdk.fetchPageConfig(actualRoute, isPreviewContext);
1928
+ if (!page) {
1929
+ setPageConfig({
1930
+ id: "default",
1931
+ route: actualRoute,
1932
+ title: "Page Not Configured",
1933
+ description: "This page has not been configured in the orchestrator yet.",
1934
+ layoutTree: []
1935
+ });
1936
+ } else {
1937
+ setPageConfig(page);
1938
+ }
1939
+ } catch (err) {
1940
+ console.error("Error fetching page config:", err);
1941
+ setError(err instanceof Error ? err.message : "Unknown error");
1942
+ } finally {
1943
+ setLoading(false);
1944
+ }
1945
+ };
1946
+ fetchConfig();
1947
+ }, [pathname, initialized]);
1948
+ return /* @__PURE__ */ jsxRuntime.jsx(AmplifyUpContext.Provider, { value: { pageConfig, loading, error }, children });
1949
+ }
1950
+ function useAmplifyUp() {
1951
+ const context = react.useContext(AmplifyUpContext);
1952
+ if (!context) {
1953
+ throw new Error("useAmplifyUp must be used within AmplifyUpProvider");
1954
+ }
1955
+ return context;
1956
+ }
1957
+
1958
+ exports.AmplifyRenderer = AmplifyRenderer;
1959
+ exports.AmplifyUpProvider = AmplifyUpProvider;
1960
+ exports.ComponentContextProvider = ComponentContextProvider;
1961
+ exports.Field = Field;
1962
+ exports.Slot = Slot;
1963
+ exports.fetchPageConfigServer = fetchPageConfigServer;
1964
+ exports.useAmplifyUp = useAmplifyUp;
1965
+ //# sourceMappingURL=react.js.map
1966
+ //# sourceMappingURL=react.js.map