@amplifyup/sdk 0.1.22 → 0.1.24

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 CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ var marked = require('marked');
6
7
 
7
8
  /* @amplifyup/sdk - https://amplifyup.ai */
8
9
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -153,17 +154,42 @@ var AWSAdapter = class {
153
154
  };
154
155
  this.sendEvent("/v1/track", eventData);
155
156
  }
156
- identify(userId, traits) {
157
+ identify(userIdOrIdentifiers, traits = {}) {
157
158
  if (!this.initialized) {
158
159
  console.warn("AmplifyUp SDK: Not initialized. Call init() first.");
159
160
  return;
160
161
  }
162
+ const isMap = userIdOrIdentifiers !== null && typeof userIdOrIdentifiers === "object" && !Array.isArray(userIdOrIdentifiers);
163
+ const identifiers = isMap ? userIdOrIdentifiers : traits.identifiers;
164
+ const userId = isMap ? void 0 : String(userIdOrIdentifiers);
161
165
  const eventData = {
162
166
  type: "identify",
163
- userId,
164
167
  anonymousId: traits.instanceId,
165
168
  traits: {
166
- ...traits
169
+ ...traits,
170
+ ...identifiers ? { identifiers } : {}
171
+ },
172
+ ...identifiers ? { identifiers } : {},
173
+ ...userId ? { userId } : {},
174
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
175
+ };
176
+ this.sendEvent("/v1/track", eventData);
177
+ }
178
+ setProfileFields(fields, context = {}) {
179
+ if (!this.initialized) {
180
+ console.warn("AmplifyUp SDK: Not initialized. Call init() first.");
181
+ return;
182
+ }
183
+ const eventData = {
184
+ type: "set_fields",
185
+ anonymousId: context.instanceId,
186
+ fields,
187
+ traits: {
188
+ instanceId: context.instanceId,
189
+ trackingId: context.trackingId
190
+ },
191
+ properties: {
192
+ trackingId: context.trackingId
167
193
  },
168
194
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
169
195
  };
@@ -225,6 +251,7 @@ function clearIds() {
225
251
  var AWS_DEV_API = "https://api-dev.amplifyup.ai";
226
252
  var AWS_PROD_API = "https://api.amplifyup.ai";
227
253
  var PROD_ORCHESTRATOR = "https://amplifyup.ai";
254
+ var PROD_EDGE = "https://edge.amplifyup.ai";
228
255
  function isAmplifyUpTarget(value) {
229
256
  return value === "local" || value === "dev" || value === "production";
230
257
  }
@@ -260,6 +287,10 @@ function localAwsApiUrl() {
260
287
  const fromEnv = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_AMPLIFYUP_API_URL : void 0;
261
288
  return (fromEnv || AWS_DEV_API).replace(/\/$/, "");
262
289
  }
290
+ function localEdgeUrl() {
291
+ const fromEnv = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_EDGE_URL : void 0;
292
+ return (fromEnv || "http://localhost:8787").replace(/\/$/, "");
293
+ }
263
294
  function resolveAmplifyUpUrls(target) {
264
295
  if (target === "local") {
265
296
  const orchestratorUrl2 = localOrchestratorUrl();
@@ -270,18 +301,22 @@ function resolveAmplifyUpUrls(target) {
270
301
  orchestratorUrl: orchestratorUrl2,
271
302
  // Events + personalize → AWS development stack
272
303
  apiGatewayUrl,
273
- decisionApiUrl: `${apiGatewayUrl}/v1/personalize`
304
+ decisionApiUrl: `${apiGatewayUrl}/v1/personalize`,
305
+ edgeUrl: localEdgeUrl()
274
306
  };
275
307
  }
276
308
  const resolvedApi = typeof process !== "undefined" && process.env.NEXT_PUBLIC_AMPLIFYUP_API_URL?.replace(/\/$/, "") || AWS_PROD_API;
277
309
  const orchestratorFromEnv = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_ORCHESTRATOR_URL : void 0;
278
310
  const isLocalOrchestrator = !!orchestratorFromEnv && (orchestratorFromEnv.includes("localhost") || orchestratorFromEnv.includes("127.0.0.1"));
279
311
  const orchestratorUrl = isLocalOrchestrator || !orchestratorFromEnv ? PROD_ORCHESTRATOR : orchestratorFromEnv.replace(/\/$/, "");
312
+ const edgeFromEnv = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_EDGE_URL?.replace(/\/$/, "") : void 0;
313
+ const edgeUrl = edgeFromEnv && !edgeFromEnv.includes("localhost") && !edgeFromEnv.includes("127.0.0.1") ? edgeFromEnv : PROD_EDGE;
280
314
  return {
281
315
  target: "production",
282
316
  apiGatewayUrl: resolvedApi,
283
317
  orchestratorUrl,
284
- decisionApiUrl: `${resolvedApi}/v1/personalize`
318
+ decisionApiUrl: `${resolvedApi}/v1/personalize`,
319
+ edgeUrl
285
320
  };
286
321
  }
287
322
 
@@ -311,6 +346,177 @@ function isComposerPreview() {
311
346
  return false;
312
347
  }
313
348
 
349
+ // src/edge/normalizeRoute.ts
350
+ function normalizeRoute(route) {
351
+ let path = String(route || "").trim();
352
+ if (!path) return "/";
353
+ try {
354
+ if (/^https?:\/\//i.test(path)) {
355
+ path = new URL(path).pathname;
356
+ }
357
+ } catch {
358
+ }
359
+ const q = path.indexOf("?");
360
+ if (q >= 0) path = path.slice(0, q);
361
+ const h = path.indexOf("#");
362
+ if (h >= 0) path = path.slice(0, h);
363
+ if (!path.startsWith("/")) path = `/${path}`;
364
+ if (path.length > 1 && path.endsWith("/")) {
365
+ path = path.replace(/\/+$/, "");
366
+ }
367
+ return path || "/";
368
+ }
369
+
370
+ // src/edge/resolve.ts
371
+ var EdgeResolveError = class extends Error {
372
+ constructor(message, status, body) {
373
+ super(message);
374
+ this.name = "EdgeResolveError";
375
+ this.status = status;
376
+ this.body = body;
377
+ }
378
+ };
379
+ async function fetchEdgeResolve(options) {
380
+ const base = options.edgeUrl.replace(/\/+$/, "");
381
+ const route = normalizeRoute(options.route);
382
+ const params = new URLSearchParams({
383
+ route,
384
+ trackingId: options.trackingId
385
+ });
386
+ if (options.siteId) params.set("site", options.siteId);
387
+ if (options.anonId) params.set("anonId", options.anonId);
388
+ const fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);
389
+ const res = await fetchFn(`${base}/v1/resolve?${params.toString()}`, {
390
+ method: "GET",
391
+ headers: { Accept: "application/json" }
392
+ });
393
+ const body = await res.json().catch(() => ({}));
394
+ if (!res.ok) {
395
+ throw new EdgeResolveError(
396
+ (body && typeof body === "object" && "error" in body ? String(body.error) : `Edge resolve failed (${res.status})`) || `Edge resolve failed (${res.status})`,
397
+ res.status,
398
+ body
399
+ );
400
+ }
401
+ return body;
402
+ }
403
+
404
+ // src/edge/mapResolvedToPageConfig.ts
405
+ function mapResolvedComponent(node) {
406
+ const slots = [];
407
+ if (node.children) {
408
+ for (const [slotId, kids] of Object.entries(node.children)) {
409
+ slots.push({
410
+ slotId,
411
+ children: (kids || []).map(mapResolvedComponent)
412
+ });
413
+ }
414
+ }
415
+ return {
416
+ id: node.instanceId,
417
+ componentId: node.componentId,
418
+ props: { ...node.props || {} },
419
+ ...slots.length > 0 ? { slots } : {},
420
+ _contentSource: "edge"
421
+ };
422
+ }
423
+ function mapResolvedToPageConfig(payload) {
424
+ let layoutTree = [];
425
+ if (payload.tree && payload.tree.length > 0) {
426
+ layoutTree = payload.tree.map(mapResolvedComponent);
427
+ } else {
428
+ const zoneIds = Object.keys(payload.zones || {}).sort();
429
+ for (const zoneId of zoneIds) {
430
+ const roots = payload.zones[zoneId] || [];
431
+ layoutTree.push(...roots.map(mapResolvedComponent));
432
+ }
433
+ }
434
+ return {
435
+ id: `edge:${payload.siteId}:${payload.route}:v${payload.version}`,
436
+ route: payload.route,
437
+ title: "",
438
+ layoutTree,
439
+ meta: {
440
+ // Stash edge meta for diagnostics (not visitor-facing)
441
+ description: payload.meta?.degraded ? "degraded" : void 0
442
+ }
443
+ };
444
+ }
445
+ function collectDegraded(payload) {
446
+ if (payload.meta?.degraded) return true;
447
+ const walk = (nodes) => nodes.some(
448
+ (n) => !!n.degraded || (n.children ? Object.values(n.children).some((kids) => walk(kids || [])) : false)
449
+ );
450
+ if (payload.tree && walk(payload.tree)) return true;
451
+ return Object.values(payload.zones || {}).some((roots) => walk(roots || []));
452
+ }
453
+
454
+ // src/content/fetchContent.ts
455
+ async function fetchContent(options) {
456
+ const preview = options.preview ?? (typeof window !== "undefined" ? isComposerPreview() : false);
457
+ if (preview) {
458
+ try {
459
+ const page = await options.fetchOrchestratorPage(options.route, true);
460
+ return {
461
+ pageConfig: page,
462
+ source: "orchestrator",
463
+ meta: { source: "orchestrator" },
464
+ error: null
465
+ };
466
+ } catch (err) {
467
+ return {
468
+ pageConfig: null,
469
+ source: "orchestrator",
470
+ meta: { source: "orchestrator" },
471
+ error: err instanceof Error ? err.message : "Failed to load preview"
472
+ };
473
+ }
474
+ }
475
+ try {
476
+ const payload = await fetchEdgeResolve({
477
+ edgeUrl: options.edgeUrl,
478
+ trackingId: options.trackingId,
479
+ route: options.route,
480
+ anonId: options.anonId,
481
+ siteId: options.siteId,
482
+ fetch: options.fetchFn
483
+ });
484
+ const pageConfig = mapResolvedToPageConfig(payload);
485
+ const degraded = collectDegraded(payload);
486
+ return {
487
+ pageConfig,
488
+ source: "edge",
489
+ meta: {
490
+ source: "edge",
491
+ version: payload.version,
492
+ siteId: payload.siteId,
493
+ variantKeys: payload.meta?.variantKeys,
494
+ degraded,
495
+ audienceLookup: payload.meta?.audienceLookup,
496
+ decideLookup: payload.meta?.decideLookup,
497
+ resolvedAt: payload.meta?.resolvedAt
498
+ },
499
+ error: null
500
+ };
501
+ } catch (err) {
502
+ if (err instanceof EdgeResolveError && err.status === 404) {
503
+ return {
504
+ pageConfig: null,
505
+ source: "edge",
506
+ meta: { source: "edge" },
507
+ error: null
508
+ // unpublished → site fallback, not an error banner
509
+ };
510
+ }
511
+ return {
512
+ pageConfig: null,
513
+ source: "edge",
514
+ meta: { source: "edge" },
515
+ error: err instanceof Error ? err.message : "Couldn't load page content."
516
+ };
517
+ }
518
+ }
519
+
314
520
  // src/index.ts
315
521
  var SESSION_IDLE_GAP_MS = 30 * 60 * 1e3;
316
522
  function readPreviewQueryOverrides() {
@@ -340,6 +546,8 @@ var AmplifyUpSDK = class {
340
546
  this.apiGatewayUrl = "";
341
547
  this.orchestratorUrl = "";
342
548
  this.decisionApiUrl = "";
549
+ this.edgeUrl = "";
550
+ this.siteId = "";
343
551
  this.target = "local";
344
552
  this.initialized = false;
345
553
  /** Prevents overlapping init() from React Strict Mode double-mount */
@@ -496,7 +704,8 @@ var AmplifyUpSDK = class {
496
704
  isInitialized: () => this.isInitialized(),
497
705
  page: (url, properties) => this.page(url, properties),
498
706
  track: (eventName, properties) => this.track(eventName, properties),
499
- identify: (userId, traits) => this.identify(userId, traits)
707
+ identify: (userIdOrIdentifiers, traits) => this.identify(userIdOrIdentifiers, traits),
708
+ setProfileFields: (fields) => this.setProfileFields(fields)
500
709
  };
501
710
  }
502
711
  async performInit(config) {
@@ -511,6 +720,8 @@ var AmplifyUpSDK = class {
511
720
  this.apiGatewayUrl = urls.apiGatewayUrl;
512
721
  this.orchestratorUrl = urls.orchestratorUrl;
513
722
  this.decisionApiUrl = urls.decisionApiUrl;
723
+ this.edgeUrl = urls.edgeUrl;
724
+ this.siteId = config.siteId || "";
514
725
  const previewOverrides = readPreviewQueryOverrides();
515
726
  if (previewOverrides.orchestratorUrl) {
516
727
  this.orchestratorUrl = previewOverrides.orchestratorUrl;
@@ -519,6 +730,7 @@ var AmplifyUpSDK = class {
519
730
  this.target = "production";
520
731
  this.apiGatewayUrl = prodUrls.apiGatewayUrl;
521
732
  this.decisionApiUrl = prodUrls.decisionApiUrl;
733
+ this.edgeUrl = prodUrls.edgeUrl;
522
734
  }
523
735
  }
524
736
  this.config = {
@@ -526,7 +738,8 @@ var AmplifyUpSDK = class {
526
738
  debug: config.debug ?? true,
527
739
  // Enable debug by default
528
740
  target: this.target,
529
- dev: config.dev
741
+ dev: config.dev,
742
+ ...config.siteId ? { siteId: config.siteId } : {}
530
743
  };
531
744
  try {
532
745
  const adapterConfig = {
@@ -924,12 +1137,17 @@ var AmplifyUpSDK = class {
924
1137
  });
925
1138
  }
926
1139
  /**
927
- * Identify user (when they sign up or log in)
1140
+ * Identify a visitor.
1141
+ * - identify('user_123') — bare string; server maps via org default type
1142
+ * - identify({ email: '...', crm_id: '...' }) — explicit types
1143
+ * - identify('user_123', traits) — legacy form
928
1144
  */
929
- identify(userId, traits) {
1145
+ identify(userIdOrIdentifiers, traits) {
930
1146
  if (this.isPreviewMode()) {
931
1147
  if (this.config?.debug) {
932
- console.log("AmplifyUp SDK: Identify tracking disabled in preview mode", { userId });
1148
+ console.log("AmplifyUp SDK: Identify tracking disabled in preview mode", {
1149
+ userIdOrIdentifiers
1150
+ });
933
1151
  }
934
1152
  return;
935
1153
  }
@@ -945,10 +1163,45 @@ var AmplifyUpSDK = class {
945
1163
  trackingId: this.config?.trackingId,
946
1164
  ...traits
947
1165
  };
948
- this.adapter.identify(userId, identifyTraits);
949
- setUserId(userId);
1166
+ this.adapter.identify(userIdOrIdentifiers, identifyTraits);
1167
+ if (typeof userIdOrIdentifiers === "string") {
1168
+ setUserId(userIdOrIdentifiers);
1169
+ }
950
1170
  if (this.config?.debug) {
951
- console.log("AmplifyUp SDK: User identified", { userId, instanceId });
1171
+ console.log("AmplifyUp SDK: User identified", {
1172
+ userIdOrIdentifiers,
1173
+ instanceId
1174
+ });
1175
+ }
1176
+ }
1177
+ /**
1178
+ * Set profile fields (partial update). Omitted keys are left alone;
1179
+ * an explicit empty value clears the field.
1180
+ */
1181
+ setProfileFields(fields) {
1182
+ if (this.isPreviewMode()) {
1183
+ if (this.config?.debug) {
1184
+ console.log("AmplifyUp SDK: setProfileFields disabled in preview mode", {
1185
+ fields
1186
+ });
1187
+ }
1188
+ return;
1189
+ }
1190
+ if (!this.isInitialized()) {
1191
+ if (this.config?.debug) {
1192
+ console.warn("AmplifyUp SDK: Not initialized. setProfileFields not called.");
1193
+ }
1194
+ return;
1195
+ }
1196
+ const instanceId = getInstanceId();
1197
+ if (typeof this.adapter.setProfileFields === "function") {
1198
+ this.adapter.setProfileFields(fields, {
1199
+ instanceId: instanceId || void 0,
1200
+ trackingId: this.config?.trackingId
1201
+ });
1202
+ }
1203
+ if (this.config?.debug) {
1204
+ console.log("AmplifyUp SDK: Profile fields set", { fields, instanceId });
952
1205
  }
953
1206
  }
954
1207
  /**
@@ -1123,6 +1376,18 @@ Available Console Commands:
1123
1376
  getDecisionApiUrl() {
1124
1377
  return this.decisionApiUrl || void 0;
1125
1378
  }
1379
+ /**
1380
+ * Get Edge Worker URL (production content resolve)
1381
+ */
1382
+ getEdgeUrl() {
1383
+ return this.edgeUrl || void 0;
1384
+ }
1385
+ /**
1386
+ * Optional site id from config (Edge can resolve via trackingId alone).
1387
+ */
1388
+ getSiteId() {
1389
+ return this.siteId || this.config?.siteId || void 0;
1390
+ }
1126
1391
  /**
1127
1392
  * Build API URL for fetching page config from the Layout Provider
1128
1393
  */
@@ -2397,6 +2662,53 @@ function getSDK(adapter) {
2397
2662
  var trackContentView = (contentId, contentType, properties) => getSDK().trackContentView(contentId, contentType, properties);
2398
2663
  var refreshEventTracking = () => getSDK().refreshEventTracking();
2399
2664
  getSDK();
2665
+ var ComponentRenderBoundary = class extends react.Component {
2666
+ constructor() {
2667
+ super(...arguments);
2668
+ this.state = { error: null };
2669
+ }
2670
+ static getDerivedStateFromError(error) {
2671
+ return { error };
2672
+ }
2673
+ componentDidCatch(error) {
2674
+ console.error(
2675
+ `[AmplifyRenderer] "${this.props.componentId}" (${this.props.nodeId}) threw while rendering:`,
2676
+ error
2677
+ );
2678
+ }
2679
+ render() {
2680
+ if (this.state.error) {
2681
+ if (process.env.NODE_ENV !== "production") {
2682
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2683
+ "div",
2684
+ {
2685
+ "data-amplifyup-render-error": "",
2686
+ "data-component-id": this.props.componentId,
2687
+ "data-node-id": this.props.nodeId,
2688
+ style: {
2689
+ margin: "1rem 0",
2690
+ padding: "0.75rem 1rem",
2691
+ border: "1px solid #fca5a5",
2692
+ borderRadius: "0.5rem",
2693
+ background: "#fef2f2",
2694
+ color: "#991b1b",
2695
+ fontSize: "0.875rem"
2696
+ },
2697
+ children: [
2698
+ 'Component "',
2699
+ this.props.componentId,
2700
+ '" failed to render:',
2701
+ " ",
2702
+ this.state.error.message
2703
+ ]
2704
+ }
2705
+ );
2706
+ }
2707
+ return null;
2708
+ }
2709
+ return this.props.children;
2710
+ }
2711
+ };
2400
2712
  function AmplifyRenderer({
2401
2713
  renderComponent,
2402
2714
  pageConfig: serverPageConfig,
@@ -2409,6 +2721,7 @@ function AmplifyRenderer({
2409
2721
  const pageConfig = serverPageConfig || clientHook.pageConfig || null;
2410
2722
  const loading = serverPageConfig ? false : clientHook.loading;
2411
2723
  const error = serverPageConfig ? null : clientHook.error;
2724
+ const contentSource = clientHook.source;
2412
2725
  const [variantCache, setVariantCache] = react.useState({});
2413
2726
  react.useEffect(() => {
2414
2727
  if (typeof window !== "undefined") {
@@ -2550,6 +2863,9 @@ function AmplifyRenderer({
2550
2863
  }, [pageConfig]);
2551
2864
  react.useEffect(() => {
2552
2865
  if (!pageConfig?.layoutTree || !pageConfig.layoutId) return;
2866
+ const roots = pageConfig.layoutTree;
2867
+ const fromEdge = contentSource === "edge" || roots.some((n) => n._contentSource === "edge");
2868
+ if (fromEdge) return;
2553
2869
  const sdk = getSDK();
2554
2870
  const instanceId = sdk.getInstanceId();
2555
2871
  const decisionApiUrl = sdk.getDecisionApiUrl();
@@ -2644,7 +2960,7 @@ function AmplifyRenderer({
2644
2960
  setVariantCache(newCache);
2645
2961
  };
2646
2962
  fetchVariants();
2647
- }, [pageConfig?.layoutTree, pageConfig?.layoutId, pageConfig?.route]);
2963
+ }, [pageConfig?.layoutTree, pageConfig?.layoutId, pageConfig?.route, contentSource]);
2648
2964
  if (loading) {
2649
2965
  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: [
2650
2966
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-gray-200 rounded w-1/4 mb-4" }),
@@ -2749,11 +3065,20 @@ function AmplifyRenderer({
2749
3065
  const slotsToPass = Object.keys(slotChildren).length > 0 ? slotChildren : void 0;
2750
3066
  console.log(`[AmplifyRenderer] Calling renderComponent for ${node.componentId}`, {
2751
3067
  hasSlots: !!slotsToPass,
2752
- slotKeys: slotsToPass ? Object.keys(slotsToPass) : []
3068
+ slotKeys: slotsToPass ? Object.keys(slotsToPass) : [],
3069
+ propKeys: Object.keys(node.props || {}),
3070
+ props: node.props || {}
2753
3071
  });
2754
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-node-id": node.id, "data-amplifyup-node": "", children: renderComponent(node.componentId, node.props || {}, slotsToPass, {
2755
- layoutNodeId: node.id
2756
- }) });
3072
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-node-id": node.id, "data-amplifyup-node": "", children: /* @__PURE__ */ jsxRuntime.jsx(
3073
+ ComponentRenderBoundary,
3074
+ {
3075
+ componentId: node.componentId,
3076
+ nodeId: node.id,
3077
+ children: renderComponent(node.componentId, node.props || {}, slotsToPass, {
3078
+ layoutNodeId: node.id
3079
+ })
3080
+ }
3081
+ ) });
2757
3082
  };
2758
3083
  const rawTree = pageConfig.layoutTree;
2759
3084
  const layoutTree = Array.isArray(rawTree) ? rawTree : rawTree && typeof rawTree === "object" && rawTree.componentId ? [rawTree] : [];
@@ -2845,28 +3170,38 @@ async function fetchPageConfigServer(route, trackingId, preview = false, options
2845
3170
  const target = resolveAmplifyUpTarget({
2846
3171
  configTarget: options?.target
2847
3172
  });
2848
- const { orchestratorUrl } = resolveAmplifyUpUrls(target);
2849
- const params = new URLSearchParams({
2850
- route,
2851
- trackingId
2852
- });
2853
- if (preview) {
2854
- params.set("preview", "true");
2855
- }
2856
- const url = `${orchestratorUrl}/api/layout/provider?${params.toString()}`;
2857
- try {
2858
- const res = await fetch(url, {
2859
- cache: "no-store",
2860
- headers: { Accept: "application/json" }
3173
+ const { orchestratorUrl, edgeUrl } = resolveAmplifyUpUrls(target);
3174
+ const previewFlag = preview || (options?.searchParams ? isPreviewRequest(options.searchParams) : false);
3175
+ const fetchOrchestratorPage = async (pageRoute, isPreview) => {
3176
+ const params = new URLSearchParams({
3177
+ route: pageRoute,
3178
+ trackingId
2861
3179
  });
2862
- if (!res.ok) {
3180
+ if (isPreview) params.set("preview", "true");
3181
+ const url = `${orchestratorUrl}/api/layout/provider?${params.toString()}`;
3182
+ try {
3183
+ const res = await fetch(url, {
3184
+ cache: "no-store",
3185
+ headers: { Accept: "application/json" }
3186
+ });
3187
+ if (!res.ok) return null;
3188
+ const data = await res.json();
3189
+ return data.page || null;
3190
+ } catch {
2863
3191
  return null;
2864
3192
  }
2865
- const data = await res.json();
2866
- return data.page || null;
2867
- } catch {
2868
- return null;
2869
- }
3193
+ };
3194
+ const result = await fetchContent({
3195
+ route,
3196
+ trackingId,
3197
+ anonId: options?.anonId,
3198
+ siteId: options?.siteId,
3199
+ preview: previewFlag,
3200
+ orchestratorUrl,
3201
+ edgeUrl,
3202
+ fetchOrchestratorPage
3203
+ });
3204
+ return result.pageConfig;
2870
3205
  }
2871
3206
  var ComponentContext = react.createContext(null);
2872
3207
  function ComponentContextProvider({
@@ -2989,6 +3324,25 @@ function Slot({
2989
3324
  }
2990
3325
  );
2991
3326
  }
3327
+ function formatFieldValue(value) {
3328
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
3329
+ return String(value);
3330
+ }
3331
+ if (Array.isArray(value)) {
3332
+ const parts = value.map((item) => {
3333
+ if (typeof item === "string") return item;
3334
+ if (item && typeof item === "object" && "children" in item && Array.isArray(item.children)) {
3335
+ return item.children.map((c) => typeof c?.text === "string" ? c.text : "").join("");
3336
+ }
3337
+ return "";
3338
+ }).filter(Boolean);
3339
+ if (parts.length > 0) return parts.join(" ");
3340
+ }
3341
+ if (value && typeof value === "object") {
3342
+ return "";
3343
+ }
3344
+ return "";
3345
+ }
2992
3346
  function Field({
2993
3347
  name,
2994
3348
  fallback,
@@ -3007,7 +3361,51 @@ function Field({
3007
3361
  if (render) {
3008
3362
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: render(fieldValue) });
3009
3363
  }
3010
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: String(fieldValue) });
3364
+ const text = formatFieldValue(fieldValue);
3365
+ if (!text) {
3366
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback }) : null;
3367
+ }
3368
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: text });
3369
+ }
3370
+ function markdownToHtml(markdown) {
3371
+ try {
3372
+ const result = marked.marked.parse(markdown, { async: false });
3373
+ return typeof result === "string" ? result : "";
3374
+ } catch {
3375
+ return markdown;
3376
+ }
3377
+ }
3378
+ function RichText({
3379
+ name,
3380
+ fallback,
3381
+ className,
3382
+ as: Tag = "div"
3383
+ }) {
3384
+ const context = react.useContext(ComponentContext);
3385
+ if (!context) {
3386
+ console.warn(`[AmplifyUp SDK] RichText "${name}" used outside ComponentContextProvider`);
3387
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
3388
+ }
3389
+ const fieldValue = context.props?.[name];
3390
+ if (fieldValue === void 0 || fieldValue === null || fieldValue === "") {
3391
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, children: fallback }) : null;
3392
+ }
3393
+ let html = "";
3394
+ if (typeof fieldValue === "string") {
3395
+ const trimmed = fieldValue.trim();
3396
+ if (trimmed.startsWith("<") && trimmed.includes(">")) {
3397
+ html = fieldValue;
3398
+ } else {
3399
+ html = markdownToHtml(fieldValue);
3400
+ }
3401
+ } else {
3402
+ const text = formatFieldValue(fieldValue);
3403
+ html = text ? markdownToHtml(text) : "";
3404
+ }
3405
+ if (!html) {
3406
+ return fallback ? /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, children: fallback }) : null;
3407
+ }
3408
+ return /* @__PURE__ */ jsxRuntime.jsx(Tag, { className, dangerouslySetInnerHTML: { __html: html } });
3011
3409
  }
3012
3410
  var usePathname = null;
3013
3411
  if (typeof window !== "undefined") {
@@ -3025,6 +3423,8 @@ function AmplifyUpProvider({ children, config }) {
3025
3423
  const [pageConfig, setPageConfig] = react.useState(null);
3026
3424
  const [loading, setLoading] = react.useState(true);
3027
3425
  const [error, setError] = react.useState(null);
3426
+ const [source, setSource] = react.useState(null);
3427
+ const [meta, setMeta] = react.useState(null);
3028
3428
  const getActualRoute = () => {
3029
3429
  if (typeof window === "undefined") return "";
3030
3430
  const currentPath = window.location.pathname;
@@ -3090,10 +3490,10 @@ function AmplifyUpProvider({ children, config }) {
3090
3490
  return () => {
3091
3491
  cancelled = true;
3092
3492
  };
3093
- }, [config.trackingId, config.target, config.debug, config.dev]);
3493
+ }, [config.trackingId, config.target, config.debug, config.dev, config.siteId]);
3094
3494
  react.useEffect(() => {
3095
3495
  if (!initialized) return;
3096
- const fetchConfig = async () => {
3496
+ const load = async () => {
3097
3497
  try {
3098
3498
  setLoading(true);
3099
3499
  setError(null);
@@ -3104,29 +3504,57 @@ function AmplifyUpProvider({ children, config }) {
3104
3504
  }
3105
3505
  const sdk = getSDK();
3106
3506
  sdk.routeChange(actualRoute);
3107
- const isPreviewContext = isComposerPreview();
3108
- const page = await sdk.fetchPageConfig(actualRoute, isPreviewContext);
3109
- if (!page) {
3507
+ const preview = isComposerPreview();
3508
+ const edgeUrl = sdk.getEdgeUrl();
3509
+ const orchestratorUrl = sdk.getOrchestratorUrl();
3510
+ const trackingId = sdk.getTrackingId();
3511
+ if (!trackingId || !orchestratorUrl || !edgeUrl) {
3512
+ setError("SDK not initialized");
3513
+ setLoading(false);
3514
+ return;
3515
+ }
3516
+ const result = await fetchContent({
3517
+ route: actualRoute,
3518
+ trackingId,
3519
+ anonId: sdk.getInstanceId(),
3520
+ siteId: sdk.getSiteId() || null,
3521
+ preview,
3522
+ orchestratorUrl,
3523
+ edgeUrl,
3524
+ fetchOrchestratorPage: (route, isPreview) => sdk.fetchPageConfig(route, isPreview)
3525
+ });
3526
+ setSource(result.source);
3527
+ setMeta(result.meta);
3528
+ setError(result.error);
3529
+ if (!result.pageConfig) {
3110
3530
  setPageConfig({
3111
3531
  id: "default",
3112
3532
  route: actualRoute,
3113
3533
  title: "Page Not Configured",
3114
- description: "This page has not been configured in the orchestrator yet.",
3534
+ description: "This page has not been published yet.",
3115
3535
  layoutTree: []
3116
3536
  });
3117
3537
  } else {
3118
- setPageConfig(page);
3538
+ setPageConfig(result.pageConfig);
3119
3539
  }
3120
3540
  } catch (err) {
3121
3541
  console.error("Error fetching page config:", err);
3122
3542
  setError(err instanceof Error ? err.message : "Unknown error");
3543
+ setSource(null);
3544
+ setMeta(null);
3123
3545
  } finally {
3124
3546
  setLoading(false);
3125
3547
  }
3126
3548
  };
3127
- fetchConfig();
3549
+ void load();
3128
3550
  }, [pathname, initialized]);
3129
- return /* @__PURE__ */ jsxRuntime.jsx(AmplifyUpContext.Provider, { value: { pageConfig, loading, error }, children });
3551
+ return /* @__PURE__ */ jsxRuntime.jsx(
3552
+ AmplifyUpContext.Provider,
3553
+ {
3554
+ value: { pageConfig, loading, error, source, meta },
3555
+ children
3556
+ }
3557
+ );
3130
3558
  }
3131
3559
  function useAmplifyUp() {
3132
3560
  const context = react.useContext(AmplifyUpContext);
@@ -3141,6 +3569,7 @@ exports.AmplifyRenderer = AmplifyRenderer;
3141
3569
  exports.AmplifyUpProvider = AmplifyUpProvider;
3142
3570
  exports.ComponentContextProvider = ComponentContextProvider;
3143
3571
  exports.Field = Field;
3572
+ exports.RichText = RichText;
3144
3573
  exports.Slot = Slot;
3145
3574
  exports.fetchPageConfigServer = fetchPageConfigServer;
3146
3575
  exports.isComposerPreview = isComposerPreview;