@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.
package/dist/react.js CHANGED
@@ -1,13 +1,13 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var React2 = require('react');
4
+ var React3 = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
  var marked = require('marked');
7
7
 
8
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
 
10
- var React2__default = /*#__PURE__*/_interopDefault(React2);
10
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
11
11
 
12
12
  /* @amplifyup/sdk - https://amplifyup.ai */
13
13
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -440,6 +440,13 @@ function isFieldEnvelope(value) {
440
440
  const rec = value;
441
441
  return typeof rec.name === "string" && "value" in rec && !("url" in rec);
442
442
  }
443
+ var OBJECT_FIELD_KINDS = /* @__PURE__ */ new Set(["object", "array", "list"]);
444
+ function isLeafFieldKind(kind) {
445
+ if (typeof kind !== "string") return false;
446
+ const normalized = kind.trim().toLowerCase();
447
+ if (!normalized) return false;
448
+ return !OBJECT_FIELD_KINDS.has(normalized);
449
+ }
443
450
  function isPageResourceSentinel(value) {
444
451
  return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.__amplifyupPageResource === "string";
445
452
  }
@@ -579,6 +586,10 @@ function toFieldEnvelopes(source, prefix = "", scope) {
579
586
  out[key] = { value: envelopeListRows(raw, owner), name: path };
580
587
  continue;
581
588
  }
589
+ if (ctx.leaf?.(path)) {
590
+ out[key] = withLeaf({ value: emptyValue(raw), name: path }, ctx);
591
+ continue;
592
+ }
582
593
  if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !raw.$$typeof) {
583
594
  out[key] = toFieldEnvelopes(raw, path, ctx);
584
595
  continue;
@@ -607,15 +618,33 @@ function envelopeListRow(item, owner) {
607
618
  }
608
619
  return row;
609
620
  }
621
+ var warnedMissingFieldKind = /* @__PURE__ */ new Set();
622
+ function warnMissingFieldKind(fieldName) {
623
+ if (process.env.NODE_ENV === "production") return;
624
+ if (warnedMissingFieldKind.has(fieldName)) return;
625
+ warnedMissingFieldKind.add(fieldName);
626
+ console.warn(
627
+ `[AmplifyUp SDK] Field "${fieldName}" has no schema kind; using value-shape detection. Republish the component definition so its fields carry a type.`
628
+ );
629
+ }
610
630
  function attachFieldEnvelopes(props, sources) {
611
631
  const raw = props && typeof props === "object" ? { ...props } : {};
612
632
  if (isBrandedFields(raw.fields)) return raw;
613
633
  const { fields: existing, ...content } = raw;
614
634
  const source = existing && typeof existing === "object" && !isFieldEnvelope(existing) ? existing : content;
635
+ const { reads } = normalizeEnvelopeSources(sources);
636
+ if (reads) {
637
+ for (const [fieldName, read] of Object.entries(reads)) {
638
+ if (!read?.kind) warnMissingFieldKind(fieldName);
639
+ }
640
+ }
615
641
  return {
616
642
  ...raw,
617
643
  fields: toFieldEnvelopes(source, "", {
618
- owners: (fieldName) => listOwnerForField(fieldName, source, sources)
644
+ owners: (fieldName) => listOwnerForField(fieldName, source, sources),
645
+ // Top-level field paths equal field names; nested paths have no schema
646
+ // entry and fall back to shape-based handling.
647
+ leaf: (path) => isLeafFieldKind(reads?.[path]?.kind)
619
648
  })
620
649
  };
621
650
  }
