@amplifyup/sdk 0.1.42 → 0.1.44

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.
@@ -405,6 +405,153 @@ function isComposerPreview() {
405
405
  return false;
406
406
  }
407
407
 
408
+ // src/page-context.ts
409
+ var defaultLog = {
410
+ warn: (...args) => {
411
+ if (typeof console !== "undefined") console.warn(...args);
412
+ }
413
+ };
414
+ function coerceField(def, raw, log) {
415
+ if (raw == null) return void 0;
416
+ if (def.multiValue) {
417
+ const list = Array.isArray(raw) ? raw : [raw];
418
+ const out = [];
419
+ for (const item of list) {
420
+ const coerced = coerceScalar(def, item, log);
421
+ if (coerced !== void 0) out.push(coerced);
422
+ }
423
+ return out;
424
+ }
425
+ return coerceScalar(def, raw, log);
426
+ }
427
+ function coerceScalar(def, raw, log) {
428
+ switch (def.dataType) {
429
+ case "string":
430
+ if (typeof raw === "string") return raw;
431
+ if (typeof raw === "number" || typeof raw === "boolean") return String(raw);
432
+ log.warn(`AmplifyUp: dropped field "${def.key}" (expected string)`);
433
+ return void 0;
434
+ case "number": {
435
+ if (typeof raw === "number" && Number.isFinite(raw)) return raw;
436
+ if (typeof raw === "string" && raw.trim() !== "" && Number.isFinite(Number(raw))) {
437
+ return Number(raw);
438
+ }
439
+ log.warn(`AmplifyUp: dropped field "${def.key}" (expected number)`);
440
+ return void 0;
441
+ }
442
+ case "boolean": {
443
+ if (typeof raw === "boolean") return raw;
444
+ if (raw === "true") return true;
445
+ if (raw === "false") return false;
446
+ log.warn(`AmplifyUp: dropped field "${def.key}" (expected boolean)`);
447
+ return void 0;
448
+ }
449
+ case "date": {
450
+ if (typeof raw === "string" && !Number.isNaN(Date.parse(raw))) return raw;
451
+ log.warn(`AmplifyUp: dropped field "${def.key}" (expected date)`);
452
+ return void 0;
453
+ }
454
+ default:
455
+ return void 0;
456
+ }
457
+ }
458
+ function applyPageContext(input, opts) {
459
+ const log = opts.log || defaultLog;
460
+ const key = String(input.key || "").trim();
461
+ if (!key || !input.resourceType) {
462
+ log.warn("AmplifyUp: pageContext needs resourceType and key");
463
+ return null;
464
+ }
465
+ const slot = opts.pageData?.slots?.[0];
466
+ if (slot && slot.type && slot.type !== input.resourceType) {
467
+ log.warn(
468
+ `AmplifyUp: pageContext resourceType "${input.resourceType}" does not match page slot "${slot.type}"`
469
+ );
470
+ return null;
471
+ }
472
+ const fieldDefs = slot?.fields || [];
473
+ const defByKey = new Map(fieldDefs.map((f) => [f.key, f]));
474
+ const rawFields = input.fields && typeof input.fields === "object" ? input.fields : {};
475
+ const fields = {};
476
+ if (fieldDefs.length === 0) {
477
+ Object.assign(fields, rawFields);
478
+ } else {
479
+ for (const [k, v] of Object.entries(rawFields)) {
480
+ const def = defByKey.get(k);
481
+ if (!def) {
482
+ log.warn(`AmplifyUp: unknown resource field "${k}" dropped`);
483
+ continue;
484
+ }
485
+ const coerced = coerceField(def, v, log);
486
+ if (coerced !== void 0) fields[k] = coerced;
487
+ }
488
+ }
489
+ const params = { ...opts.routeParams || {} };
490
+ const mapping = slot?.urlParamMapping;
491
+ let resourceKey = key;
492
+ if (mapping) {
493
+ const fromUrl = params[mapping.param];
494
+ const fromField = fields[mapping.field];
495
+ if (fromUrl) resourceKey = String(fromUrl);
496
+ if (fromUrl && fromField != null && String(fromField) !== String(fromUrl)) {
497
+ log.warn(
498
+ `AmplifyUp: resource key mismatch \u2014 URL :${mapping.param} is "${fromUrl}" but ${mapping.field} is "${String(fromField)}". Using the URL param.`
499
+ );
500
+ }
501
+ }
502
+ const pageFields = {};
503
+ for (const pf of opts.pageData?.pageFields || []) {
504
+ if (pf.source === "static") {
505
+ if (pf.value !== void 0) pageFields[pf.key] = pf.value;
506
+ continue;
507
+ }
508
+ if (pf.source === "mapped" && pf.slot && pf.field) {
509
+ pageFields[pf.key] = fields[pf.field];
510
+ }
511
+ }
512
+ return {
513
+ resource: {
514
+ type: input.resourceType,
515
+ key: resourceKey,
516
+ params,
517
+ fields
518
+ },
519
+ page: { fields: pageFields },
520
+ pageRoute: opts.pageRoute || ""
521
+ };
522
+ }
523
+ function staticPageFieldsFromSnapshot(pageData) {
524
+ const pageFields = {};
525
+ for (const pf of pageData?.pageFields || []) {
526
+ if (pf.source === "static" && pf.value !== void 0) {
527
+ pageFields[pf.key] = pf.value;
528
+ }
529
+ }
530
+ return pageFields;
531
+ }
532
+ function attachStaticPageContext(pageData, pageRoute) {
533
+ if (!pageData) return null;
534
+ const fields = staticPageFieldsFromSnapshot(pageData);
535
+ const hasStatic = Object.keys(fields).length > 0;
536
+ const hasRoute = !!pageRoute;
537
+ if (!hasStatic && !hasRoute) return null;
538
+ return {
539
+ page: { fields },
540
+ pageRoute: pageRoute || ""
541
+ };
542
+ }
543
+ function mergeEventContext(properties, attached) {
544
+ if (!attached) return properties;
545
+ return {
546
+ ...properties,
547
+ ...attached.resource ? { resource: attached.resource } : {},
548
+ page: attached.page,
549
+ ...attached.pageRoute ? { pageRoute: attached.pageRoute } : {}
550
+ };
551
+ }
552
+ function setPageSlotFields(fields) {
553
+ }
554
+
408
555
  // src/index.ts
