@amplifyup/sdk 0.1.63 → 0.1.64

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
@@ -413,6 +413,16 @@ function isComposerPreview() {
413
413
 
414
414
  // src/field-envelope.ts
415
415
  var FIELDS_BRAND = Symbol("amplifyup.fields");
416
+ var IDENTIFIER_KEYS = /* @__PURE__ */ new Set([
417
+ "id",
418
+ "_id",
419
+ "slug",
420
+ "key",
421
+ "_key",
422
+ "docId",
423
+ "_type",
424
+ "type"
425
+ ]);
416
426
  function isBrandedFields(value) {
417
427
  return !!value && typeof value === "object" && FIELDS_BRAND in value;
418
428
  }
@@ -434,21 +444,79 @@ function isPortableImageShape(value) {
434
444
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
435
445
  return "url" in value;
436
446
  }
437
- function isEntryRefItem(value) {
438
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
439
- const rec = value;
440
- return typeof rec.id === "string" && rec.id.trim().length > 0 || typeof rec._id === "string" && rec._id.trim().length > 0 || typeof rec.docId === "string" && rec.docId.trim().length > 0;
447
+ function isIdentifierKey(key) {
448
+ return IDENTIFIER_KEYS.has(key);
441
449
  }
442
- function isRepeaterArray(value) {
450
+ function isRecordList(value) {
443
451
  if (!Array.isArray(value) || value.length === 0) return false;
444
452
  const sample = value.find((item) => item != null);
445
453
  if (sample === void 0) return false;
446
454
  if (typeof sample !== "object" || Array.isArray(sample)) return false;
447
455
  if (isPortableImageShape(sample)) return false;
448
- if (isEntryRefItem(sample)) return false;
449
456
  if (sample.$$typeof) return false;
450
457
  return true;
451
458
  }
459
+ function isFieldRef(value) {
460
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
461
+ const rec = value;
462
+ return typeof rec.providerId === "string" && !!rec.providerId && typeof rec.entity === "string" && !!rec.entity && typeof rec.id === "string" && !!rec.id;
463
+ }
464
+ function isListOwner(value) {
465
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
466
+ const rec = value;
467
+ return typeof rec.providerId === "string" && !!rec.providerId && typeof rec.entity === "string" && !!rec.entity;
468
+ }
469
+ function isReadOnlyRowField(value) {
470
+ return isFieldEnvelope(value) && value.readOnly === true && !value.ref;
471
+ }
472
+ function composerStoreKey(name, ref, layoutNodeId) {
473
+ if (isFieldRef(ref)) {
474
+ return `record:${ref.providerId}:${ref.entity}:${ref.id}:${name}`;
475
+ }
476
+ return layoutNodeId ? `page:${layoutNodeId}:${name}` : name;
477
+ }
478
+ function listOwnerFromConnection(conn) {
479
+ if (!conn || typeof conn !== "object") return void 0;
480
+ if (conn.mode === "query") {
481
+ const providerId = String(conn.spec?.providerId || "").trim();
482
+ const entity = String(conn.spec?.entity || "").trim();
483
+ return providerId && entity ? { providerId, entity } : void 0;
484
+ }
485
+ if (conn.mode === "item" || conn.mode === "items") {
486
+ const providerId = String(conn.providerId || "").trim();
487
+ const entity = String(conn.entity || "").trim();
488
+ return providerId && entity ? { providerId, entity } : void 0;
489
+ }
490
+ return void 0;
491
+ }
492
+ function listOwnerFromSpec(spec) {
493
+ const providerId = String(spec?.providerId || "").trim();
494
+ const entity = String(spec?.entity || "").trim();
495
+ return providerId && entity ? { providerId, entity } : void 0;
496
+ }
497
+ function isEnvelopeSources(value) {
498
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
499
+ const rec = value;
500
+ return rec.connections != null || rec.reads != null;
501
+ }
502
+ function normalizeEnvelopeSources(sources) {
503
+ if (!sources) return {};
504
+ if (isEnvelopeSources(sources) && !("mode" in sources)) {
505
+ return sources;
506
+ }
507
+ return { connections: sources };
508
+ }
509
+ function listOwnerForField(fieldName, props, sources) {
510
+ const { connections, reads } = normalizeEnvelopeSources(sources);
511
+ const resourceName = reads?.[fieldName]?.resource || fieldName;
512
+ const fromConn = listOwnerFromConnection(connections?.[resourceName]);
513
+ if (fromConn) return fromConn;
514
+ const paging = props?.[`${fieldName}Pagination`];
515
+ if (paging && typeof paging === "object" && !Array.isArray(paging)) {
516
+ return listOwnerFromSpec(paging.spec);
517
+ }
518
+ return void 0;
519
+ }
452
520
  function joinPath(prefix, key) {
453
521
  return prefix ? `${prefix}.${key}` : key;
454
522
  }
@@ -456,61 +524,101 @@ function emptyValue(raw) {
456
524
  if (raw === void 0) return null;
457
525
  return raw;
458
526
  }
459
- function toFieldEnvelopes(source, prefix = "") {
527
+ function rowId(raw) {
528
+ const candidates = [raw.id, raw._id, raw.docId, raw.key, raw._key];
529
+ for (const c of candidates) {
530
+ if (typeof c === "string" && c.trim()) return c;
531
+ }
532
+ return "";
533
+ }
534
+ function withLeaf(envelope, scope) {
535
+ if (isFieldRef(scope?.ref)) envelope.ref = scope.ref;
536
+ else if (scope?.readOnly) envelope.readOnly = true;
537
+ return envelope;
538
+ }
539
+ function toFieldEnvelopes(source, prefix = "", scope) {
540
+ const ctx = isFieldRef(scope) ? { ref: scope } : scope || {};
460
541
  const input = source && typeof source === "object" && !Array.isArray(source) ? source : {};
461
542
  if (isBrandedFields(input)) {
462
543
  return input;
463
544
  }
464
545
  const out = {};
465
546
  for (const [key, raw] of Object.entries(input)) {
547
+ if (isIdentifierKey(key) && (typeof raw === "string" || raw == null)) {
548
+ out[key] = raw ?? "";
549
+ continue;
550
+ }
466
551
  const path = joinPath(prefix, key);
467
552
  if (isFieldEnvelope(raw)) {
468
553
  const inner = raw.value;
469
- if (isRepeaterArray(inner)) {
554
+ if (isRecordList(inner)) {
555
+ const owner = ctx.owners?.(key);
470
556
  out[key] = {
471
- value: envelopeRepeaterItems(inner, raw.name || path),
557
+ value: envelopeListRows(inner, owner),
472
558
  name: raw.name || path
473
559
  };
474
560
  continue;
475
561
  }
476
- out[key] = { value: raw.value ?? null, name: raw.name || path };
562
+ out[key] = withLeaf(
563
+ { value: raw.value ?? null, name: raw.name || path },
564
+ raw.ref ? { ref: raw.ref } : ctx
565
+ );
477
566
  continue;
478
567
  }
479
- if (isRepeaterArray(raw)) {
480
- out[key] = { value: envelopeRepeaterItems(raw, path), name: path };
568
+ if (isRecordList(raw)) {
569
+ const owner = ctx.owners?.(key);
570
+ out[key] = { value: envelopeListRows(raw, owner), name: path };
481
571
  continue;
482
572
  }
483
573
  if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !raw.$$typeof) {
484
- out[key] = toFieldEnvelopes(raw, path);
574
+ out[key] = toFieldEnvelopes(raw, path, ctx);
485
575
  continue;
486
576
  }
487
- out[key] = { value: emptyValue(raw), name: path };
577
+ out[key] = withLeaf({ value: emptyValue(raw), name: path }, ctx);
488
578
  }
489
579
  return brandFields(out);
490
580
  }
491
- function envelopeRepeaterItems(items, prefix) {
492
- return items.map((item, i) => {
493
- const path = joinPath(prefix, String(i));
494
- if (isBrandedFields(item)) return item;
495
- if (item && typeof item === "object" && !Array.isArray(item)) {
496
- return toFieldEnvelopes(item, path);
497
- }
498
- return toFieldEnvelopes({}, path);
581
+ function envelopeListRows(items, owner) {
582
+ return items.map((item) => envelopeListRow(item, owner));
583
+ }
584
+ function envelopeListRow(item, owner) {
585
+ if (isBrandedFields(item) && typeof item.id === "string") {
586
+ return item;
587
+ }
588
+ const raw = item && typeof item === "object" && !Array.isArray(item) ? item : {};
589
+ const id = rowId(raw);
590
+ const ref = isListOwner(owner) && id ? { providerId: owner.providerId, entity: owner.entity, id } : void 0;
591
+ const fields = toFieldEnvelopes(raw, "", {
592
+ ref,
593
+ readOnly: !ref
499
594
  });
595
+ const row = fields;
596
+ if (typeof row.id !== "string") {
597
+ row.id = id;
598
+ }
599
+ return row;
500
600
  }
501
- function attachFieldEnvelopes(props) {
601
+ function attachFieldEnvelopes(props, sources) {
502
602
  const raw = props && typeof props === "object" ? { ...props } : {};
503
603
  if (isBrandedFields(raw.fields)) return raw;
504
604
  const { fields: existing, ...content } = raw;
505
605
  const source = existing && typeof existing === "object" && !isFieldEnvelope(existing) ? existing : content;
506
- return { ...raw, fields: toFieldEnvelopes(source) };
606
+ return {
607
+ ...raw,
608
+ fields: toFieldEnvelopes(source, "", {
609
+ owners: (fieldName) => listOwnerForField(fieldName, source, sources)
610
+ })
611
+ };
507
612
  }
508
613
  function walkLayoutNodes(nodes) {
509
614
  if (!Array.isArray(nodes)) return;
510
615
  for (const node of nodes) {
511
616
  if (!node || typeof node !== "object") continue;
512
617
  const rec = node;
513
- rec.props = attachFieldEnvelopes(rec.props);
618
+ rec.props = attachFieldEnvelopes(rec.props, {
619
+ connections: rec.resources,
620
+ reads: rec.reads
621
+ });
514
622
  for (const slot of rec.slots || []) {
515
623
  walkLayoutNodes(slot.children);
516
624
  }
@@ -524,31 +632,36 @@ function envelopePageConfig(page) {
524
632
  }
525
633
  function collectFieldPatches(props) {
526
634
  const out = [];
527
- const walkRepeater = (items, prefix) => {
528
- items.forEach((item, i) => {
529
- const itemPath = joinPath(prefix, String(i));
635
+ const walkRows = (items) => {
636
+ for (const item of items) {
530
637
  if (isBrandedFields(item) || item && typeof item === "object" && !Array.isArray(item) && !isFieldEnvelope(item)) {
531
- walk(item, itemPath);
638
+ walk(item, "");
532
639
  }
533
- });
640
+ }
534
641
  };
535
642
  const walk = (obj, prefix) => {
536
643
  for (const [key, raw] of Object.entries(obj)) {
644
+ if (isIdentifierKey(key)) continue;
537
645
  if (key === "fields" && raw && typeof raw === "object" && !Array.isArray(raw) && !isFieldEnvelope(raw)) {
538
646
  walk(raw, prefix);
539
647
  continue;
540
648
  }
541
649
  const path = joinPath(prefix, key);
542
650
  if (isFieldEnvelope(raw)) {
543
- if (isRepeaterArray(raw.value)) {
544
- walkRepeater(raw.value, raw.name || path);
651
+ if (isRecordList(raw.value)) {
652
+ walkRows(raw.value);
545
653
  continue;
546
654
  }
547
- out.push({ name: raw.name || path, value: raw.value });
655
+ const patch = {
656
+ name: raw.name || path,
657
+ value: raw.value
658
+ };
659
+ if (raw.ref) patch.ref = raw.ref;
660
+ out.push(patch);
548
661
  continue;
549
662
  }
550
- if (isRepeaterArray(raw)) {
551
- walkRepeater(raw, path);
663
+ if (isRecordList(raw)) {
664
+ walkRows(raw);
552
665
  continue;
553
666
  }
554
667
  if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !raw.$$typeof) {
@@ -563,40 +676,14 @@ function collectFieldPatches(props) {
563
676
  }
564
677
  return out;
565
678
  }
566
- function unwrapFieldsBag(item) {
567
- if (!item || typeof item !== "object" || Array.isArray(item)) return {};
568
- const out = {};
569
- for (const [key, raw] of Object.entries(item)) {
570
- if (isFieldEnvelope(raw)) {
571
- out[key] = raw.value;
572
- continue;
573
- }
574
- if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !isEntryRefItem(raw) && !raw.$$typeof) {
575
- out[key] = unwrapFieldsBag(raw);
576
- continue;
577
- }
578
- out[key] = raw;
579
- }
580
- return out;
581
- }
582
- function resolveCollectionItems(field, overlay) {
583
- const name = typeof field?.name === "string" ? field.name : "";
584
- const raw = overlay !== void 0 ? overlay : field?.value;
585
- const list = Array.isArray(raw) ? raw : isFieldEnvelope(raw) && Array.isArray(raw.value) ? raw.value : [];
586
- if (list.length === 0) return [];
587
- if (isRepeaterArray(list) || list.every((item) => isBrandedFields(item))) {
588
- return envelopeRepeaterItems(list, name);
589
- }
590
- return envelopeRepeaterItems(
591
- list.map(
592
- (item) => item && typeof item === "object" && !Array.isArray(item) ? item : {}
593
- ),
594
- name
595
- );
596
- }
597
679
  function resolveEditableBinding(field, value, name) {
598
680
  if (isFieldEnvelope(field)) {
599
- return { name: field.name, value: field.value, requiresName: false };
681
+ return {
682
+ name: field.name,
683
+ value: field.value,
684
+ ref: field.ref,
685
+ requiresName: false
686
+ };
600
687
  }
601
688
  return {
602
689
  name: name || "",
@@ -955,7 +1042,10 @@ async function fetchEdgeResolve(options) {
955
1042
  return envelopeResolvedPayload(body);
956
1043
  }
957
1044
  function envelopeResolvedComponent(node) {
958
- node.props = attachFieldEnvelopes(node.props);
1045
+ node.props = attachFieldEnvelopes(node.props, {
1046
+ connections: node.resources,
1047
+ reads: node.reads
1048
+ });
959
1049
  if (!node.children) return;
960
1050
  for (const kids of Object.values(node.children)) {
961
1051
  for (const child of kids || []) envelopeResolvedComponent(child);
@@ -1214,7 +1304,7 @@ async function fetchEdgeQuery(options) {
1214
1304
  async function queryContent(options) {
1215
1305
  const target = resolveAmplifyUpTarget({ configTarget: options.target });
1216
1306
  const { edgeUrl } = resolveAmplifyUpUrls(target);
1217
- return fetchEdgeQuery({
1307
+ const raw = await fetchEdgeQuery({
1218
1308
  edgeUrl,
1219
1309
  trackingId: options.trackingId,
1220
1310
  route: options.route,
@@ -1223,6 +1313,13 @@ async function queryContent(options) {
1223
1313
  fetch: options.fetch,
1224
1314
  signal: options.signal
1225
1315
  });
1316
+ return {
1317
+ ...raw,
1318
+ results: envelopeListRows(
1319
+ raw.results,
1320
+ listOwnerFromSpec(options.spec)
1321
+ )
1322
+ };
1226
1323
  }
1227
1324
  function nextPageSpec(pagination) {
1228
1325
  return { ...pagination.spec, offset: pagination.nextOffset };
@@ -3719,44 +3816,50 @@ var ComposerFieldStore = class {
3719
3816
  }
3720
3817
  };
3721
3818
  var ComposerStoreContext = React2.createContext(null);
3722
- function matchesNode(eventNodeId, layoutNodeId) {
3723
- if (eventNodeId && layoutNodeId && String(eventNodeId) !== String(layoutNodeId)) {
3724
- return false;
3725
- }
3726
- return true;
3819
+ var ComposerNodeContext = React2.createContext(void 0);
3820
+ function applyComposerPatch(store, name, value, ref, layoutNodeId) {
3821
+ store.set(composerStoreKey(name, ref, layoutNodeId), value);
3727
3822
  }
3728
3823
  function ComposerProvider({
3729
- layoutNodeId,
3730
3824
  children
3731
3825
  }) {
3826
+ const existing = React2.useContext(ComposerStoreContext);
3732
3827
  const storeRef = React2.useRef(null);
3733
3828
  if (!storeRef.current) storeRef.current = new ComposerFieldStore();
3734
- const store = storeRef.current;
3829
+ const store = existing || storeRef.current;
3735
3830
  React2.useEffect(() => {
3736
- const applyField = (fieldName, value, nodeId) => {
3831
+ if (existing) return;
3832
+ const applyField = (fieldName, value, nodeId, ref) => {
3737
3833
  if (!fieldName || typeof fieldName !== "string") return;
3738
- if (!matchesNode(nodeId, layoutNodeId)) return;
3739
- store.set(fieldName, value);
3740
- if (value && typeof value === "object") {
3741
- for (const patch of collectFieldPatches(
3742
- toFieldEnvelopes({ [fieldName]: value })
3743
- )) {
3744
- store.set(patch.name, patch.value);
3745
- }
3746
- }
3834
+ const fieldRef = isFieldRef(ref) ? ref : void 0;
3835
+ const layoutNodeId = typeof nodeId === "string" && nodeId ? nodeId : void 0;
3836
+ applyComposerPatch(store, fieldName, value, fieldRef, layoutNodeId);
3747
3837
  };
3748
3838
  const applyProps = (nextProps, nodeId) => {
3749
3839
  if (!nextProps || typeof nextProps !== "object" || Array.isArray(nextProps)) {
3750
3840
  return;
3751
3841
  }
3752
- if (!matchesNode(nodeId, layoutNodeId)) return;
3753
- for (const patch of collectFieldPatches(nextProps)) {
3754
- store.set(patch.name, patch.value);
3842
+ const layoutNodeId = typeof nodeId === "string" && nodeId ? nodeId : void 0;
3843
+ for (const patch of collectFieldPatches(
3844
+ nextProps
3845
+ )) {
3846
+ applyComposerPatch(
3847
+ store,
3848
+ patch.name,
3849
+ patch.value,
3850
+ patch.ref,
3851
+ patch.ref ? void 0 : layoutNodeId
3852
+ );
3755
3853
  }
3756
3854
  };
3757
3855
  const onPatch = (event) => {
3758
3856
  const detail = event.detail || {};
3759
- applyField(detail.fieldName, detail.value, detail.layoutNodeId);
3857
+ applyField(
3858
+ detail.fieldName,
3859
+ detail.value,
3860
+ detail.layoutNodeId,
3861
+ detail.ref
3862
+ );
3760
3863
  };
3761
3864
  const onPropsPatch = (event) => {
3762
3865
  const detail = event.detail || {};
@@ -3768,7 +3871,12 @@ function ComposerProvider({
3768
3871
  return;
3769
3872
  }
3770
3873
  if (event.data?.type !== "AMPLIFYUP_FIELD_PATCH") return;
3771
- applyField(event.data.fieldName, event.data.value, event.data.layoutNodeId);
3874
+ applyField(
3875
+ event.data.fieldName,
3876
+ event.data.value,
3877
+ event.data.layoutNodeId,
3878
+ event.data.ref
3879
+ );
3772
3880
  };
3773
3881
  window.addEventListener("amplifyup:field-patch", onPatch);
3774
3882
  window.addEventListener("amplifyup:props-patch", onPropsPatch);
@@ -3778,29 +3886,29 @@ function ComposerProvider({
3778
3886
  window.removeEventListener("amplifyup:props-patch", onPropsPatch);
3779
3887
  window.removeEventListener("message", onMessage);
3780
3888
  };
3781
- }, [layoutNodeId, store]);
3889
+ }, [existing, store]);
3890
+ if (existing) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
3782
3891
  return /* @__PURE__ */ jsxRuntime.jsx(ComposerStoreContext.Provider, { value: store, children });
3783
3892
  }
3784
- function useComposerFieldValue(name, fallback) {
3893
+ function useComposerFieldValue(name, fallback, ref) {
3785
3894
  const store = React2.useContext(ComposerStoreContext);
3895
+ const layoutNodeId = React2.useContext(ComposerNodeContext);
3896
+ const key = composerStoreKey(name, ref, layoutNodeId);
3786
3897
  const subscribe = React2.useCallback(
3787
3898
  (onStoreChange) => {
3788
3899
  if (!store || !name) return () => {
3789
3900
  };
3790
- return store.subscribe(name, onStoreChange);
3901
+ return store.subscribe(key, onStoreChange);
3791
3902
  },
3792
- [store, name]
3903
+ [store, name, key]
3793
3904
  );
3794
3905
  const getSnapshot = React2.useCallback(() => {
3795
3906
  if (!store || !name) return void 0;
3796
- return store.get(name);
3797
- }, [store, name]);
3907
+ return store.get(key);
3908
+ }, [store, name, key]);
3798
3909
  const overlay = React2.useSyncExternalStore(subscribe, getSnapshot, () => void 0);
3799
3910
  return overlay !== void 0 ? overlay : fallback;
3800
3911
  }
3801
- function useComposerStore() {
3802
- return React2.useContext(ComposerStoreContext);
3803
- }
3804
3912
  function MaybeComposerProvider({
3805
3913
  enabled,
3806
3914
  layoutNodeId,
@@ -3808,7 +3916,7 @@ function MaybeComposerProvider({
3808
3916
  }) {
3809
3917
  const content = React2.useMemo(() => children, [children]);
3810
3918
  if (!enabled) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: content });
3811
- return /* @__PURE__ */ jsxRuntime.jsx(ComposerProvider, { layoutNodeId, children });
3919
+ return /* @__PURE__ */ jsxRuntime.jsx(ComposerProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ComposerNodeContext.Provider, { value: layoutNodeId, children }) });
3812
3920
  }
3813
3921
  function nodeRenderProps(node) {
3814
3922
  const settings = node.settings || {};
@@ -4266,7 +4374,7 @@ function AmplifyRendererInner({
4266
4374
  if (layoutTree.length === 0) {
4267
4375
  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." }) }) });
4268
4376
  }
4269
- return /* @__PURE__ */ jsxRuntime.jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsxRuntime.jsx(
4377
+ return /* @__PURE__ */ jsxRuntime.jsx(ComposerProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsxRuntime.jsx(
4270
4378
  "div",
4271
4379
  {
4272
4380
  "data-slot-id": "main",
@@ -4275,7 +4383,7 @@ function AmplifyRendererInner({
4275
4383
  "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
4276
4384
  children: layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(React2.Fragment, { children: renderNode(node) }, node.id))
4277
4385
  }
4278
- ) : layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(React2.Fragment, { children: renderNode(node) }, node.id)) });
4386
+ ) : layoutTree.map((node) => /* @__PURE__ */ jsxRuntime.jsx(React2.Fragment, { children: renderNode(node) }, node.id)) }) });
4279
4387
  }
4280
4388
  var AmplifyRenderer = React2.memo(AmplifyRendererInner);
4281
4389
  function AddComponentIcon({ size = 28 }) {
@@ -4628,10 +4736,10 @@ function useBoundField(field, value, name, component) {
4628
4736
  `[AmplifyUp SDK] ${component} value={\u2026} requires name="\u2026" for computed values`
4629
4737
  );
4630
4738
  }
4631
- const fieldValue = useComposerFieldValue(bound.name, bound.value);
4632
- return { editName: bound.name, fieldValue };
4739
+ const fieldValue = useComposerFieldValue(bound.name, bound.value, bound.ref);
4740
+ return { editName: bound.name, fieldValue, ref: bound.ref };
4633
4741
  }
4634
- function commitComposerField(layoutNodeId, componentId, name, value) {
4742
+ function commitComposerField(layoutNodeId, componentId, name, value, ref) {
4635
4743
  if (typeof window === "undefined" || window.parent === window) return;
4636
4744
  window.parent.postMessage(
4637
4745
  {
@@ -4639,7 +4747,8 @@ function commitComposerField(layoutNodeId, componentId, name, value) {
4639
4747
  layoutNodeId,
4640
4748
  componentId,
4641
4749
  fieldName: name,
4642
- value
4750
+ value,
4751
+ ...ref ? { ref } : {}
4643
4752
  },
4644
4753
  "*"
4645
4754
  );
@@ -4664,6 +4773,7 @@ function requestCanvasRichTextEdit(opts) {
4664
4773
  componentId: opts.componentId,
4665
4774
  fieldName: opts.name,
4666
4775
  value: opts.value,
4776
+ ...opts.fieldRef ? { ref: opts.fieldRef } : {},
4667
4777
  bounds: {
4668
4778
  top: rect.top,
4669
4779
  left: rect.left,
@@ -4688,6 +4798,7 @@ function ComposerInlineField({
4688
4798
  className,
4689
4799
  layoutNodeId,
4690
4800
  componentId,
4801
+ fieldRef,
4691
4802
  rich = false
4692
4803
  }) {
4693
4804
  const [focused, setFocused] = React2.useState(false);
@@ -4716,12 +4827,12 @@ function ComposerInlineField({
4716
4827
  if (storedEmpty && next === "") {
4717
4828
  if (elRef.current) elRef.current.textContent = hint;
4718
4829
  if (typeof value === "string" && EMPTY_FIELD_PLACEHOLDERS.has(value.trim())) {
4719
- commitComposerField(layoutNodeId, componentId, name, "");
4830
+ commitComposerField(layoutNodeId, componentId, name, "", fieldRef);
4720
4831
  }
4721
4832
  return;
4722
4833
  }
4723
4834
  if (next === text) return;
4724
- commitComposerField(layoutNodeId, componentId, name, next);
4835
+ commitComposerField(layoutNodeId, componentId, name, next, fieldRef);
4725
4836
  };
4726
4837
  const openRichEditor = (event) => {
4727
4838
  if (!isComposerEditMode() || !elRef.current) return;
@@ -4733,7 +4844,8 @@ function ComposerInlineField({
4733
4844
  layoutNodeId,
4734
4845
  componentId,
4735
4846
  name,
4736
- value: source
4847
+ value: source,
4848
+ fieldRef
4737
4849
  });
4738
4850
  };
4739
4851
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -4744,6 +4856,9 @@ function ComposerInlineField({
4744
4856
  "data-amplifyup-field": name,
4745
4857
  "data-amplifyup-field-type": rich ? "richtext" : "text",
4746
4858
  "data-amplifyup-node-id": layoutNodeId || void 0,
4859
+ "data-amplifyup-ref-provider": fieldRef?.providerId || void 0,
4860
+ "data-amplifyup-ref-entity": fieldRef?.entity || void 0,
4861
+ "data-amplifyup-ref-id": fieldRef?.id || void 0,
4747
4862
  "data-amplifyup-empty-hint": showHint ? "true" : void 0,
4748
4863
  contentEditable: !rich,
4749
4864
  suppressContentEditableWarning: true,
@@ -4808,7 +4923,8 @@ function ComposerInlineHtml({
4808
4923
  className,
4809
4924
  Tag = "div",
4810
4925
  layoutNodeId,
4811
- componentId
4926
+ componentId,
4927
+ fieldRef
4812
4928
  }) {
4813
4929
  const elRef = React2.useRef(null);
4814
4930
  React2.useEffect(() => {
@@ -4825,7 +4941,8 @@ function ComposerInlineHtml({
4825
4941
  layoutNodeId,
4826
4942
  componentId,
4827
4943
  name,
4828
- value: sourceValue
4944
+ value: sourceValue,
4945
+ fieldRef
4829
4946
  });
4830
4947
  };
4831
4948
  return React2__default.default.createElement(Tag, {
@@ -4834,6 +4951,9 @@ function ComposerInlineHtml({
4834
4951
  "data-amplifyup-field": name,
4835
4952
  "data-amplifyup-field-type": "richtext",
4836
4953
  "data-amplifyup-node-id": layoutNodeId || void 0,
4954
+ "data-amplifyup-ref-provider": fieldRef?.providerId || void 0,
4955
+ "data-amplifyup-ref-entity": fieldRef?.entity || void 0,
4956
+ "data-amplifyup-ref-id": fieldRef?.id || void 0,
4837
4957
  dangerouslySetInnerHTML: { __html: html },
4838
4958
  onMouseDown: (event) => {
4839
4959
  event.stopPropagation();
@@ -4865,6 +4985,40 @@ function formatFieldValue(value) {
4865
4985
  }
4866
4986
  return "";
4867
4987
  }
4988
+ var FIELD_SCALAR_HINT = "Field expects a scalar field. Did you mean <Field field={post.title} />?";
4989
+ function warnNotScalarField(component, field, fieldValue) {
4990
+ if (process.env.NODE_ENV === "production") return false;
4991
+ if (isBrandedFields(field)) {
4992
+ console.error(`[AmplifyUp SDK] ${component}: ${FIELD_SCALAR_HINT}`);
4993
+ return true;
4994
+ }
4995
+ if (isFieldEnvelope(field) && Array.isArray(field.value)) {
4996
+ console.error(`[AmplifyUp SDK] ${component}: ${FIELD_SCALAR_HINT}`);
4997
+ return true;
4998
+ }
4999
+ if (Array.isArray(fieldValue)) {
5000
+ console.error(`[AmplifyUp SDK] ${component}: ${FIELD_SCALAR_HINT}`);
5001
+ return true;
5002
+ }
5003
+ return false;
5004
+ }
5005
+ var warnedReadOnlyFields = /* @__PURE__ */ new Set();
5006
+ function warnReadOnlyRowField(name) {
5007
+ if (process.env.NODE_ENV === "production") return;
5008
+ const key = name || "(unnamed)";
5009
+ if (warnedReadOnlyFields.has(key)) return;
5010
+ warnedReadOnlyFields.add(key);
5011
+ console.warn(
5012
+ `[AmplifyUp SDK] ${key} has no write target; rendered read-only.`
5013
+ );
5014
+ }
5015
+ function canComposerBind(field, ref, editName) {
5016
+ if (isReadOnlyRowField(field) || isFieldEnvelope(field) && field.readOnly) {
5017
+ warnReadOnlyRowField(editName || (isFieldEnvelope(field) ? field.name : ""));
5018
+ return false;
5019
+ }
5020
+ return true;
5021
+ }
4868
5022
  function warnProjectionProps(component, hasField, hasValue, name) {
4869
5023
  if (process.env.NODE_ENV === "production") return;
4870
5024
  if (!hasField && !hasValue && !name) {
@@ -4882,16 +5036,20 @@ function Field(props) {
4882
5036
  const composerEdit = useComposerEditMode();
4883
5037
  const hasField = isFieldEnvelope(field);
4884
5038
  const hasValue = hasOwnProp(props, "value");
4885
- const { editName, fieldValue } = useBoundField(field, value, name, "Field");
5039
+ const { editName, fieldValue, ref } = useBoundField(field, value, name, "Field");
4886
5040
  warnProjectionProps("Field", hasField, hasValue, name);
5041
+ if (warnNotScalarField("Field", field, fieldValue)) {
5042
+ return null;
5043
+ }
4887
5044
  if (!hasField && !hasValue && !name) {
4888
5045
  return null;
4889
5046
  }
4890
- if (Array.isArray(fieldValue)) {
5047
+ if (isBrandedFields(field) || Array.isArray(fieldValue)) {
4891
5048
  return null;
4892
5049
  }
4893
5050
  const empty = isComposerEmptyText(fieldValue);
4894
- if (composerEdit && editName && context && canInlineEdit(editName, fieldValue)) {
5051
+ const bindComposer = composerEdit && editName && context && canInlineEdit(editName, fieldValue) && canComposerBind(field, ref, editName);
5052
+ if (bindComposer) {
4895
5053
  return /* @__PURE__ */ jsxRuntime.jsx(
4896
5054
  ComposerInlineField,
4897
5055
  {
@@ -4900,6 +5058,7 @@ function Field(props) {
4900
5058
  className,
4901
5059
  layoutNodeId: context.layoutNodeId,
4902
5060
  componentId: context.componentId,
5061
+ fieldRef: ref,
4903
5062
  rich: looksLikeRichCanvasField(editName)
4904
5063
  }
4905
5064
  );
@@ -4909,186 +5068,6 @@ function Field(props) {
4909
5068
  if (!text) return null;
4910
5069
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: text });
4911
5070
  }
4912
- var COMPOSER_EMPTY_COLLECTION_HINT = "Click here to add an item";
4913
- function CollectionChromeButton({
4914
- label,
4915
- onClick
4916
- }) {
4917
- return /* @__PURE__ */ jsxRuntime.jsx(
4918
- "button",
4919
- {
4920
- type: "button",
4921
- onMouseDown: (event) => event.stopPropagation(),
4922
- onClick: (event) => {
4923
- event.preventDefault();
4924
- event.stopPropagation();
4925
- onClick();
4926
- },
4927
- style: {
4928
- fontSize: 11,
4929
- lineHeight: 1.2,
4930
- padding: "4px 8px",
4931
- border: "1px solid color-mix(in srgb, currentColor 28%, transparent)",
4932
- borderRadius: 6,
4933
- background: "transparent",
4934
- cursor: "pointer",
4935
- color: "inherit"
4936
- },
4937
- children: label
4938
- }
4939
- );
4940
- }
4941
- function Collection({
4942
- field,
4943
- children,
4944
- className
4945
- }) {
4946
- const context = React2.useContext(ComponentContext);
4947
- const store = useComposerStore();
4948
- const composerEdit = useComposerEditMode();
4949
- const name = isFieldEnvelope(field) ? field.name : "";
4950
- const overlay = useComposerFieldValue(name, void 0);
4951
- const items = resolveCollectionItems(field, overlay);
4952
- const writeItems = (nextPlain) => {
4953
- if (name && store) {
4954
- const prefixed = name ? store.keys().filter((key) => key === name || key.startsWith(`${name}.`)) : [];
4955
- for (const key of prefixed) store.delete(key);
4956
- store.set(name, nextPlain);
4957
- for (const patch of collectFieldPatches(
4958
- toFieldEnvelopes({ [name]: nextPlain })
4959
- )) {
4960
- store.set(patch.name, patch.value);
4961
- }
4962
- }
4963
- commitComposerField(
4964
- context?.layoutNodeId,
4965
- context?.componentId,
4966
- name,
4967
- nextPlain
4968
- );
4969
- };
4970
- const plainItems = () => items.map((item, index) => {
4971
- const base = unwrapFieldsBag(item);
4972
- if (!store || !name) return base;
4973
- const out = { ...base };
4974
- for (const key of Object.keys(out)) {
4975
- const overlayValue = store.get(`${name}.${index}.${key}`);
4976
- if (overlayValue !== void 0) out[key] = overlayValue;
4977
- }
4978
- return out;
4979
- });
4980
- const addItem = () => writeItems([...plainItems(), {}]);
4981
- const removeItem = (index) => writeItems(plainItems().filter((_, i) => i !== index));
4982
- const moveItem = (index, delta) => {
4983
- const next = plainItems();
4984
- const target = index + delta;
4985
- if (target < 0 || target >= next.length) return;
4986
- const swap = next[index];
4987
- next[index] = next[target];
4988
- next[target] = swap;
4989
- writeItems(next);
4990
- };
4991
- if (!composerEdit) {
4992
- if (items.length === 0) return null;
4993
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, style: { display: "contents" }, children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React2__default.default.Fragment, { children: children(item, index) }, item && typeof item === "object" ? `${name}.${index}` : index)) });
4994
- }
4995
- if (items.length === 0) {
4996
- return /* @__PURE__ */ jsxRuntime.jsx(
4997
- "span",
4998
- {
4999
- className,
5000
- "data-amplifyup-collection": name || void 0,
5001
- "data-amplifyup-collection-empty": "true",
5002
- "data-amplifyup-node-id": context?.layoutNodeId || void 0,
5003
- role: "button",
5004
- tabIndex: 0,
5005
- onMouseDown: (event) => event.stopPropagation(),
5006
- onClick: (event) => {
5007
- event.preventDefault();
5008
- event.stopPropagation();
5009
- addItem();
5010
- },
5011
- onKeyDown: (event) => {
5012
- if (event.key === "Enter" || event.key === " ") {
5013
- event.preventDefault();
5014
- addItem();
5015
- }
5016
- },
5017
- style: {
5018
- display: "block",
5019
- cursor: "pointer",
5020
- outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
5021
- outlineOffset: 2,
5022
- minHeight: "3em",
5023
- opacity: 0.5
5024
- },
5025
- children: /* @__PURE__ */ jsxRuntime.jsx(EmptySlotHint, { label: COMPOSER_EMPTY_COLLECTION_HINT, compact: true })
5026
- }
5027
- );
5028
- }
5029
- return /* @__PURE__ */ jsxRuntime.jsxs(
5030
- "span",
5031
- {
5032
- className,
5033
- "data-amplifyup-collection": name || void 0,
5034
- "data-amplifyup-node-id": context?.layoutNodeId || void 0,
5035
- style: { display: "flex", flexDirection: "column", gap: 8 },
5036
- children: [
5037
- items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
5038
- "span",
5039
- {
5040
- "data-amplifyup-collection-item": `${name}.${index}`,
5041
- style: {
5042
- display: "block",
5043
- outline: "1px dashed color-mix(in srgb, currentColor 28%, transparent)",
5044
- outlineOffset: 2,
5045
- padding: 8
5046
- },
5047
- children: [
5048
- /* @__PURE__ */ jsxRuntime.jsxs(
5049
- "span",
5050
- {
5051
- style: {
5052
- display: "flex",
5053
- gap: 6,
5054
- justifyContent: "flex-end",
5055
- marginBottom: 8
5056
- },
5057
- children: [
5058
- /* @__PURE__ */ jsxRuntime.jsx(
5059
- CollectionChromeButton,
5060
- {
5061
- label: "Move up",
5062
- onClick: () => moveItem(index, -1)
5063
- }
5064
- ),
5065
- /* @__PURE__ */ jsxRuntime.jsx(
5066
- CollectionChromeButton,
5067
- {
5068
- label: "Move down",
5069
- onClick: () => moveItem(index, 1)
5070
- }
5071
- ),
5072
- /* @__PURE__ */ jsxRuntime.jsx(
5073
- CollectionChromeButton,
5074
- {
5075
- label: "Remove",
5076
- onClick: () => removeItem(index)
5077
- }
5078
- )
5079
- ]
5080
- }
5081
- ),
5082
- children(item, index)
5083
- ]
5084
- },
5085
- `${name}.${index}`
5086
- )),
5087
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: /* @__PURE__ */ jsxRuntime.jsx(CollectionChromeButton, { label: "Add item", onClick: addItem }) })
5088
- ]
5089
- }
5090
- );
5091
- }
5092
5071
  function markdownToHtml(markdown) {
5093
5072
  try {
5094
5073
  const result = marked.marked.parse(markdown, { async: false });
@@ -5112,7 +5091,7 @@ function RichText(props) {
5112
5091
  const hasField = isFieldEnvelope(field);
5113
5092
  const hasValueProp = hasOwnProp(props, "value") || childrenValue !== void 0;
5114
5093
  const boundValue = hasOwnProp(props, "value") ? value : childrenValue !== void 0 ? childrenValue : value;
5115
- const { editName, fieldValue } = useBoundField(
5094
+ const { editName, fieldValue, ref } = useBoundField(
5116
5095
  field,
5117
5096
  boundValue,
5118
5097
  name,
@@ -5122,7 +5101,10 @@ function RichText(props) {
5122
5101
  return null;
5123
5102
  }
5124
5103
  warnProjectionProps("RichText", hasField, hasValueProp, name);
5125
- const canEdit = !!context && canInlineEditRichText(editName, fieldValue);
5104
+ if (warnNotScalarField("RichText", field, fieldValue)) {
5105
+ return null;
5106
+ }
5107
+ const canEdit = !!context && canInlineEditRichText(editName, fieldValue) && canComposerBind(field, ref, editName);
5126
5108
  if (isComposerEmptyText(fieldValue)) {
5127
5109
  if (composerEdit && canEdit) {
5128
5110
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -5133,6 +5115,7 @@ function RichText(props) {
5133
5115
  className,
5134
5116
  layoutNodeId: context?.layoutNodeId,
5135
5117
  componentId: context?.componentId,
5118
+ fieldRef: ref,
5136
5119
  rich: true
5137
5120
  }
5138
5121
  );
@@ -5164,7 +5147,8 @@ function RichText(props) {
5164
5147
  className,
5165
5148
  Tag,
5166
5149
  layoutNodeId: context?.layoutNodeId,
5167
- componentId: context?.componentId
5150
+ componentId: context?.componentId,
5151
+ fieldRef: ref
5168
5152
  }
5169
5153
  );
5170
5154
  }
@@ -5194,9 +5178,12 @@ function Image(props) {
5194
5178
  const context = React2.useContext(ComponentContext);
5195
5179
  const hasField = isFieldEnvelope(field);
5196
5180
  const hasValue = hasOwnProp(props, "value");
5197
- const { editName, fieldValue } = useBoundField(field, value, name, "Image");
5181
+ const { editName, fieldValue, ref } = useBoundField(field, value, name, "Image");
5198
5182
  const composerEdit = useComposerEditMode();
5199
5183
  warnProjectionProps("Image", hasField, hasValue, name);
5184
+ if (warnNotScalarField("Image", field, fieldValue)) {
5185
+ return null;
5186
+ }
5200
5187
  if (!hasField && !hasValue && !name) {
5201
5188
  return null;
5202
5189
  }
@@ -5205,10 +5192,13 @@ function Image(props) {
5205
5192
  const alt = altProp ?? portable?.alt ?? "";
5206
5193
  const width = widthProp ?? portable?.width;
5207
5194
  const height = heightProp ?? portable?.height;
5208
- const fieldAttrs = composerEdit && editName ? {
5195
+ const fieldAttrs = composerEdit && editName && canComposerBind(field, ref, editName) ? {
5209
5196
  "data-amplifyup-field": editName,
5210
5197
  "data-amplifyup-field-type": "image",
5211
- "data-amplifyup-node-id": context?.layoutNodeId || void 0
5198
+ "data-amplifyup-node-id": context?.layoutNodeId || void 0,
5199
+ "data-amplifyup-ref-provider": ref?.providerId || void 0,
5200
+ "data-amplifyup-ref-entity": ref?.entity || void 0,
5201
+ "data-amplifyup-ref-id": ref?.id || void 0
5212
5202
  } : null;
5213
5203
  if (composerEdit && !editName && (hasField || hasValue)) {
5214
5204
  console.warn(
@@ -5480,7 +5470,6 @@ function useAmplifyUp() {
5480
5470
  exports.AmplifyPageContent = AmplifyPageContent;
5481
5471
  exports.AmplifyRenderer = AmplifyRenderer;
5482
5472
  exports.AmplifyUpProvider = AmplifyUpProvider;
5483
- exports.Collection = Collection;
5484
5473
  exports.ComponentContextProvider = ComponentContextProvider;
5485
5474
  exports.ComposerProvider = ComposerProvider;
5486
5475
  exports.Field = Field;
@@ -5490,11 +5479,16 @@ exports.QueryContentError = QueryContentError;
5490
5479
  exports.RichText = RichText;
5491
5480
  exports.SelectVariantsError = SelectVariantsError;
5492
5481
  exports.Slot = Slot;
5482
+ exports.attachFieldEnvelopes = attachFieldEnvelopes;
5483
+ exports.composerStoreKey = composerStoreKey;
5484
+ exports.envelopeListRows = envelopeListRows;
5493
5485
  exports.isBrandedFields = isBrandedFields;
5494
5486
  exports.isComposerPreview = isComposerPreview;
5495
5487
  exports.isFieldEnvelope = isFieldEnvelope;
5496
5488
  exports.isPreviewRequest = isPreviewRequest;
5489
+ exports.isReadOnlyRowField = isReadOnlyRowField;
5497
5490
  exports.isWorkingContentRequest = isWorkingContentRequest;
5491
+ exports.listOwnerFromSpec = listOwnerFromSpec;
5498
5492
  exports.nextPageSpec = nextPageSpec;
5499
5493
  exports.queryContent = queryContent;
5500
5494
  exports.searchSpec = searchSpec;