@amplifyup/sdk 0.1.22 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @amplifyup/sdk
2
2
 
3
- Official JavaScript SDK for [AmplifyUp](https://amplifyup.ai) — event tracking, page layouts, personalization, and Composer preview on your site.
3
+ Official JavaScript SDK for [AmplifyUp](https://amplifyup.ai) — event tracking, Edge-resolved page layouts, personalization, and Composer preview on your site.
4
+
5
+ **Full guide:** [docs/sdk-site-setup.md](../../docs/sdk-site-setup.md)
4
6
 
5
7
  ## Install
6
8
 
@@ -8,11 +10,11 @@ Official JavaScript SDK for [AmplifyUp](https://amplifyup.ai) — event tracking
8
10
  npm install @amplifyup/sdk
9
11
  ```
10
12
 
11
- ## Quick start
13
+ ## Quick start (production)
12
14
 
13
- ### React / Next.js
15
+ Production is the default. You only need a **tracking ID** from the AmplifyUp dashboard.
14
16
 
15
- Wrap your app with the provider and pass your site **tracking ID** from the AmplifyUp dashboard:
17
+ ### 1. Provider
16
18
 
17
19
  ```tsx
18
20
  import { AmplifyUpProvider } from '@amplifyup/sdk/react';
@@ -26,61 +28,86 @@ export default function RootLayout({ children }) {
26
28
  }
27
29
  ```
28
30
 
29
- ### Track events
31
+ ### 2. Page content
30
32
 
31
- ```typescript
32
- import { init, page, track, identify } from '@amplifyup/sdk';
33
+ ```tsx
34
+ import { AmplifyPageContent, ComponentContextProvider } from '@amplifyup/sdk/react';
35
+ import { Hero } from '@/components/Hero';
33
36
 
34
- await init({ trackingId: 'your-tracking-id' });
37
+ const registry = { Hero };
35
38
 
36
- page('/home');
39
+ function renderComponent(componentId, props, slots, context) {
40
+ const Component = registry[componentId];
41
+ if (!Component) return null;
42
+ return (
43
+ <ComponentContextProvider
44
+ props={props}
45
+ slots={slots}
46
+ layoutNodeId={context?.layoutNodeId}
47
+ componentId={componentId}
48
+ >
49
+ <Component {...props} />
50
+ </ComponentContextProvider>
51
+ );
52
+ }
37
53
 
38
- track('Product Viewed', { productId: '123' });
54
+ export default function Page() {
55
+ return (
56
+ <AmplifyPageContent
57
+ renderComponent={renderComponent}
58
+ fallback={<YourStaticPage />}
59
+ />
60
+ );
61
+ }
62
+ ```
39
63
 
40
- identify('user-123', { email: 'user@example.com' });
64
+ ### 3. Components use Field / Slot
65
+
66
+ ```tsx
67
+ import { Field, Slot } from '@amplifyup/sdk/react';
68
+
69
+ export function Hero() {
70
+ return (
71
+ <section>
72
+ <h1>
73
+ <Field name="heading" fallback="Welcome" />
74
+ </h1>
75
+ </section>
76
+ );
77
+ }
41
78
  ```
42
79
 
43
- ## Composer and hybrid pages
80
+ Field names must match the component registry in AmplifyUp. Connect CMS documents in Composer, then **Deploy** so Edge can serve the page.
44
81
 
45
- If a page has its own native content **and** can be edited in Composer, use `AmplifyPageContent`:
82
+ ## Content sources
46
83
 
47
- - **Visitors** see your AmplifyUp layout, or your native content when no layout is configured yet.
48
- - **Composer preview** shows only the layout canvas — native fallback content is hidden.
84
+ | Mode | Source | When |
85
+ |------|--------|------|
86
+ | Production visitors | Edge `/v1/resolve` | Default |
87
+ | Composer preview | Orchestrator layout provider | `?preview=true` / Composer iframe |
49
88
 
50
- ```tsx
51
- import { isPreviewRequest } from '@amplifyup/sdk';
52
- import { AmplifyPageContent } from '@amplifyup/sdk/react';
89
+ The SDK maps Edge’s resolved tree into `layoutTree` and renders it. Edge already applies personalization and CMS projection — the site does not call CMS or Decision APIs for layout.
53
90
 
54
- export default async function Page({ searchParams }) {
55
- const params = await searchParams;
91
+ ## Track events
56
92
 
57
- if (isPreviewRequest(params)) {
58
- return (
59
- <Layout>
60
- <AmplifyPageContent renderComponent={renderAmplifyComponent} />
61
- </Layout>
62
- );
63
- }
93
+ ```typescript
94
+ import { init, page, track, identify } from '@amplifyup/sdk';
64
95
 
65
- return (
66
- <Layout>
67
- <AmplifyPageContent
68
- fallback={<YourNativePage />}
69
- renderComponent={renderAmplifyComponent}
70
- />
71
- </Layout>
72
- );
73
- }
96
+ await init({ trackingId: 'your-tracking-id' });
97
+
98
+ page('/home');
99
+ track('Product Viewed', { productId: '123' });
100
+ identify('user-123', { email: 'user@example.com' });
74
101
  ```
75
102
 
76
103
  ## Debugging
77
104
 
78
- In the browser console (on your site):
79
-
80
105
  ```js
81
106
  AmplifyUp.getStatus()
82
107
  ```
83
108
 
109
+ React context: `useAmplifyUp()` → `{ pageConfig, loading, error, source, meta }`.
110
+
84
111
  ## License
85
112
 
86
113
  MIT
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { P as PageConfig } from './types-Cy5aWQ5c.mjs';
3
+ import { P as PageConfig } from './types-BxpG9Gsy.mjs';
4
4
 
5
5
  interface AmplifyRendererProps {
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
- import { P as PageConfig } from './types-Cy5aWQ5c.js';
3
+ import { P as PageConfig } from './types-BxpG9Gsy.js';
4
4
 
5
5
  interface AmplifyRendererProps {
6
6
  /**
@@ -153,17 +153,42 @@ var AWSAdapter = class {
153
153
  };
154
154
  this.sendEvent("/v1/track", eventData);
155
155
  }
156
- identify(userId, traits) {
156
+ identify(userIdOrIdentifiers, traits = {}) {
157
157
  if (!this.initialized) {
158
158
  console.warn("AmplifyUp SDK: Not initialized. Call init() first.");
159
159
  return;
160
160
  }
161
+ const isMap = userIdOrIdentifiers !== null && typeof userIdOrIdentifiers === "object" && !Array.isArray(userIdOrIdentifiers);
162
+ const identifiers = isMap ? userIdOrIdentifiers : traits.identifiers;
163
+ const userId = isMap ? void 0 : String(userIdOrIdentifiers);
161
164
  const eventData = {
162
165
  type: "identify",
163
- userId,
164
166
  anonymousId: traits.instanceId,
165
167
  traits: {
166
- ...traits
168
+ ...traits,
169
+ ...identifiers ? { identifiers } : {}
170
+ },
171
+ ...identifiers ? { identifiers } : {},
172
+ ...userId ? { userId } : {},
173
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
174
+ };
175
+ this.sendEvent("/v1/track", eventData);
176
+ }
177
+ setProfileFields(fields, context = {}) {
178
+ if (!this.initialized) {
179
+ console.warn("AmplifyUp SDK: Not initialized. Call init() first.");
180
+ return;
181
+ }
182
+ const eventData = {
183
+ type: "set_fields",
184
+ anonymousId: context.instanceId,
185
+ fields,
186
+ traits: {
187
+ instanceId: context.instanceId,
188
+ trackingId: context.trackingId
189
+ },
190
+ properties: {
191
+ trackingId: context.trackingId
167
192
  },
168
193
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
169
194
  };
@@ -225,6 +250,7 @@ function clearIds() {
225
250
  var AWS_DEV_API = "https://api-dev.amplifyup.ai";
226
251
  var AWS_PROD_API = "https://api.amplifyup.ai";
227
252
  var PROD_ORCHESTRATOR = "https://amplifyup.ai";
253
+ var PROD_EDGE = "https://edge.amplifyup.ai";
228
254
  function isAmplifyUpTarget(value) {
229
255
  return value === "local" || value === "dev" || value === "production";
230
256
  }
@@ -260,6 +286,10 @@ function localAwsApiUrl() {
260
286
  const fromEnv = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_AMPLIFYUP_API_URL : void 0;
261
287
  return (fromEnv || AWS_DEV_API).replace(/\/$/, "");
262
288
  }
289
+ function localEdgeUrl() {
290
+ const fromEnv = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_EDGE_URL : void 0;
291
+ return (fromEnv || "http://localhost:8787").replace(/\/$/, "");
292
+ }
263
293
  function resolveAmplifyUpUrls(target) {
264
294
  if (target === "local") {
265
295
  const orchestratorUrl2 = localOrchestratorUrl();
@@ -270,18 +300,22 @@ function resolveAmplifyUpUrls(target) {
270
300
  orchestratorUrl: orchestratorUrl2,
271
301
  // Events + personalize → AWS development stack
272
302
  apiGatewayUrl,
273
- decisionApiUrl: `${apiGatewayUrl}/v1/personalize`
303
+ decisionApiUrl: `${apiGatewayUrl}/v1/personalize`,
304
+ edgeUrl: localEdgeUrl()
274
305
  };
275
306
  }
276
307
  const resolvedApi = typeof process !== "undefined" && process.env.NEXT_PUBLIC_AMPLIFYUP_API_URL?.replace(/\/$/, "") || AWS_PROD_API;
277
308
  const orchestratorFromEnv = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_ORCHESTRATOR_URL : void 0;
278
309
  const isLocalOrchestrator = !!orchestratorFromEnv && (orchestratorFromEnv.includes("localhost") || orchestratorFromEnv.includes("127.0.0.1"));
279
310
  const orchestratorUrl = isLocalOrchestrator || !orchestratorFromEnv ? PROD_ORCHESTRATOR : orchestratorFromEnv.replace(/\/$/, "");
311
+ const edgeFromEnv = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_EDGE_URL?.replace(/\/$/, "") : void 0;
312
+ const edgeUrl = edgeFromEnv && !edgeFromEnv.includes("localhost") && !edgeFromEnv.includes("127.0.0.1") ? edgeFromEnv : PROD_EDGE;
280
313
  return {
281
314
  target: "production",
282
315
  apiGatewayUrl: resolvedApi,
283
316
  orchestratorUrl,
284
- decisionApiUrl: `${resolvedApi}/v1/personalize`
317
+ decisionApiUrl: `${resolvedApi}/v1/personalize`,
318
+ edgeUrl
285
319
  };
286
320
  }
287
321
 
@@ -340,6 +374,8 @@ var AmplifyUpSDK = class {
340
374
  this.apiGatewayUrl = "";
341
375
  this.orchestratorUrl = "";
342
376
  this.decisionApiUrl = "";
377
+ this.edgeUrl = "";
378
+ this.siteId = "";
343
379
  this.target = "local";
344
380
  this.initialized = false;
345
381
  /** Prevents overlapping init() from React Strict Mode double-mount */
@@ -496,7 +532,8 @@ var AmplifyUpSDK = class {
496
532
  isInitialized: () => this.isInitialized(),
497
533
  page: (url, properties) => this.page(url, properties),
498
534
  track: (eventName, properties) => this.track(eventName, properties),
499
- identify: (userId, traits) => this.identify(userId, traits)
535
+ identify: (userIdOrIdentifiers, traits) => this.identify(userIdOrIdentifiers, traits),
536
+ setProfileFields: (fields) => this.setProfileFields(fields)
500
537
  };
501
538
  }
502
539
  async performInit(config) {
@@ -511,6 +548,8 @@ var AmplifyUpSDK = class {
511
548
  this.apiGatewayUrl = urls.apiGatewayUrl;
512
549
  this.orchestratorUrl = urls.orchestratorUrl;
513
550
  this.decisionApiUrl = urls.decisionApiUrl;
551
+ this.edgeUrl = urls.edgeUrl;
552
+ this.siteId = config.siteId || "";
514
553
  const previewOverrides = readPreviewQueryOverrides();
515
554
  if (previewOverrides.orchestratorUrl) {
516
555
  this.orchestratorUrl = previewOverrides.orchestratorUrl;
@@ -519,6 +558,7 @@ var AmplifyUpSDK = class {
519
558
  this.target = "production";
520
559
  this.apiGatewayUrl = prodUrls.apiGatewayUrl;
521
560
  this.decisionApiUrl = prodUrls.decisionApiUrl;
561
+ this.edgeUrl = prodUrls.edgeUrl;
522
562
  }
523
563
  }
524
564
  this.config = {
@@ -526,7 +566,8 @@ var AmplifyUpSDK = class {
526
566
  debug: config.debug ?? true,
527
567
  // Enable debug by default
528
568
  target: this.target,
529
- dev: config.dev
569
+ dev: config.dev,
570
+ ...config.siteId ? { siteId: config.siteId } : {}
530
571
  };
531
572
  try {
532
573
  const adapterConfig = {
@@ -924,12 +965,17 @@ var AmplifyUpSDK = class {
924
965
  });
925
966
  }
926
967
  /**
927
- * Identify user (when they sign up or log in)
968
+ * Identify a visitor.
969
+ * - identify('user_123') — bare string; server maps via org default type
970
+ * - identify({ email: '...', crm_id: '...' }) — explicit types
971
+ * - identify('user_123', traits) — legacy form
928
972
  */
929
- identify(userId, traits) {
973
+ identify(userIdOrIdentifiers, traits) {
930
974
  if (this.isPreviewMode()) {
931
975
  if (this.config?.debug) {
932
- console.log("AmplifyUp SDK: Identify tracking disabled in preview mode", { userId });
976
+ console.log("AmplifyUp SDK: Identify tracking disabled in preview mode", {
977
+ userIdOrIdentifiers
978
+ });
933
979
  }
934
980
  return;
935
981
  }
@@ -945,10 +991,45 @@ var AmplifyUpSDK = class {
945
991
  trackingId: this.config?.trackingId,
946
992
  ...traits
947
993
  };
948
- this.adapter.identify(userId, identifyTraits);
949
- setUserId(userId);
994
+ this.adapter.identify(userIdOrIdentifiers, identifyTraits);
995
+ if (typeof userIdOrIdentifiers === "string") {
996
+ setUserId(userIdOrIdentifiers);
997
+ }
998
+ if (this.config?.debug) {
999
+ console.log("AmplifyUp SDK: User identified", {
1000
+ userIdOrIdentifiers,
1001
+ instanceId
1002
+ });
1003
+ }
1004
+ }
1005
+ /**
1006
+ * Set profile fields (partial update). Omitted keys are left alone;
1007
+ * an explicit empty value clears the field.
1008
+ */
1009
+ setProfileFields(fields) {
1010
+ if (this.isPreviewMode()) {
1011
+ if (this.config?.debug) {
1012
+ console.log("AmplifyUp SDK: setProfileFields disabled in preview mode", {
1013
+ fields
1014
+ });
1015
+ }
1016
+ return;
1017
+ }
1018
+ if (!this.isInitialized()) {
1019
+ if (this.config?.debug) {
1020
+ console.warn("AmplifyUp SDK: Not initialized. setProfileFields not called.");
1021
+ }
1022
+ return;
1023
+ }
1024
+ const instanceId = getInstanceId();
1025
+ if (typeof this.adapter.setProfileFields === "function") {
1026
+ this.adapter.setProfileFields(fields, {
1027
+ instanceId: instanceId || void 0,
1028
+ trackingId: this.config?.trackingId
1029
+ });
1030
+ }
950
1031
  if (this.config?.debug) {
951
- console.log("AmplifyUp SDK: User identified", { userId, instanceId });
1032
+ console.log("AmplifyUp SDK: Profile fields set", { fields, instanceId });
952
1033
  }
953
1034
  }
954
1035
  /**
@@ -1123,6 +1204,18 @@ Available Console Commands:
1123
1204
  getDecisionApiUrl() {
1124
1205
  return this.decisionApiUrl || void 0;
1125
1206
  }
1207
+ /**
1208
+ * Get Edge Worker URL (production content resolve)
1209
+ */
1210
+ getEdgeUrl() {
1211
+ return this.edgeUrl || void 0;
1212
+ }
1213
+ /**
1214
+ * Optional site id from config (Edge can resolve via trackingId alone).
1215
+ */
1216
+ getSiteId() {
1217
+ return this.siteId || this.config?.siteId || void 0;
1218
+ }
1126
1219
  /**
1127
1220
  * Build API URL for fetching page config from the Layout Provider
1128
1221
  */
@@ -2428,6 +2521,7 @@ function AmplifyRenderer({
2428
2521
  const pageConfig = serverPageConfig || clientHook.pageConfig || null;
2429
2522
  const loading = serverPageConfig ? false : clientHook.loading;
2430
2523
  const error = serverPageConfig ? null : clientHook.error;
2524
+ const contentSource = clientHook.source;
2431
2525
  const [variantCache, setVariantCache] = react.useState({});
2432
2526
  react.useEffect(() => {
2433
2527
  if (typeof window !== "undefined") {
@@ -2569,6 +2663,9 @@ function AmplifyRenderer({
2569
2663
  }, [pageConfig]);
2570
2664
  react.useEffect(() => {
2571
2665
  if (!pageConfig?.layoutTree || !pageConfig.layoutId) return;
2666
+ const roots = pageConfig.layoutTree;
2667
+ const fromEdge = contentSource === "edge" || roots.some((n) => n._contentSource === "edge");
2668
+ if (fromEdge) return;
2572
2669
  const sdk = getSDK();
2573
2670
  const instanceId = sdk.getInstanceId();
2574
2671
  const decisionApiUrl = sdk.getDecisionApiUrl();
@@ -2663,7 +2760,7 @@ function AmplifyRenderer({
2663
2760
  setVariantCache(newCache);
2664
2761
  };
2665
2762
  fetchVariants();
2666
- }, [pageConfig?.layoutTree, pageConfig?.layoutId, pageConfig?.route]);
2763
+ }, [pageConfig?.layoutTree, pageConfig?.layoutId, pageConfig?.route, contentSource]);
2667
2764
  if (loading) {
2668
2765
  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: [
2669
2766
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-gray-200 rounded w-1/4 mb-4" }),