@@ -890,6 +919,8 @@ function hydrateFieldsBag(bag, fields) {
890
919
  if (val && typeof val === "object" && !Array.isArray(val) && typeof val.name === "string" && "value" in val && !("url" in val)) {
891
920
  const env = val;
892
921
  out[key] = {
922
+ // Keep ref / readOnly / source — dropping them severs the write target.
923
+ ...val,
893
924
  name: env.name,
894
925
  value: isPageResourceSentinel2(env.value) ? hydratePageResourceValue(env.value, fields) : env.value
895
926
  };
@@ -971,6 +1002,51 @@ function sdkDebugOnce(key, ...args) {
971
1002
  console.log(...args);
972
1003
  }
973
1004
 
1005
+ // src/content/layoutProviderCache.ts
1006
+ var SETTLED_TTL_MS = 5e3;
1007
+ var inflight = /* @__PURE__ */ new Map();
1008
+ var settled = /* @__PURE__ */ new Map();
1009
+ function layoutProviderCacheKey(opts) {
1010
+ const route = (opts.route || "/").split("?")[0] || "/";
1011
+ return [
1012
+ route,
1013
+ opts.trackingId || "",
1014
+ opts.instanceId || "",
1015
+ opts.preview ? "1" : "0",
1016
+ opts.working ? "1" : "0",
1017
+ opts.zoneId || "",
1018
+ opts.compositionId || ""
1019
+ ].join("|");
1020
+ }
1021
+ function getSettledLayoutProvider(key) {
1022
+ const entry = settled.get(key);
1023
+ if (!entry) return void 0;
1024
+ if (Date.now() - entry.at > SETTLED_TTL_MS) {
1025
+ settled.delete(key);
1026
+ return void 0;
1027
+ }
1028
+ return entry.value;
1029
+ }
1030
+ function dedupeLayoutProviderFetch(key, fetchFn) {
1031
+ const cached = getSettledLayoutProvider(key);
1032
+ if (cached !== void 0) return Promise.resolve(cached);
1033
+ const existing = inflight.get(key);
1034
+ if (existing) return existing;
1035
+ const pending = fetchFn().then((value) => {
1036
+ settled.set(key, { value, at: Date.now() });
1037
+ return value;
1038
+ }).finally(() => {
1039
+ inflight.delete(key);
1040
+ });
1041
+ inflight.set(key, pending);
1042
+ return pending;
1043
+ }
1044
+ function previewLayoutReady(opts) {
1045
+ if (opts.hasClientLayout) return true;
1046
+ if (opts.clientLoading) return false;
1047
+ return true;
1048
+ }
1049
+
974
1050
  // src/edge/normalizeRoute.ts
975
1051
  function normalizeRoute(route) {
976
1052
  let path = String(route || "").trim();
@@ -2384,6 +2460,32 @@ Available Console Commands:
2384
2460
  */
2385
2461
  async fetchPageConfig(route, preview = false) {
2386
2462
  const url = this.buildLayoutProviderUrl(route, preview);
2463
+ const pageSearch = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
2464
+ const cacheKey = layoutProviderCacheKey({
2465
+ route,
2466
+ trackingId: this.getTrackingId(),
2467
+ instanceId: getInstanceId(),
2468
+ preview,
2469
+ working: pageSearch ? isWorkingContentRequest(pageSearch) : false,
2470
+ zoneId: pageSearch?.get("zoneId"),
2471
+ compositionId: pageSearch?.get("compositionId")
2472
+ });
2473
+ return dedupeLayoutProviderFetch(cacheKey, async () => {
2474
+ const startedAt = Date.now();
2475
+ sdkDebug("[AmplifyUp SDK] layout/provider request", {
2476
+ route,
2477
+ at: new Date(startedAt).toISOString()
2478
+ });
2479
+ const page = await this.fetchPageConfigUncached(url);
2480
+ sdkDebug("[AmplifyUp SDK] layout/provider response", {
2481
+ route,
2482
+ durationMs: Date.now() - startedAt,
2483
+ hasPage: !!page
2484
+ });
2485
+ return page;
2486
+ });
2487
+ }
2488
+ async fetchPageConfigUncached(url) {
2387
2489
  const response = await fetch(url);
2388
2490
  if (!response.ok) {
2389
2491
  if (response.status === 404) {
@@ -2742,6 +2844,12 @@ Available Console Commands:
2742
2844
  );
2743
2845
  return;
2744
2846
  }
2847
+ if (event.data?.type === "AMPLIFYUP_LAYOUT_PATCH") {
2848
+ window.dispatchEvent(
2849
+ new CustomEvent("amplifyup:layout-patch", { detail: event.data })
2850
+ );
2851
+ return;
2852
+ }
2745
2853
  if (event.data?.type === "COMPONENT_DELETABLE_STATUS") {
2746
2854
  const { componentId, deletable, nodeId } = event.data;
2747
2855
  const element = document.querySelector(`[data-component-id="${componentId}"]`);
@@ -3782,6 +3890,13 @@ function getSDK(adapter) {
3782
3890
  var trackContentView = (contentId, contentType, properties) => getSDK().trackContentView(contentId, contentType, properties);
3783
3891
  var refreshEventTracking = () => getSDK().refreshEventTracking();
3784
3892
  getSDK();
3893
+ function useComposerPreviewFlag(force = false) {
3894
+ const [detected, setDetected] = React3.useState(false);
3895
+ React3.useEffect(() => {
3896
+ if (!force && isComposerPreview()) setDetected(true);
3897
+ }, [force]);
3898
+ return force || detected;
3899
+ }
3785
3900
 
3786
3901
  // src/composer-store.ts
3787
3902
  var ComposerFieldStore = class {
@@ -3824,19 +3939,19 @@ var ComposerFieldStore = class {
3824
3939
  for (const fn of set) fn();
3825
3940
  }
3826
3941
  };
3827
- var ComposerStoreContext = React2.createContext(null);
3828
- var ComposerNodeContext = React2.createContext(void 0);
3942
+ var ComposerStoreContext = React3.createContext(null);
3943
+ var ComposerNodeContext = React3.createContext(void 0);
3829
3944
  function applyComposerPatch(store, name, value, ref, layoutNodeId) {
3830
3945
  store.set(composerStoreKey(name, ref, layoutNodeId), value);
3831
3946
  }
3832
3947
  function ComposerProvider({
3833
3948
  children
3834
3949
  }) {
3835
- const existing = React2.useContext(ComposerStoreContext);
3836
- const storeRef = React2.useRef(null);
3950
+ const existing = React3.useContext(ComposerStoreContext);
3951
+ const storeRef = React3.useRef(null);
3837
3952
  if (!storeRef.current) storeRef.current = new ComposerFieldStore();
3838
3953
  const store = existing || storeRef.current;
3839
- React2.useEffect(() => {
3954
+ React3.useEffect(() => {
3840
3955
  if (existing) return;
3841
3956
  const applyField = (fieldName, value, nodeId, ref) => {
3842
3957
  if (!fieldName || typeof fieldName !== "string") return;
@@ -3900,10 +4015,10 @@ function ComposerProvider({
3900
4015
  return /* @__PURE__ */ jsxRuntime.jsx(ComposerStoreContext.Provider, { value: store, children });
3901
4016
  }
3902
4017
  function useComposerFieldValue(name, fallback, ref) {
3903
- const store = React2.useContext(ComposerStoreContext);
3904
- const layoutNodeId = React2.useContext(ComposerNodeContext);
4018
+ const store = React3.useContext(ComposerStoreContext);
4019
+ const layoutNodeId = React3.useContext(ComposerNodeContext);
3905
4020
  const key = composerStoreKey(name, ref, layoutNodeId);
3906
- const subscribe = React2.useCallback(
4021
+ const subscribe = React3.useCallback(
3907
4022
  (onStoreChange) => {
3908
4023
  if (!store || !name) return () => {
3909
4024
  };
@@ -3911,11 +4026,11 @@ function useComposerFieldValue(name, fallback, ref) {
3911
4026
  },
3912
4027
  [store, name, key]
3913
4028
  );
3914
- const getSnapshot = React2.useCallback(() => {
4029
+ const getSnapshot = React3.useCallback(() => {
3915
4030
  if (!store || !name) return void 0;
3916
4031
  return store.get(key);
3917
4032
  }, [store, name, key]);
3918
- const overlay = React2.useSyncExternalStore(subscribe, getSnapshot, () => void 0);
4033
+ const overlay = React3.useSyncExternalStore(subscribe, getSnapshot, () => void 0);
3919
4034
  return overlay !== void 0 ? overlay : fallback;
3920
4035
  }
3921
4036
  function MaybeComposerProvider({
@@ -3923,7 +4038,7 @@ function MaybeComposerProvider({
3923
4038
  layoutNodeId,
3924
4039
  children
3925
4040
  }) {
3926
- const content = React2.useMemo(() => children, [children]);
4041
+ const content = React3.useMemo(() => children, [children]);
3927
4042
  if (!enabled) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: content });
3928
4043
  return /* @__PURE__ */ jsxRuntime.jsx(ComposerProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ComposerNodeContext.Provider, { value: layoutNodeId, children }) });
3929
4044
  }
@@ -3943,7 +4058,7 @@ function nodeRenderProps(node) {
3943
4058
  fields: branded
3944
4059
  };
3945
4060
  }
3946
- var ComponentRenderBoundary = class extends React2.Component {
4061
+ var ComponentRenderBoundary = class extends React3.Component {
3947
4062
  constructor() {
3948
4063
  super(...arguments);
3949
4064
  this.state = { error: null };
@@ -3998,16 +4113,9 @@ function NodeWithFields({
3998
4113
  renderComponent,
3999
4114
  composerPreview
4000
4115
  }) {
4001
- return /* @__PURE__ */ jsxRuntime.jsx(
4002
- MaybeComposerProvider,
4003
- {
4004
- enabled: !!(composerPreview || isComposerPreview()),
4005
- layoutNodeId: nodeId,
4006
- children: renderComponent(componentId, props, slots, {
4007
- layoutNodeId: nodeId
4008
- })
4009
- }
4010
- );
4116
+ return /* @__PURE__ */ jsxRuntime.jsx(MaybeComposerProvider, { enabled: !!composerPreview, layoutNodeId: nodeId, children: renderComponent(componentId, props, slots, {
4117
+ layoutNodeId: nodeId
4118
+ }) });
4011
4119
  }
4012
4120
  function AmplifyRendererInner({
4013
4121
  renderComponent,
@@ -4018,12 +4126,13 @@ function AmplifyRendererInner({
4018
4126
  composerPreview
4019
4127
  }) {
4020
4128
  const clientHook = useAmplifyUp();
4129
+ const composerPreviewFlag = useComposerPreviewFlag(!!composerPreview);
4021
4130
  const pageConfig = serverPageConfig || clientHook.pageConfig || null;
4022
4131
  const loading = serverPageConfig ? false : clientHook.loading;
4023
4132
  const error = serverPageConfig ? null : clientHook.error;
4024
4133
  const contentSource = clientHook.source;
4025
- const [variantCache, setVariantCache] = React2.useState({});
4026
- React2.useEffect(() => {
4134
+ const [variantCache, setVariantCache] = React3.useState({});
4135
+ React3.useEffect(() => {
4027
4136
  if (typeof window !== "undefined") {
4028
4137
  if (window.__AMPLIFYUP_PREVIEW_MODE__ === true) {
4029
4138
  return;
@@ -4049,7 +4158,7 @@ function AmplifyRendererInner({
4049
4158
  });
4050
4159
  }
4051
4160
  }, [pageConfig]);
4052
- React2.useEffect(() => {
4161
+ React3.useEffect(() => {
4053
4162
  if (!pageConfig?.eventTracking?.length) return;
4054
4163
  const sdk = getSDK();
4055
4164
  if (!sdk.isInitialized()) return;
@@ -4067,7 +4176,7 @@ function AmplifyRendererInner({
4067
4176
  window.clearTimeout(t2);
4068
4177
  };
4069
4178
  }, [pageConfig?.id, pageConfig?.eventTracking]);
4070
- React2.useEffect(() => {
4179
+ React3.useEffect(() => {
4071
4180
  if (!pageConfig) return;
4072
4181
  const t = window.setTimeout(() => {
4073
4182
  getSDK().setupComponentViewTracking();
@@ -4075,7 +4184,7 @@ function AmplifyRendererInner({
4075
4184
  }, 100);
4076
4185
  return () => window.clearTimeout(t);
4077
4186
  }, [variantCache, pageConfig?.id]);
4078
- React2.useEffect(() => {
4187
+ React3.useEffect(() => {
4079
4188
  const isVisualEditorMode = () => {
4080
4189
  if (typeof window === "undefined") return false;
4081
4190
  const urlParams = new URLSearchParams(window.location.search);
@@ -4169,7 +4278,7 @@ function AmplifyRendererInner({
4169
4278
  );
4170
4279
  }
4171
4280
  }, [pageConfig]);
4172
- React2.useEffect(() => {
4281
+ React3.useEffect(() => {
4173
4282
  if (!pageConfig?.layoutTree || !pageConfig.layoutId) return;
4174
4283
  const roots = pageConfig.layoutTree;
4175
4284
  const fromEdge = contentSource === "edge" || roots.some((n) => n._contentSource === "edge");
@@ -4310,7 +4419,7 @@ function AmplifyRendererInner({
4310
4419
  "data-amplifyup-slot": "content",
4311
4420
  "data-parent-node-id": node.id,
4312
4421
  "data-parent-component-id": "AmplifyZone",
4313
- children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(React2.Fragment, { children: renderNode(child) }, child.id))
4422
+ children: children.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(React3.Fragment, { children: renderNode(child) }, child.id))
4314
4423
  }
4315
4424
  )
4316
4425
  }
@@ -4343,7 +4452,7 @@ function AmplifyRendererInner({
4343
4452
  "data-zone-id": slot.zoneId || void 0,
4344
4453
  "data-parent-node-id": node.id,
4345
4454
  "data-parent-component-id": node.componentId,
4346
- children: safeChildren.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(React2.Fragment, { children: renderNode(child) }, child.id))
4455
+ children: safeChildren.filter(Boolean).map((child) => /* @__PURE__ */ jsxRuntime.jsx(React3.Fragment, { children: renderNode(child) }, child.id))
4347
4456
  },
4348
4457
  slot.slotId
4349
4458
  );
@@ -4370,7 +4479,7 @@ function AmplifyRendererInner({
4370
4479
  props: nodeRenderProps(node),
4371
4480
  slots: slotsToPass,
4372
4481
  renderComponent,
4373
- composerPreview
4482
+ composerPreview: composerPreviewFlag
4374
4483
  }
4375
4484
  )
4376
4485
  }
@@ -4383,18 +4492,19 @@ function AmplifyRendererInner({
4383
4492
  if (layoutTree.length === 0) {
4384
4493
  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." }) }) });
4385
4494
  }
4386
- return /* @__PURE__ */ jsxRuntime.jsx(ComposerProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsxRuntime.jsx(
4495
+ return /* @__PURE__ */ jsxRuntime.jsx(ComposerProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("main", { children: composerPreviewFlag ? /* @__PURE__ */ jsxRuntime.jsx(
4387
4496
  "div",
4388
4497
  {
4389
4498
  "data-slot-id": "main",
4390
4499
  "data-amplifyup-slot": "main",
4391
4500
  "data-slot-source": "page",
4501
+ "data-slot-state": layoutTree.length === 0 ? "empty" : "filled",
4392
4502
  "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
4393
- children: layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(React2.Fragment, { children: renderNode(node) }, node.id))
4503
+ children: layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(React3.Fragment, { children: renderNode(node) }, node.id))
4394
4504
  }
4395
- ) : layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(React2.Fragment, { children: renderNode(node) }, node.id)) }) });
4505
+ ) : layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(React3.Fragment, { children: renderNode(node) }, node.id)) }) });
4396
4506
  }
4397
- var AmplifyRenderer = React2.memo(AmplifyRendererInner);
4507
+ var AmplifyRenderer = React3.memo(AmplifyRendererInner);
4398
4508
  function AddComponentIcon({ size = 28 }) {
4399
4509
  return /* @__PURE__ */ jsxRuntime.jsxs(
4400
4510
  "svg",
@@ -4489,16 +4599,34 @@ function EmptySlotHint({
4489
4599
  }
4490
4600
  );
4491
4601
  }
4492
- function defaultPreviewCanvas() {
4602
+ var ComposerLayoutStatusContext = React3.createContext({
4603
+ ready: true
4604
+ });
4605
+ function ComposerLayoutStatusProvider({
4606
+ ready,
4607
+ children
4608
+ }) {
4609
+ return /* @__PURE__ */ jsxRuntime.jsx(ComposerLayoutStatusContext.Provider, { value: { ready }, children });
4610
+ }
4611
+ function useComposerLayoutStatus() {
4612
+ return React3.useContext(ComposerLayoutStatusContext);
4613
+ }
4614
+ function previewPageSlot(state) {
4493
4615
  return /* @__PURE__ */ jsxRuntime.jsx(
4494
4616
  "div",
4495
4617
  {
4496
4618
  "data-slot-id": "main",
4497
4619
  "data-amplifyup-slot": "main",
4498
4620
  "data-slot-source": "page",
4499
- "data-slot-empty": "true",
4500
- style: { minHeight: "50vh", width: "100%", display: "block", position: "relative" },
4501
- children: /* @__PURE__ */ jsxRuntime.jsx(EmptySlotHint, {})
4621
+ "data-slot-state": state,
4622
+ "data-slot-empty": state === "empty" ? "true" : void 0,
4623
+ style: {
4624
+ minHeight: state === "empty" ? "50vh" : void 0,
4625
+ width: "100%",
4626
+ display: "block",
4627
+ position: "relative"
4628
+ },
4629
+ children: state === "empty" ? /* @__PURE__ */ jsxRuntime.jsx(EmptySlotHint, {}) : null
4502
4630
  }
4503
4631
  );
4504
4632
  }
@@ -4532,7 +4660,7 @@ function AmplifyPageContent({
4532
4660
  errorComponent
4533
4661
  }) {
4534
4662
  const client = useAmplifyUp();
4535
- const preview = forceComposerPreview || isComposerPreview();
4663
+ const preview = useComposerPreviewFlag(forceComposerPreview);
4536
4664
  const clientLayout = hasAmplifyLayout(client.pageConfig) ? client.pageConfig : null;
4537
4665
  const serverLayout = hasAmplifyLayout(pageConfigProp) ? pageConfigProp : null;
4538
4666
  let pageConfig = serverLayout ?? clientLayout;
@@ -4545,20 +4673,26 @@ function AmplifyPageContent({
4545
4673
  }
4546
4674
  const loading = !hasAmplifyLayout(pageConfig) && client.loading;
4547
4675
  const error = hasAmplifyLayout(pageConfig) ? null : pageConfigProp ? null : client.error;
4548
- const previewCanvas = previewEmptyComponent ?? defaultPreviewCanvas();
4549
- const previewLoading = previewLoadingComponent ?? previewCanvas;
4676
+ const previewEmpty = previewEmptyComponent ?? previewPageSlot("empty");
4677
+ const previewLoading = previewLoadingComponent ?? previewPageSlot("loading");
4550
4678
  if (preview) {
4551
- return /* @__PURE__ */ jsxRuntime.jsx(
4679
+ const ready = previewLayoutReady({
4680
+ clientLoading: client.loading,
4681
+ hasClientLayout: !!clientLayout});
4682
+ if (!ready) {
4683
+ return /* @__PURE__ */ jsxRuntime.jsx(ComposerLayoutStatusProvider, { ready: false, children: previewLoading });
4684
+ }
4685
+ return /* @__PURE__ */ jsxRuntime.jsx(ComposerLayoutStatusProvider, { ready: true, children: /* @__PURE__ */ jsxRuntime.jsx(
4552
4686
  AmplifyRenderer,
4553
4687
  {
4554
4688
  pageConfig: pageConfig ?? void 0,
4555
4689
  renderComponent,
4556
4690
  composerPreview: true,
4557
4691
  loadingComponent: loadingComponent ?? previewLoading,
4558
- emptyComponent: emptyComponent ?? previewCanvas,
4559
- errorComponent: errorComponent ?? (() => previewCanvas)
4692
+ emptyComponent: emptyComponent ?? previewEmpty,
4693
+ errorComponent: errorComponent ?? (() => previewEmpty)
4560
4694
  }
4561
- );
4695
+ ) });
4562
4696
  }
4563
4697
  if (loading) {
4564
4698
  return loadingComponent ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent }) : null;
@@ -4582,6 +4716,24 @@ function AmplifyPageContent({
4582
4716
  );
4583
4717
  }
4584
4718
 
4719
+ // src/slot-state.ts
4720
+ function resolveSlotState(opts) {
4721
+ if (!opts.layoutReady) return "loading";
4722
+ return opts.hasContent ? "filled" : "empty";
4723
+ }
4724
+ function slotShowsAddComponent(state) {
4725
+ return state === "empty";
4726
+ }
4727
+ function slotStateFromEl(el) {
4728
+ if (!el) return null;
4729
+ const explicit = el.getAttribute("data-slot-state");
4730
+ if (explicit === "loading" || explicit === "empty" || explicit === "filled") {
4731
+ return explicit;
4732
+ }
4733
+ if (el.getAttribute("data-slot-empty") === "true") return "empty";
4734
+ return null;
4735
+ }
4736
+
4585
4737
  // src/composer-edit-mode.ts
4586
4738
  function isComposerEditMode() {
4587
4739
  if (typeof window === "undefined") return false;
@@ -4596,7 +4748,7 @@ function subscribeComposerEdit(onStoreChange) {
4596
4748
  function getComposerEditModeServer() {
4597
4749
  return false;
4598
4750
  }
4599
- var ComponentContext = React2.createContext(null);
4751
+ var ComponentContext = React3.createContext(null);
4600
4752
  function ComponentContextProvider({
4601
4753
  children,
4602
4754
  slots,
@@ -4617,13 +4769,18 @@ function Slot({
4617
4769
  name,
4618
4770
  className
4619
4771
  }) {
4620
- const context = React2.useContext(ComponentContext);
4772
+ const context = React3.useContext(ComponentContext);
4621
4773
  if (!context) {
4622
4774
  console.warn(`[AmplifyUp SDK] Slot "${name}" used outside ComponentContextProvider`);
4623
4775
  return null;
4624
4776
  }
4625
4777
  const slotContent = context.slots?.[name];
4626
- const isEmpty = !slotContent;
4778
+ const { ready: layoutReady } = useComposerLayoutStatus();
4779
+ const slotState = resolveSlotState({
4780
+ layoutReady,
4781
+ hasContent: !!slotContent
4782
+ });
4783
+ const isEmpty = slotState === "empty";
4627
4784
  const isEditorMode = typeof window !== "undefined" && // Check for preview mode flag set by proxy
4628
4785
  (window.__AMPLIFYUP_PREVIEW_MODE__ === true || // Check URL query params
4629
4786
  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)
@@ -4639,6 +4796,7 @@ function Slot({
4639
4796
  `slot:${context.layoutNodeId || ""}:${name}`,
4640
4797
  `[Slot:${name}]`,
4641
4798
  {
4799
+ slotState,
4642
4800
  isEmpty,
4643
4801
  isEditorMode,
4644
4802
  availableSlotKeys: context.slots ? Object.keys(context.slots) : []
@@ -4651,6 +4809,7 @@ function Slot({
4651
4809
  "data-slot-name": name,
4652
4810
  "data-slot-id": name,
4653
4811
  "data-amplifyup-slot": name,
4812
+ "data-slot-state": slotState,
4654
4813
  "data-slot-empty": isEmpty ? "true" : void 0,
4655
4814
  "data-parent-node-id": context.layoutNodeId || void 0,
4656
4815
  "data-parent-component-id": context.componentId || void 0,
@@ -4660,7 +4819,7 @@ function Slot({
4660
4819
  // Only add position relative for overlay positioning in editor mode
4661
4820
  position: isEditorMode ? "relative" : void 0
4662
4821
  },
4663
- children: isEmpty ? isEditorMode ? /* @__PURE__ */ jsxRuntime.jsx(EmptySlotHint, { compact: true }) : /* @__PURE__ */ jsxRuntime.jsx(
4822
+ children: slotState === "loading" ? null : isEmpty ? isEditorMode ? /* @__PURE__ */ jsxRuntime.jsx(EmptySlotHint, { compact: true }) : /* @__PURE__ */ jsxRuntime.jsx(
4664
4823
  "div",
4665
4824
  {
4666
4825
  className: "amplifyup-slot-placeholder",
@@ -4732,7 +4891,7 @@ function canInlineEditRichText(name, value) {
4732
4891
  return true;
4733
4892
  }
4734
4893
  function useComposerEditMode() {
4735
- return React2.useSyncExternalStore(
4894
+ return React3.useSyncExternalStore(
4736
4895
  subscribeComposerEdit,
4737
4896
  isComposerEditMode,
4738
4897
  getComposerEditModeServer
@@ -4810,14 +4969,14 @@ function ComposerInlineField({
4810
4969
  fieldRef,
4811
4970
  rich = false
4812
4971
  }) {
4813
- const [focused, setFocused] = React2.useState(false);
4814
- const elRef = React2.useRef(null);
4972
+ const [focused, setFocused] = React3.useState(false);
4973
+ const elRef = React3.useRef(null);
4815
4974
  const storedEmpty = isComposerEmptyText(value);
4816
4975
  const text = storedEmpty ? "" : formatFieldValue(value);
4817
4976
  const hint = COMPOSER_EMPTY_FIELD_HINT;
4818
4977
  const idleDisplay = storedEmpty ? hint : text;
4819
4978
  const showHint = storedEmpty && !focused;
4820
- React2.useEffect(() => {
4979
+ React3.useEffect(() => {
4821
4980
  const el = elRef.current;
4822
4981
  if (!el) return;
4823
4982
  if (document.activeElement === el) return;
@@ -4935,8 +5094,8 @@ function ComposerInlineHtml({
4935
5094
  componentId,
4936
5095
  fieldRef
4937
5096
  }) {
4938
- const elRef = React2.useRef(null);
4939
- React2.useEffect(() => {
5097
+ const elRef = React3.useRef(null);
5098
+ React3.useEffect(() => {
4940
5099
  const el = elRef.current;
4941
5100
  if (!el) return;
4942
5101
  if (el.innerHTML !== html) el.innerHTML = html;
@@ -4954,7 +5113,7 @@ function ComposerInlineHtml({
4954
5113
  fieldRef
4955
5114
  });
4956
5115
  };
4957
- return React2__default.default.createElement(Tag, {
5116
+ return React3__default.default.createElement(Tag, {
4958
5117
  ref: elRef,
4959
5118
  className,
4960
5119
  "data-amplifyup-field": name,
@@ -5011,6 +5170,18 @@ function warnNotScalarField(component, field, fieldValue) {
5011
5170
  }
5012
5171
  return false;
5013
5172
  }
5173
+ function assertNamedEnvelope(component, field) {
5174
+ if (process.env.NODE_ENV === "production") return;
5175
+ if (!field || typeof field !== "object" || Array.isArray(field)) return;
5176
+ const rec = field;
5177
+ if (!("value" in rec)) return;
5178
+ if (typeof rec.name === "string" && rec.name) return;
5179
+ throw new Error(
5180
+ `[AmplifyUp SDK] ${component} received an envelope-shaped object with a \`value\` but no \`name\` \u2014 a partial envelope was manufactured upstream (keys: ${Object.keys(
5181
+ rec
5182
+ ).join(", ")}). Find the producer; do not strip or rebuild envelopes.`
5183
+ );
5184
+ }
5014
5185
  var warnedReadOnlyFields = /* @__PURE__ */ new Set();
5015
5186
  function warnReadOnlyRowField(name) {
5016
5187
  if (process.env.NODE_ENV === "production") return;
@@ -5041,11 +5212,12 @@ function hasOwnProp(props, key) {
5041
5212
  }
5042
5213
  function Field(props) {
5043
5214
  const { field, value, name, className } = props;
5044
- const context = React2.useContext(ComponentContext);
5215
+ const context = React3.useContext(ComponentContext);
5045
5216
  const composerEdit = useComposerEditMode();
5046
5217
  const hasField = isFieldEnvelope(field);
5047
5218
  const hasValue = hasOwnProp(props, "value");
5048
5219
  const { editName, fieldValue, ref } = useBoundField(field, value, name, "Field");
5220
+ assertNamedEnvelope("Field", field);
5049
5221
  warnProjectionProps("Field", hasField, hasValue, name);
5050
5222
  if (warnNotScalarField("Field", field, fieldValue)) {
5051
5223
  return null;
@@ -5094,7 +5266,7 @@ function RichText(props) {
5094
5266
  as: Tag = "div",
5095
5267
  children
5096
5268
  } = props;
5097
- const context = React2.useContext(ComponentContext);
5269
+ const context = React3.useContext(ComponentContext);
5098
5270
  const composerEdit = useComposerEditMode();
5099
5271
  const childrenValue = typeof children === "string" || typeof children === "number" ? String(children) : void 0;
5100
5272
  const hasField = isFieldEnvelope(field);
@@ -5106,6 +5278,7 @@ function RichText(props) {
5106
5278
  name,
5107
5279
  "RichText"
5108
5280
  );
5281
+ assertNamedEnvelope("RichText", field);
5109
5282
  if (!hasField && !hasValueProp && !name) {
5110
5283
  return null;
5111
5284
  }
@@ -5216,7 +5389,8 @@ function Image(props) {
5216
5389
  height: heightProp,
5217
5390
  ...imgProps
5218
5391
  } = props;
5219
- const context = React2.useContext(ComponentContext);
5392
+ const context = React3.useContext(ComponentContext);
5393
+ assertNamedEnvelope("Image", field);
5220
5394
  const coerced = coerceImageField(field, value, name);
5221
5395
  const boundField = coerced.field;
5222
5396
  const hasField = isFieldEnvelope(boundField);
@@ -5302,7 +5476,7 @@ if (typeof window !== "undefined") {
5302
5476
  } catch {
5303
5477
  }
5304
5478
  }
5305
- var AmplifyUpContext = React2.createContext(null);
5479
+ var AmplifyUpContext = React3.createContext(null);
5306
5480
  function normalizeRoutePath2(route) {
5307
5481
  if (!route) return "";
5308
5482
  const path = route.split("?")[0] || "/";
@@ -5314,12 +5488,12 @@ function AmplifyUpProvider({
5314
5488
  config,
5315
5489
  pageContext
5316
5490
  }) {
5317
- const [initialized, setInitialized] = React2.useState(false);
5318
- const [pageConfig, setPageConfig] = React2.useState(null);
5319
- const [loading, setLoading] = React2.useState(true);
5320
- const [error, setError] = React2.useState(null);
5321
- const [source, setSource] = React2.useState(null);
5322
- const [meta, setMeta] = React2.useState(null);
5491
+ const [initialized, setInitialized] = React3.useState(false);
5492
+ const [pageConfig, setPageConfig] = React3.useState(null);
5493
+ const [loading, setLoading] = React3.useState(true);
5494
+ const [error, setError] = React3.useState(null);
5495
+ const [source, setSource] = React3.useState(null);
5496
+ const [meta, setMeta] = React3.useState(null);
5323
5497
  const getActualRoute = () => {
5324
5498
  if (typeof window === "undefined") return "";
5325
5499
  const currentPath = window.location.pathname;
@@ -5336,15 +5510,15 @@ function AmplifyUpProvider({
5336
5510
  }
5337
5511
  return currentPath;
5338
5512
  };
5339
- const loadedRouteRef = React2.useRef(null);
5513
+ const loadedRouteRef = React3.useRef(null);
5340
5514
  const nextPathname = usePathname ? usePathname() : null;
5341
- const [pathname, setPathname] = React2.useState(() => {
5515
+ const [pathname, setPathname] = React3.useState(() => {
5342
5516
  if (typeof window !== "undefined") {
5343
5517
  return getActualRoute();
5344
5518
  }
5345
5519
  return "";
5346
5520
  });
5347
- React2.useEffect(() => {
5521
+ React3.useEffect(() => {
5348
5522
  if (typeof window !== "undefined") {
5349
5523
  const actualRoute = getActualRoute();
5350
5524
  if (window.location.pathname === "/api/pages/proxy" || window.location.pathname.startsWith("/api/pages/proxy")) {
@@ -5371,11 +5545,11 @@ function AmplifyUpProvider({
5371
5545
  }
5372
5546
  }, [nextPathname]);
5373
5547
  const pageContextKey = pageContext ? `${pageContext.resourceType}:${pageContext.key}` : "";
5374
- React2.useEffect(() => {
5548
+ React3.useEffect(() => {
5375
5549
  if (!initialized || !pageContext) return;
5376
5550
  getSDK().setPageContext(pageContext);
5377
5551
  }, [initialized, pageContextKey]);
5378
- React2.useEffect(() => {
5552
+ React3.useEffect(() => {
5379
5553
  let cancelled = false;
5380
5554
  const sdk = getSDK();
5381
5555
  const run = async () => {
@@ -5452,6 +5626,13 @@ function AmplifyUpProvider({
5452
5626
  description: "This page has not been published yet.",
5453
5627
  layoutTree: []
5454
5628
  };
5629
+ if (result.pageConfig) {
5630
+ sdk.applyResolvedPageData(
5631
+ result.pageConfig.pageData || null,
5632
+ result.pageConfig.route,
5633
+ extractRouteParams(result.pageConfig.route, actualRoute)
5634
+ );
5635
+ }
5455
5636
  setInitialized(true);
5456
5637
  setSource((prev) => prev === result.source ? prev : result.source);
5457
5638
  setMeta(
@@ -5464,13 +5645,6 @@ function AmplifyUpProvider({
5464
5645
  }
5465
5646
  return nextConfig;
5466
5647
  });
5467
- if (result.pageConfig) {
5468
- sdk.applyResolvedPageData(
5469
- result.pageConfig.pageData || null,
5470
- result.pageConfig.route,
5471
- extractRouteParams(result.pageConfig.route, actualRoute)
5472
- );
5473
- }
5474
5648
  loadedRouteRef.current = actualRoute;
5475
5649
  } catch (err) {
5476
5650
  if (cancelled) return;
@@ -5497,6 +5671,39 @@ function AmplifyUpProvider({
5497
5671
  config.clientPersonalization,
5498
5672
  pageContextKey
5499
5673
  ]);
5674
+ React3.useEffect(() => {
5675
+ const applyLayoutTree = (layoutTree) => {
5676
+ if (!Array.isArray(layoutTree)) return;
5677
+ setPageConfig((prev) => {
5678
+ const next = envelopePageConfig({
5679
+ ...prev || {
5680
+ id: "patched",
5681
+ route: getActualRoute(),
5682
+ title: "",
5683
+ layoutTree: []
5684
+ },
5685
+ layoutTree
5686
+ });
5687
+ return next;
5688
+ });
5689
+ setLoading(false);
5690
+ };
5691
+ const onMessage = (event) => {
5692
+ if (event.data?.type === "AMPLIFYUP_LAYOUT_PATCH") {
5693
+ applyLayoutTree(event.data.layoutTree);
5694
+ }
5695
+ };
5696
+ const onCustom = (event) => {
5697
+ const detail = event.detail || {};
5698
+ applyLayoutTree(detail.layoutTree);
5699
+ };
5700
+ window.addEventListener("message", onMessage);
5701
+ window.addEventListener("amplifyup:layout-patch", onCustom);
5702
+ return () => {
5703
+ window.removeEventListener("message", onMessage);
5704
+ window.removeEventListener("amplifyup:layout-patch", onCustom);
5705
+ };
5706
+ }, []);
5500
5707
  return /* @__PURE__ */ jsxRuntime.jsx(
5501
5708
  AmplifyUpContext.Provider,
5502
5709
  {
@@ -5506,7 +5713,7 @@ function AmplifyUpProvider({
5506
5713
  );
5507
5714
  }
5508
5715
  function useAmplifyUp() {
5509
- const context = React2.useContext(AmplifyUpContext);
5716
+ const context = React3.useContext(AmplifyUpContext);
5510
5717
  if (!context) {
5511
5718
  throw new Error("useAmplifyUp must be used within AmplifyUpProvider");
5512
5719
  }
@@ -5517,6 +5724,7 @@ exports.AmplifyPageContent = AmplifyPageContent;
5517
5724
  exports.AmplifyRenderer = AmplifyRenderer;
5518
5725
  exports.AmplifyUpProvider = AmplifyUpProvider;
5519
5726
  exports.ComponentContextProvider = ComponentContextProvider;
5727
+ exports.ComposerLayoutStatusProvider = ComposerLayoutStatusProvider;
5520
5728
  exports.ComposerProvider = ComposerProvider;
5521
5729
  exports.Field = Field;
5522
5730
  exports.Image = Image;
@@ -5537,8 +5745,11 @@ exports.isWorkingContentRequest = isWorkingContentRequest;
5537
5745
  exports.listOwnerFromSpec = listOwnerFromSpec;
5538
5746
  exports.nextPageSpec = nextPageSpec;
5539
5747
  exports.queryContent = queryContent;
5748
+ exports.resolveSlotState = resolveSlotState;
5540
5749
  exports.searchSpec = searchSpec;
5541
5750
  exports.selectVariants = selectVariants;
5751
+ exports.slotShowsAddComponent = slotShowsAddComponent;
5752
+ exports.slotStateFromEl = slotStateFromEl;
5542
5753
  exports.toFieldEnvelopes = toFieldEnvelopes;
5543
5754
  exports.useAmplifyUp = useAmplifyUp;
5544
5755
  //# sourceMappingURL=react.js.map