@amplifyup/sdk 0.1.67 → 0.1.69

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.
@@ -434,6 +434,13 @@ function isFieldEnvelope(value) {
434
434
  const rec = value;
435
435
  return typeof rec.name === "string" && "value" in rec && !("url" in rec);
436
436
  }
437
+ var OBJECT_FIELD_KINDS = /* @__PURE__ */ new Set(["object", "array", "list"]);
438
+ function isLeafFieldKind(kind) {
439
+ if (typeof kind !== "string") return false;
440
+ const normalized = kind.trim().toLowerCase();
441
+ if (!normalized) return false;
442
+ return !OBJECT_FIELD_KINDS.has(normalized);
443
+ }
437
444
  function isPageResourceSentinel(value) {
438
445
  return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.__amplifyupPageResource === "string";
439
446
  }
@@ -570,6 +577,10 @@ function toFieldEnvelopes(source, prefix = "", scope) {
570
577
  out[key] = { value: envelopeListRows(raw, owner), name: path };
571
578
  continue;
572
579
  }
580
+ if (ctx.leaf?.(path)) {
581
+ out[key] = withLeaf({ value: emptyValue(raw), name: path }, ctx);
582
+ continue;
583
+ }
573
584
  if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !raw.$$typeof) {
574
585
  out[key] = toFieldEnvelopes(raw, path, ctx);
575
586
  continue;
@@ -598,15 +609,33 @@ function envelopeListRow(item, owner) {
598
609
  }
599
610
  return row;
600
611
  }
612
+ var warnedMissingFieldKind = /* @__PURE__ */ new Set();
613
+ function warnMissingFieldKind(fieldName) {
614
+ if (process.env.NODE_ENV === "production") return;
615
+ if (warnedMissingFieldKind.has(fieldName)) return;
616
+ warnedMissingFieldKind.add(fieldName);
617
+ console.warn(
618
+ `[AmplifyUp SDK] Field "${fieldName}" has no schema kind; using value-shape detection. Republish the component definition so its fields carry a type.`
619
+ );
620
+ }
601
621
  function attachFieldEnvelopes(props, sources) {
602
622
  const raw = props && typeof props === "object" ? { ...props } : {};
603
623
  if (isBrandedFields(raw.fields)) return raw;
604
624
  const { fields: existing, ...content } = raw;
605
625
  const source = existing && typeof existing === "object" && !isFieldEnvelope(existing) ? existing : content;
626
+ const { reads } = normalizeEnvelopeSources(sources);
627
+ if (reads) {
628
+ for (const [fieldName, read] of Object.entries(reads)) {
629
+ if (!read?.kind) warnMissingFieldKind(fieldName);
630
+ }
631
+ }
606
632
  return {
607
633
  ...raw,
608
634
  fields: toFieldEnvelopes(source, "", {
609
- owners: (fieldName) => listOwnerForField(fieldName, source, sources)
635
+ owners: (fieldName) => listOwnerForField(fieldName, source, sources),
636
+ // Top-level field paths equal field names; nested paths have no schema
637
+ // entry and fall back to shape-based handling.
638
+ leaf: (path) => isLeafFieldKind(reads?.[path]?.kind)
610
639
  })
611
640
  };
612
641
  }