409
556
  var DWELL_ACTIVITY_IDLE_MS = 2 * 60 * 1e3;
410
557
  var DWELL_POLL_MS = 5e3;
@@ -473,6 +620,11 @@ var AmplifyUpSDK = class {
473
620
  this.pageEngagementActive = false;
474
621
  /** Path of the last page() / routeChange — avoids duplicate SPA fires. */
475
622
  this.lastTrackedPath = null;
623
+ this.pendingPageContext = null;
624
+ this.attachedPageContext = null;
625
+ this.resolvedPageData = null;
626
+ this.resolvedPageRoute = "";
627
+ this.resolvedRouteParams = {};
476
628
  this.historyHooksInstalled = false;
477
629
  this.pageEngagementState = {
478
630
  reached: /* @__PURE__ */ new Set(),
@@ -635,8 +787,13 @@ var AmplifyUpSDK = class {
635
787
  // Enable debug by default
636
788
  target: this.target,
637
789
  dev: config.dev,
638
- ...config.siteId ? { siteId: config.siteId } : {}
790
+ ...config.siteId ? { siteId: config.siteId } : {},
791
+ ...config.pageContext ? { pageContext: config.pageContext } : {}
639
792
  };
793
+ if (config.pageContext) {
794
+ this.pendingPageContext = config.pageContext;
795
+ this.recomputeAttachedContext();
796
+ }
640
797
  try {
641
798
  const adapterConfig = {
642
799
  ...this.config,
@@ -993,6 +1150,43 @@ var AmplifyUpSDK = class {
993
1150
  this.componentViewObservers.forEach((obs) => obs.disconnect());
994
1151
  this.componentViewObservers = [];
995
1152
  }
1153
+ /**
1154
+ * Set resource context for this page. Prefer passing `pageContext` on
1155
+ * AmplifyUpProvider / init — required for components using From page.
1156
+ * Late calls still attach to subsequent events (not a second page view).
1157
+ */
1158
+ setPageContext(input) {
1159
+ this.pendingPageContext = input;
1160
+ this.recomputeAttachedContext();
1161
+ }
1162
+ /**
1163
+ * Apply published pageData (from /v1/resolve) so field validation and
1164
+ * mapped page fields can run.
1165
+ */
1166
+ applyResolvedPageData(pageData, pageRoute, routeParams) {
1167
+ this.resolvedPageData = pageData || null;
1168
+ if (pageRoute) this.resolvedPageRoute = pageRoute;
1169
+ if (routeParams) this.resolvedRouteParams = routeParams;
1170
+ this.recomputeAttachedContext();
1171
+ }
1172
+ recomputeAttachedContext() {
1173
+ if (!this.pendingPageContext) {
1174
+ this.attachedPageContext = attachStaticPageContext(
1175
+ this.resolvedPageData,
1176
+ this.resolvedPageRoute
1177
+ );
1178
+ return;
1179
+ }
1180
+ this.attachedPageContext = applyPageContext(this.pendingPageContext, {
1181
+ pageData: this.resolvedPageData,
1182
+ routeParams: this.resolvedRouteParams,
1183
+ pageRoute: this.resolvedPageRoute
1184
+ });
1185
+ setPageSlotFields(this.attachedPageContext?.resource?.fields || null);
1186
+ }
1187
+ getAttachedPageContext() {
1188
+ return this.attachedPageContext;
1189
+ }
996
1190
  /**
997
1191
  * Track a page view
998
1192
  */
@@ -1014,7 +1208,7 @@ var AmplifyUpSDK = class {
1014
1208
  sessionId,
1015
1209
  trackingId: this.config?.trackingId,
1016
1210
  // Timestamp is included in event payload
1017
- ...properties
1211
+ ...mergeEventContext(properties || {}, this.attachedPageContext)
1018
1212
  };
1019
1213
  this.adapter.page(pageProperties);
1020
1214
  this.lastTrackedPath = url;
@@ -1038,7 +1232,7 @@ var AmplifyUpSDK = class {
1038
1232
  url: typeof window !== "undefined" ? window.location.href : void 0,
1039
1233
  path: properties?.path || (typeof window !== "undefined" ? window.location.pathname : void 0),
1040
1234
  // Timestamp is included in event payload
1041
- ...properties
1235
+ ...mergeEventContext(properties || {}, this.attachedPageContext)
1042
1236
  };
1043
1237
  if (!trackProperties.path && typeof window !== "undefined") {
1044
1238
  trackProperties.path = window.location.pathname;
@@ -1363,8 +1557,15 @@ Available Console Commands:
1363
1557
  });
1364
1558
  if (preview) {
1365
1559
  params.set("preview", "true");
1366
- if (typeof window !== "undefined" && isWorkingContentRequest(new URLSearchParams(window.location.search))) {
1367
- params.set("working", "true");
1560
+ if (typeof window !== "undefined") {
1561
+ const pageSearch = new URLSearchParams(window.location.search);
1562
+ if (isWorkingContentRequest(pageSearch)) {
1563
+ params.set("working", "true");
1564
+ }
1565
+ const zoneId = pageSearch.get("zoneId");
1566
+ const compositionId = pageSearch.get("compositionId");
1567
+ if (zoneId) params.set("zoneId", zoneId);
1568
+ if (compositionId) params.set("compositionId", compositionId);
1368
1569
  }
1369
1570
  }
1370
1571
  if (isOnOrchestratorOrigin) {