@@ -853,6 +882,8 @@ function hydrateFieldsBag(bag, fields) {
853
882
  if (val && typeof val === "object" && !Array.isArray(val) && typeof val.name === "string" && "value" in val && !("url" in val)) {
854
883
  const env = val;
855
884
  out[key] = {
885
+ // Keep ref / readOnly / source — dropping them severs the write target.
886
+ ...val,
856
887
  name: env.name,
857
888
  value: isPageResourceSentinel2(env.value) ? hydratePageResourceValue(env.value, fields) : env.value
858
889
  };
@@ -934,6 +965,46 @@ function sdkDebugOnce(key, ...args) {
934
965
  console.log(...args);
935
966
  }
936
967
 
968
+ // src/content/layoutProviderCache.ts
969
+ var SETTLED_TTL_MS = 5e3;
970
+ var inflight = /* @__PURE__ */ new Map();
971
+ var settled = /* @__PURE__ */ new Map();
972
+ function layoutProviderCacheKey(opts) {
973
+ const route = (opts.route || "/").split("?")[0] || "/";
974
+ return [
975
+ route,
976
+ opts.trackingId || "",
977
+ opts.instanceId || "",
978
+ opts.preview ? "1" : "0",
979
+ opts.working ? "1" : "0",
980
+ opts.zoneId || "",
981
+ opts.compositionId || ""
982
+ ].join("|");
983
+ }
984
+ function getSettledLayoutProvider(key) {
985
+ const entry = settled.get(key);
986
+ if (!entry) return void 0;
987
+ if (Date.now() - entry.at > SETTLED_TTL_MS) {
988
+ settled.delete(key);
989
+ return void 0;
990
+ }
991
+ return entry.value;
992
+ }
993
+ function dedupeLayoutProviderFetch(key, fetchFn) {
994
+ const cached = getSettledLayoutProvider(key);
995
+ if (cached !== void 0) return Promise.resolve(cached);
996
+ const existing = inflight.get(key);
997
+ if (existing) return existing;
998
+ const pending = fetchFn().then((value) => {
999
+ settled.set(key, { value, at: Date.now() });
1000
+ return value;
1001
+ }).finally(() => {
1002
+ inflight.delete(key);
1003
+ });
1004
+ inflight.set(key, pending);
1005
+ return pending;
1006
+ }
1007
+
937
1008
  // src/index.ts
938
1009
  var DWELL_ACTIVITY_IDLE_MS = 2 * 60 * 1e3;
939
1010
  var DWELL_POLL_MS = 5e3;
@@ -1975,6 +2046,32 @@ Available Console Commands:
1975
2046
  */
1976
2047
  async fetchPageConfig(route, preview = false) {
1977
2048
  const url = this.buildLayoutProviderUrl(route, preview);
2049
+ const pageSearch = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
2050
+ const cacheKey = layoutProviderCacheKey({
2051
+ route,
2052
+ trackingId: this.getTrackingId(),
2053
+ instanceId: getInstanceId(),
2054
+ preview,
2055
+ working: pageSearch ? isWorkingContentRequest(pageSearch) : false,
2056
+ zoneId: pageSearch?.get("zoneId"),
2057
+ compositionId: pageSearch?.get("compositionId")
2058
+ });
2059
+ return dedupeLayoutProviderFetch(cacheKey, async () => {
2060
+ const startedAt = Date.now();
2061
+ sdkDebug("[AmplifyUp SDK] layout/provider request", {
2062
+ route,
2063
+ at: new Date(startedAt).toISOString()
2064
+ });
2065
+ const page = await this.fetchPageConfigUncached(url);
2066
+ sdkDebug("[AmplifyUp SDK] layout/provider response", {
2067
+ route,
2068
+ durationMs: Date.now() - startedAt,
2069
+ hasPage: !!page
2070
+ });
2071
+ return page;
2072
+ });
2073
+ }
2074
+ async fetchPageConfigUncached(url) {
1978
2075
  const response = await fetch(url);
1979
2076
  if (!response.ok) {
1980
2077
  if (response.status === 404) {
@@ -2333,6 +2430,12 @@ Available Console Commands:
2333
2430
  );
2334
2431
  return;
2335
2432
  }
2433
+ if (event.data?.type === "AMPLIFYUP_LAYOUT_PATCH") {
2434
+ window.dispatchEvent(
2435
+ new CustomEvent("amplifyup:layout-patch", { detail: event.data })
2436
+ );
2437
+ return;
2438
+ }
2336
2439
  if (event.data?.type === "COMPONENT_DELETABLE_STATUS") {
2337
2440
  const { componentId, deletable, nodeId } = event.data;
2338
2441
  const element = document.querySelector(`[data-component-id="${componentId}"]`);
@@ -3373,6 +3476,16 @@ function getSDK(adapter) {
3373
3476
  var trackContentView = (contentId, contentType, properties) => getSDK().trackContentView(contentId, contentType, properties);
3374
3477
  var refreshEventTracking = () => getSDK().refreshEventTracking();
3375
3478
  getSDK();
3479
+ function useComposerPreviewFlag(force = false) {
3480
+ const [detected, setDetected] = useState(false);
3481
+ useEffect(() => {
3482
+ if (!force && isComposerPreview()) setDetected(true);
3483
+ }, [force]);
3484
+ return force || detected;
3485
+ }
3486
+ createContext({
3487
+ ready: true
3488
+ });
3376
3489
 
3377
3490
  // src/composer-store.ts
3378
3491
  var ComposerFieldStore = class {
@@ -3589,16 +3702,9 @@ function NodeWithFields({
3589
3702
  renderComponent,
3590
3703
  composerPreview
3591
3704
  }) {
3592
- return /* @__PURE__ */ jsx(
3593
- MaybeComposerProvider,
3594
- {
3595
- enabled: !!(composerPreview || isComposerPreview()),
3596
- layoutNodeId: nodeId,
3597
- children: renderComponent(componentId, props, slots, {
3598
- layoutNodeId: nodeId
3599
- })
3600
- }
3601
- );
3705
+ return /* @__PURE__ */ jsx(MaybeComposerProvider, { enabled: !!composerPreview, layoutNodeId: nodeId, children: renderComponent(componentId, props, slots, {
3706
+ layoutNodeId: nodeId
3707
+ }) });
3602
3708
  }
3603
3709
  function AmplifyRendererInner({
3604
3710
  renderComponent,
@@ -3609,6 +3715,7 @@ function AmplifyRendererInner({
3609
3715
  composerPreview
3610
3716
  }) {
3611
3717
  const clientHook = useAmplifyUp();
3718
+ const composerPreviewFlag = useComposerPreviewFlag(!!composerPreview);
3612
3719
  const pageConfig = serverPageConfig || clientHook.pageConfig || null;
3613
3720
  const loading = serverPageConfig ? false : clientHook.loading;
3614
3721
  const error = serverPageConfig ? null : clientHook.error;
@@ -3961,7 +4068,7 @@ function AmplifyRendererInner({
3961
4068
  props: nodeRenderProps(node),
3962
4069
  slots: slotsToPass,
3963
4070
  renderComponent,
3964
- composerPreview
4071
+ composerPreview: composerPreviewFlag
3965
4072
  }
3966
4073
  )
3967
4074
  }
@@ -3974,12 +4081,13 @@ function AmplifyRendererInner({
3974
4081
  if (layoutTree.length === 0) {
3975
4082
  return /* @__PURE__ */ jsx("main", { children: emptyComponent || /* @__PURE__ */ jsx("div", { className: "container mx-auto px-4 py-16", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "No content configured for this page." }) }) });
3976
4083
  }
3977
- return /* @__PURE__ */ jsx(ComposerProvider, { children: /* @__PURE__ */ jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsx(
4084
+ return /* @__PURE__ */ jsx(ComposerProvider, { children: /* @__PURE__ */ jsx("main", { children: composerPreviewFlag ? /* @__PURE__ */ jsx(
3978
4085
  "div",
3979
4086
  {
3980
4087
  "data-slot-id": "main",
3981
4088
  "data-amplifyup-slot": "main",
3982
4089
  "data-slot-source": "page",
4090
+ "data-slot-state": layoutTree.length === 0 ? "empty" : "filled",
3983
4091
  "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
3984
4092
  children: layoutTree.map((node) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node) }, node.id))
3985
4093
  }