@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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import React2, { createContext, memo, useState, useEffect, Fragment as Fragment$1, useContext, Component, useMemo, useRef, useSyncExternalStore, useCallback } from 'react';
2
+ import React2, { createContext, memo, useState, useEffect, Fragment as Fragment$1, useContext, useRef, Component, useMemo, useSyncExternalStore, useCallback } from 'react';
3
3
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
4
4
  import { marked } from 'marked';
5
5
 
@@ -407,6 +407,16 @@ function isComposerPreview() {
407
407
 
408
408
  // src/field-envelope.ts
409
409
  var FIELDS_BRAND = Symbol("amplifyup.fields");
410
+ var IDENTIFIER_KEYS = /* @__PURE__ */ new Set([
411
+ "id",
412
+ "_id",
413
+ "slug",
414
+ "key",
415
+ "_key",
416
+ "docId",
417
+ "_type",
418
+ "type"
419
+ ]);
410
420
  function isBrandedFields(value) {
411
421
  return !!value && typeof value === "object" && FIELDS_BRAND in value;
412
422
  }
@@ -428,21 +438,79 @@ function isPortableImageShape(value) {
428
438
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
429
439
  return "url" in value;
430
440
  }
431
- function isEntryRefItem(value) {
432
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
433
- const rec = value;
434
- 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;
441
+ function isIdentifierKey(key) {
442
+ return IDENTIFIER_KEYS.has(key);
435
443
  }
436
- function isRepeaterArray(value) {
444
+ function isRecordList(value) {
437
445
  if (!Array.isArray(value) || value.length === 0) return false;
438
446
  const sample = value.find((item) => item != null);
439
447
  if (sample === void 0) return false;
440
448
  if (typeof sample !== "object" || Array.isArray(sample)) return false;
441
449
  if (isPortableImageShape(sample)) return false;
442
- if (isEntryRefItem(sample)) return false;
443
450
  if (sample.$$typeof) return false;
444
451
  return true;
445
452
  }
453
+ function isFieldRef(value) {
454
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
455
+ const rec = value;
456
+ return typeof rec.providerId === "string" && !!rec.providerId && typeof rec.entity === "string" && !!rec.entity && typeof rec.id === "string" && !!rec.id;
457
+ }
458
+ function isListOwner(value) {
459
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
460
+ const rec = value;
461
+ return typeof rec.providerId === "string" && !!rec.providerId && typeof rec.entity === "string" && !!rec.entity;
462
+ }
463
+ function isReadOnlyRowField(value) {
464
+ return isFieldEnvelope(value) && value.readOnly === true && !value.ref;
465
+ }
466
+ function composerStoreKey(name, ref, layoutNodeId) {
467
+ if (isFieldRef(ref)) {
468
+ return `record:${ref.providerId}:${ref.entity}:${ref.id}:${name}`;
469
+ }
470
+ return layoutNodeId ? `page:${layoutNodeId}:${name}` : name;
471
+ }
472
+ function listOwnerFromConnection(conn) {
473
+ if (!conn || typeof conn !== "object") return void 0;
474
+ if (conn.mode === "query") {
475
+ const providerId = String(conn.spec?.providerId || "").trim();
476
+ const entity = String(conn.spec?.entity || "").trim();
477
+ return providerId && entity ? { providerId, entity } : void 0;
478
+ }
479
+ if (conn.mode === "item" || conn.mode === "items") {
480
+ const providerId = String(conn.providerId || "").trim();
481
+ const entity = String(conn.entity || "").trim();
482
+ return providerId && entity ? { providerId, entity } : void 0;
483
+ }
484
+ return void 0;
485
+ }
486
+ function listOwnerFromSpec(spec) {
487
+ const providerId = String(spec?.providerId || "").trim();
488
+ const entity = String(spec?.entity || "").trim();
489
+ return providerId && entity ? { providerId, entity } : void 0;
490
+ }
491
+ function isEnvelopeSources(value) {
492
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
493
+ const rec = value;
494
+ return rec.connections != null || rec.reads != null;
495
+ }
496
+ function normalizeEnvelopeSources(sources) {
497
+ if (!sources) return {};
498
+ if (isEnvelopeSources(sources) && !("mode" in sources)) {
499
+ return sources;
500
+ }
501
+ return { connections: sources };
502
+ }
503
+ function listOwnerForField(fieldName, props, sources) {
504
+ const { connections, reads } = normalizeEnvelopeSources(sources);
505
+ const resourceName = reads?.[fieldName]?.resource || fieldName;
506
+ const fromConn = listOwnerFromConnection(connections?.[resourceName]);
507
+ if (fromConn) return fromConn;
508
+ const paging = props?.[`${fieldName}Pagination`];
509
+ if (paging && typeof paging === "object" && !Array.isArray(paging)) {
510
+ return listOwnerFromSpec(paging.spec);
511
+ }
512
+ return void 0;
513
+ }
446
514
  function joinPath(prefix, key) {
447
515
  return prefix ? `${prefix}.${key}` : key;
448
516
  }
@@ -450,61 +518,101 @@ function emptyValue(raw) {
450
518
  if (raw === void 0) return null;
451
519
  return raw;
452
520
  }
453
- function toFieldEnvelopes(source, prefix = "") {
521
+ function rowId(raw) {
522
+ const candidates = [raw.id, raw._id, raw.docId, raw.key, raw._key];
523
+ for (const c of candidates) {
524
+ if (typeof c === "string" && c.trim()) return c;
525
+ }
526
+ return "";
527
+ }
528
+ function withLeaf(envelope, scope) {
529
+ if (isFieldRef(scope?.ref)) envelope.ref = scope.ref;
530
+ else if (scope?.readOnly) envelope.readOnly = true;
531
+ return envelope;
532
+ }
533
+ function toFieldEnvelopes(source, prefix = "", scope) {
534
+ const ctx = isFieldRef(scope) ? { ref: scope } : scope || {};
454
535
  const input = source && typeof source === "object" && !Array.isArray(source) ? source : {};
455
536
  if (isBrandedFields(input)) {
456
537
  return input;
457
538
  }
458
539
  const out = {};
459
540
  for (const [key, raw] of Object.entries(input)) {
541
+ if (isIdentifierKey(key) && (typeof raw === "string" || raw == null)) {
542
+ out[key] = raw ?? "";
543
+ continue;
544
+ }
460
545
  const path = joinPath(prefix, key);
461
546
  if (isFieldEnvelope(raw)) {
462
547
  const inner = raw.value;
463
- if (isRepeaterArray(inner)) {
548
+ if (isRecordList(inner)) {
549
+ const owner = ctx.owners?.(key);
464
550
  out[key] = {
465
- value: envelopeRepeaterItems(inner, raw.name || path),
551
+ value: envelopeListRows(inner, owner),
466
552
  name: raw.name || path
467
553
  };
468
554
  continue;
469
555
  }
470
- out[key] = { value: raw.value ?? null, name: raw.name || path };
556
+ out[key] = withLeaf(
557
+ { value: raw.value ?? null, name: raw.name || path },
558
+ raw.ref ? { ref: raw.ref } : ctx
559
+ );
471
560
  continue;
472
561
  }
473
- if (isRepeaterArray(raw)) {
474
- out[key] = { value: envelopeRepeaterItems(raw, path), name: path };
562
+ if (isRecordList(raw)) {
563
+ const owner = ctx.owners?.(key);
564
+ out[key] = { value: envelopeListRows(raw, owner), name: path };
475
565
  continue;
476
566
  }
477
567
  if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !raw.$$typeof) {
478
- out[key] = toFieldEnvelopes(raw, path);
568
+ out[key] = toFieldEnvelopes(raw, path, ctx);
479
569
  continue;
480
570
  }
481
- out[key] = { value: emptyValue(raw), name: path };
571
+ out[key] = withLeaf({ value: emptyValue(raw), name: path }, ctx);
482
572
  }
483
573
  return brandFields(out);
484
574
  }
485
- function envelopeRepeaterItems(items, prefix) {
486
- return items.map((item, i) => {
487
- const path = joinPath(prefix, String(i));
488
- if (isBrandedFields(item)) return item;
489
- if (item && typeof item === "object" && !Array.isArray(item)) {
490
- return toFieldEnvelopes(item, path);
491
- }
492
- return toFieldEnvelopes({}, path);
575
+ function envelopeListRows(items, owner) {
576
+ return items.map((item) => envelopeListRow(item, owner));
577
+ }
578
+ function envelopeListRow(item, owner) {
579
+ if (isBrandedFields(item) && typeof item.id === "string") {
580
+ return item;
581
+ }
582
+ const raw = item && typeof item === "object" && !Array.isArray(item) ? item : {};
583
+ const id = rowId(raw);
584
+ const ref = isListOwner(owner) && id ? { providerId: owner.providerId, entity: owner.entity, id } : void 0;
585
+ const fields = toFieldEnvelopes(raw, "", {
586
+ ref,
587
+ readOnly: !ref
493
588
  });
589
+ const row = fields;
590
+ if (typeof row.id !== "string") {
591
+ row.id = id;
592
+ }
593
+ return row;
494
594
  }
495
- function attachFieldEnvelopes(props) {
595
+ function attachFieldEnvelopes(props, sources) {
496
596
  const raw = props && typeof props === "object" ? { ...props } : {};
497
597
  if (isBrandedFields(raw.fields)) return raw;
498
598
  const { fields: existing, ...content } = raw;
499
599
  const source = existing && typeof existing === "object" && !isFieldEnvelope(existing) ? existing : content;
500
- return { ...raw, fields: toFieldEnvelopes(source) };
600
+ return {
601
+ ...raw,
602
+ fields: toFieldEnvelopes(source, "", {
603
+ owners: (fieldName) => listOwnerForField(fieldName, source, sources)
604
+ })
605
+ };
501
606
  }
502
607
  function walkLayoutNodes(nodes) {
503
608
  if (!Array.isArray(nodes)) return;
504
609
  for (const node of nodes) {
505
610
  if (!node || typeof node !== "object") continue;
506
611
  const rec = node;
507
- rec.props = attachFieldEnvelopes(rec.props);
612
+ rec.props = attachFieldEnvelopes(rec.props, {
613
+ connections: rec.resources,
614
+ reads: rec.reads
615
+ });
508
616
  for (const slot of rec.slots || []) {
509
617
  walkLayoutNodes(slot.children);
510
618
  }
@@ -518,31 +626,36 @@ function envelopePageConfig(page) {
518
626
  }
519
627
  function collectFieldPatches(props) {
520
628
  const out = [];
521
- const walkRepeater = (items, prefix) => {
522
- items.forEach((item, i) => {
523
- const itemPath = joinPath(prefix, String(i));
629
+ const walkRows = (items) => {
630
+ for (const item of items) {
524
631
  if (isBrandedFields(item) || item && typeof item === "object" && !Array.isArray(item) && !isFieldEnvelope(item)) {
525
- walk(item, itemPath);
632
+ walk(item, "");
526
633
  }
527
- });
634
+ }
528
635
  };
529
636
  const walk = (obj, prefix) => {
530
637
  for (const [key, raw] of Object.entries(obj)) {
638
+ if (isIdentifierKey(key)) continue;
531
639
  if (key === "fields" && raw && typeof raw === "object" && !Array.isArray(raw) && !isFieldEnvelope(raw)) {
532
640
  walk(raw, prefix);
533
641
  continue;
534
642
  }
535
643
  const path = joinPath(prefix, key);
536
644
  if (isFieldEnvelope(raw)) {
537
- if (isRepeaterArray(raw.value)) {
538
- walkRepeater(raw.value, raw.name || path);
645
+ if (isRecordList(raw.value)) {
646
+ walkRows(raw.value);
539
647
  continue;
540
648
  }
541
- out.push({ name: raw.name || path, value: raw.value });
649
+ const patch = {
650
+ name: raw.name || path,
651
+ value: raw.value
652
+ };
653
+ if (raw.ref) patch.ref = raw.ref;
654
+ out.push(patch);
542
655
  continue;
543
656
  }
544
- if (isRepeaterArray(raw)) {
545
- walkRepeater(raw, path);
657
+ if (isRecordList(raw)) {
658
+ walkRows(raw);
546
659
  continue;
547
660
  }
548
661
  if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !raw.$$typeof) {
@@ -557,40 +670,14 @@ function collectFieldPatches(props) {
557
670
  }
558
671
  return out;
559
672
  }
560
- function unwrapFieldsBag(item) {
561
- if (!item || typeof item !== "object" || Array.isArray(item)) return {};
562
- const out = {};
563
- for (const [key, raw] of Object.entries(item)) {
564
- if (isFieldEnvelope(raw)) {
565
- out[key] = raw.value;
566
- continue;
567
- }
568
- if (raw && typeof raw === "object" && !Array.isArray(raw) && !isPortableImageShape(raw) && !isEntryRefItem(raw) && !raw.$$typeof) {
569
- out[key] = unwrapFieldsBag(raw);
570
- continue;
571
- }
572
- out[key] = raw;
573
- }
574
- return out;
575
- }
576
- function resolveCollectionItems(field, overlay) {
577
- const name = typeof field?.name === "string" ? field.name : "";
578
- const raw = overlay !== void 0 ? overlay : field?.value;
579
- const list = Array.isArray(raw) ? raw : isFieldEnvelope(raw) && Array.isArray(raw.value) ? raw.value : [];
580
- if (list.length === 0) return [];
581
- if (isRepeaterArray(list) || list.every((item) => isBrandedFields(item))) {
582
- return envelopeRepeaterItems(list, name);
583
- }
584
- return envelopeRepeaterItems(
585
- list.map(
586
- (item) => item && typeof item === "object" && !Array.isArray(item) ? item : {}
587
- ),
588
- name
589
- );
590
- }
591
673
  function resolveEditableBinding(field, value, name) {
592
674
  if (isFieldEnvelope(field)) {
593
- return { name: field.name, value: field.value, requiresName: false };
675
+ return {
676
+ name: field.name,
677
+ value: field.value,
678
+ ref: field.ref,
679
+ requiresName: false
680
+ };
594
681
  }
595
682
  return {
596
683
  name: name || "",
@@ -949,7 +1036,10 @@ async function fetchEdgeResolve(options) {
949
1036
  return envelopeResolvedPayload(body);
950
1037
  }
951
1038
  function envelopeResolvedComponent(node) {
952
- node.props = attachFieldEnvelopes(node.props);
1039
+ node.props = attachFieldEnvelopes(node.props, {
1040
+ connections: node.resources,
1041
+ reads: node.reads
1042
+ });
953
1043
  if (!node.children) return;
954
1044
  for (const kids of Object.values(node.children)) {
955
1045
  for (const child of kids || []) envelopeResolvedComponent(child);
@@ -1208,7 +1298,7 @@ async function fetchEdgeQuery(options) {
1208
1298
  async function queryContent(options) {
1209
1299
  const target = resolveAmplifyUpTarget({ configTarget: options.target });
1210
1300
  const { edgeUrl } = resolveAmplifyUpUrls(target);
1211
- return fetchEdgeQuery({
1301
+ const raw = await fetchEdgeQuery({
1212
1302
  edgeUrl,
1213
1303
  trackingId: options.trackingId,
1214
1304
  route: options.route,
@@ -1217,6 +1307,13 @@ async function queryContent(options) {
1217
1307
  fetch: options.fetch,
1218
1308
  signal: options.signal
1219
1309
  });
1310
+ return {
1311
+ ...raw,
1312
+ results: envelopeListRows(
1313
+ raw.results,
1314
+ listOwnerFromSpec(options.spec)
1315
+ )
1316
+ };
1220
1317
  }
1221
1318
  function nextPageSpec(pagination) {
1222
1319
  return { ...pagination.spec, offset: pagination.nextOffset };
@@ -3713,44 +3810,50 @@ var ComposerFieldStore = class {
3713
3810
  }
3714
3811
  };
3715
3812
  var ComposerStoreContext = createContext(null);
3716
- function matchesNode(eventNodeId, layoutNodeId) {
3717
- if (eventNodeId && layoutNodeId && String(eventNodeId) !== String(layoutNodeId)) {
3718
- return false;
3719
- }
3720
- return true;
3813
+ var ComposerNodeContext = createContext(void 0);
3814
+ function applyComposerPatch(store, name, value, ref, layoutNodeId) {
3815
+ store.set(composerStoreKey(name, ref, layoutNodeId), value);
3721
3816
  }
3722
3817
  function ComposerProvider({
3723
- layoutNodeId,
3724
3818
  children
3725
3819
  }) {
3820
+ const existing = useContext(ComposerStoreContext);
3726
3821
  const storeRef = useRef(null);
3727
3822
  if (!storeRef.current) storeRef.current = new ComposerFieldStore();
3728
- const store = storeRef.current;
3823
+ const store = existing || storeRef.current;
3729
3824
  useEffect(() => {
3730
- const applyField = (fieldName, value, nodeId) => {
3825
+ if (existing) return;
3826
+ const applyField = (fieldName, value, nodeId, ref) => {
3731
3827
  if (!fieldName || typeof fieldName !== "string") return;
3732
- if (!matchesNode(nodeId, layoutNodeId)) return;
3733
- store.set(fieldName, value);
3734
- if (value && typeof value === "object") {
3735
- for (const patch of collectFieldPatches(
3736
- toFieldEnvelopes({ [fieldName]: value })
3737
- )) {
3738
- store.set(patch.name, patch.value);
3739
- }
3740
- }
3828
+ const fieldRef = isFieldRef(ref) ? ref : void 0;
3829
+ const layoutNodeId = typeof nodeId === "string" && nodeId ? nodeId : void 0;
3830
+ applyComposerPatch(store, fieldName, value, fieldRef, layoutNodeId);
3741
3831
  };
3742
3832
  const applyProps = (nextProps, nodeId) => {
3743
3833
  if (!nextProps || typeof nextProps !== "object" || Array.isArray(nextProps)) {
3744
3834
  return;
3745
3835
  }
3746
- if (!matchesNode(nodeId, layoutNodeId)) return;
3747
- for (const patch of collectFieldPatches(nextProps)) {
3748
- store.set(patch.name, patch.value);
3836
+ const layoutNodeId = typeof nodeId === "string" && nodeId ? nodeId : void 0;
3837
+ for (const patch of collectFieldPatches(
3838
+ nextProps
3839
+ )) {
3840
+ applyComposerPatch(
3841
+ store,
3842
+ patch.name,
3843
+ patch.value,
3844
+ patch.ref,
3845
+ patch.ref ? void 0 : layoutNodeId
3846
+ );
3749
3847
  }
3750
3848
  };
3751
3849
  const onPatch = (event) => {
3752
3850
  const detail = event.detail || {};
3753
- applyField(detail.fieldName, detail.value, detail.layoutNodeId);
3851
+ applyField(
3852
+ detail.fieldName,
3853
+ detail.value,
3854
+ detail.layoutNodeId,
3855
+ detail.ref
3856
+ );
3754
3857
  };
3755
3858
  const onPropsPatch = (event) => {
3756
3859
  const detail = event.detail || {};
@@ -3762,7 +3865,12 @@ function ComposerProvider({
3762
3865
  return;
3763
3866
  }
3764
3867
  if (event.data?.type !== "AMPLIFYUP_FIELD_PATCH") return;
3765
- applyField(event.data.fieldName, event.data.value, event.data.layoutNodeId);
3868
+ applyField(
3869
+ event.data.fieldName,
3870
+ event.data.value,
3871
+ event.data.layoutNodeId,
3872
+ event.data.ref
3873
+ );
3766
3874
  };
3767
3875
  window.addEventListener("amplifyup:field-patch", onPatch);
3768
3876
  window.addEventListener("amplifyup:props-patch", onPropsPatch);
@@ -3772,29 +3880,29 @@ function ComposerProvider({
3772
3880
  window.removeEventListener("amplifyup:props-patch", onPropsPatch);
3773
3881
  window.removeEventListener("message", onMessage);
3774
3882
  };
3775
- }, [layoutNodeId, store]);
3883
+ }, [existing, store]);
3884
+ if (existing) return /* @__PURE__ */ jsx(Fragment, { children });
3776
3885
  return /* @__PURE__ */ jsx(ComposerStoreContext.Provider, { value: store, children });
3777
3886
  }
3778
- function useComposerFieldValue(name, fallback) {
3887
+ function useComposerFieldValue(name, fallback, ref) {
3779
3888
  const store = useContext(ComposerStoreContext);
3889
+ const layoutNodeId = useContext(ComposerNodeContext);
3890
+ const key = composerStoreKey(name, ref, layoutNodeId);
3780
3891
  const subscribe = useCallback(
3781
3892
  (onStoreChange) => {
3782
3893
  if (!store || !name) return () => {
3783
3894
  };
3784
- return store.subscribe(name, onStoreChange);
3895
+ return store.subscribe(key, onStoreChange);
3785
3896
  },
3786
- [store, name]
3897
+ [store, name, key]
3787
3898
  );
3788
3899
  const getSnapshot = useCallback(() => {
3789
3900
  if (!store || !name) return void 0;
3790
- return store.get(name);
3791
- }, [store, name]);
3901
+ return store.get(key);
3902
+ }, [store, name, key]);
3792
3903
  const overlay = useSyncExternalStore(subscribe, getSnapshot, () => void 0);
3793
3904
  return overlay !== void 0 ? overlay : fallback;
3794
3905
  }
3795
- function useComposerStore() {
3796
- return useContext(ComposerStoreContext);
3797
- }
3798
3906
  function MaybeComposerProvider({
3799
3907
  enabled,
3800
3908
  layoutNodeId,
@@ -3802,7 +3910,7 @@ function MaybeComposerProvider({
3802
3910
  }) {
3803
3911
  const content = useMemo(() => children, [children]);
3804
3912
  if (!enabled) return /* @__PURE__ */ jsx(Fragment, { children: content });
3805
- return /* @__PURE__ */ jsx(ComposerProvider, { layoutNodeId, children });
3913
+ return /* @__PURE__ */ jsx(ComposerProvider, { children: /* @__PURE__ */ jsx(ComposerNodeContext.Provider, { value: layoutNodeId, children }) });
3806
3914
  }
3807
3915
  function nodeRenderProps(node) {
3808
3916
  const settings = node.settings || {};
@@ -4260,7 +4368,7 @@ function AmplifyRendererInner({
4260
4368
  if (layoutTree.length === 0) {
4261
4369
  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." }) }) });
4262
4370
  }
4263
- return /* @__PURE__ */ jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsx(
4371
+ return /* @__PURE__ */ jsx(ComposerProvider, { children: /* @__PURE__ */ jsx("main", { children: composerPreview || isComposerPreview() ? /* @__PURE__ */ jsx(
4264
4372
  "div",
4265
4373
  {
4266
4374
  "data-slot-id": "main",
@@ -4269,7 +4377,7 @@ function AmplifyRendererInner({
4269
4377
  "data-slot-empty": layoutTree.length === 0 ? "true" : void 0,
4270
4378
  children: layoutTree.map((node) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node) }, node.id))
4271
4379
  }
4272
- ) : layoutTree.map((node) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node) }, node.id)) });
4380
+ ) : layoutTree.map((node) => /* @__PURE__ */ jsx(Fragment$1, { children: renderNode(node) }, node.id)) }) });
4273
4381
  }
4274
4382
  var AmplifyRenderer = memo(AmplifyRendererInner);
4275
4383
  function AddComponentIcon({ size = 28 }) {
@@ -4622,10 +4730,10 @@ function useBoundField(field, value, name, component) {
4622
4730
  `[AmplifyUp SDK] ${component} value={\u2026} requires name="\u2026" for computed values`
4623
4731
  );
4624
4732
  }
4625
- const fieldValue = useComposerFieldValue(bound.name, bound.value);
4626
- return { editName: bound.name, fieldValue };
4733
+ const fieldValue = useComposerFieldValue(bound.name, bound.value, bound.ref);
4734
+ return { editName: bound.name, fieldValue, ref: bound.ref };
4627
4735
  }
4628
- function commitComposerField(layoutNodeId, componentId, name, value) {
4736
+ function commitComposerField(layoutNodeId, componentId, name, value, ref) {
4629
4737
  if (typeof window === "undefined" || window.parent === window) return;
4630
4738
  window.parent.postMessage(
4631
4739
  {
@@ -4633,7 +4741,8 @@ function commitComposerField(layoutNodeId, componentId, name, value) {
4633
4741
  layoutNodeId,
4634
4742
  componentId,
4635
4743
  fieldName: name,
4636
- value
4744
+ value,
4745
+ ...ref ? { ref } : {}
4637
4746
  },
4638
4747
  "*"
4639
4748
  );
@@ -4658,6 +4767,7 @@ function requestCanvasRichTextEdit(opts) {
4658
4767
  componentId: opts.componentId,
4659
4768
  fieldName: opts.name,
4660
4769
  value: opts.value,
4770
+ ...opts.fieldRef ? { ref: opts.fieldRef } : {},
4661
4771
  bounds: {
4662
4772
  top: rect.top,
4663
4773
  left: rect.left,
@@ -4682,6 +4792,7 @@ function ComposerInlineField({
4682
4792
  className,
4683
4793
  layoutNodeId,
4684
4794
  componentId,
4795
+ fieldRef,
4685
4796
  rich = false
4686
4797
  }) {
4687
4798
  const [focused, setFocused] = useState(false);
@@ -4710,12 +4821,12 @@ function ComposerInlineField({
4710
4821
  if (storedEmpty && next === "") {
4711
4822
  if (elRef.current) elRef.current.textContent = hint;
4712
4823
  if (typeof value === "string" && EMPTY_FIELD_PLACEHOLDERS.has(value.trim())) {
4713
- commitComposerField(layoutNodeId, componentId, name, "");
4824
+ commitComposerField(layoutNodeId, componentId, name, "", fieldRef);
4714
4825
  }
4715
4826
  return;
4716
4827
  }
4717
4828
  if (next === text) return;
4718
- commitComposerField(layoutNodeId, componentId, name, next);
4829
+ commitComposerField(layoutNodeId, componentId, name, next, fieldRef);
4719
4830
  };
4720
4831
  const openRichEditor = (event) => {
4721
4832
  if (!isComposerEditMode() || !elRef.current) return;
@@ -4727,7 +4838,8 @@ function ComposerInlineField({
4727
4838
  layoutNodeId,
4728
4839
  componentId,
4729
4840
  name,
4730
- value: source
4841
+ value: source,
4842
+ fieldRef
4731
4843
  });
4732
4844
  };
4733
4845
  return /* @__PURE__ */ jsx(
@@ -4738,6 +4850,9 @@ function ComposerInlineField({
4738
4850
  "data-amplifyup-field": name,
4739
4851
  "data-amplifyup-field-type": rich ? "richtext" : "text",
4740
4852
  "data-amplifyup-node-id": layoutNodeId || void 0,
4853
+ "data-amplifyup-ref-provider": fieldRef?.providerId || void 0,
4854
+ "data-amplifyup-ref-entity": fieldRef?.entity || void 0,
4855
+ "data-amplifyup-ref-id": fieldRef?.id || void 0,
4741
4856
  "data-amplifyup-empty-hint": showHint ? "true" : void 0,
4742
4857
  contentEditable: !rich,
4743
4858
  suppressContentEditableWarning: true,
@@ -4802,7 +4917,8 @@ function ComposerInlineHtml({
4802
4917
  className,
4803
4918
  Tag = "div",
4804
4919
  layoutNodeId,
4805
- componentId
4920
+ componentId,
4921
+ fieldRef
4806
4922
  }) {
4807
4923
  const elRef = useRef(null);
4808
4924
  useEffect(() => {
@@ -4819,7 +4935,8 @@ function ComposerInlineHtml({
4819
4935
  layoutNodeId,
4820
4936
  componentId,
4821
4937
  name,
4822
- value: sourceValue
4938
+ value: sourceValue,
4939
+ fieldRef
4823
4940
  });
4824
4941
  };
4825
4942
  return React2.createElement(Tag, {
@@ -4828,6 +4945,9 @@ function ComposerInlineHtml({
4828
4945
  "data-amplifyup-field": name,
4829
4946
  "data-amplifyup-field-type": "richtext",
4830
4947
  "data-amplifyup-node-id": layoutNodeId || void 0,
4948
+ "data-amplifyup-ref-provider": fieldRef?.providerId || void 0,
4949
+ "data-amplifyup-ref-entity": fieldRef?.entity || void 0,
4950
+ "data-amplifyup-ref-id": fieldRef?.id || void 0,
4831
4951
  dangerouslySetInnerHTML: { __html: html },
4832
4952
  onMouseDown: (event) => {
4833
4953
  event.stopPropagation();
@@ -4859,6 +4979,40 @@ function formatFieldValue(value) {
4859
4979
  }
4860
4980
  return "";
4861
4981
  }
4982
+ var FIELD_SCALAR_HINT = "Field expects a scalar field. Did you mean <Field field={post.title} />?";
4983
+ function warnNotScalarField(component, field, fieldValue) {
4984
+ if (process.env.NODE_ENV === "production") return false;
4985
+ if (isBrandedFields(field)) {
4986
+ console.error(`[AmplifyUp SDK] ${component}: ${FIELD_SCALAR_HINT}`);
4987
+ return true;
4988
+ }
4989
+ if (isFieldEnvelope(field) && Array.isArray(field.value)) {
4990
+ console.error(`[AmplifyUp SDK] ${component}: ${FIELD_SCALAR_HINT}`);
4991
+ return true;
4992
+ }
4993
+ if (Array.isArray(fieldValue)) {
4994
+ console.error(`[AmplifyUp SDK] ${component}: ${FIELD_SCALAR_HINT}`);
4995
+ return true;
4996
+ }
4997
+ return false;
4998
+ }
4999
+ var warnedReadOnlyFields = /* @__PURE__ */ new Set();
5000
+ function warnReadOnlyRowField(name) {
5001
+ if (process.env.NODE_ENV === "production") return;
5002
+ const key = name || "(unnamed)";
5003
+ if (warnedReadOnlyFields.has(key)) return;
5004
+ warnedReadOnlyFields.add(key);
5005
+ console.warn(
5006
+ `[AmplifyUp SDK] ${key} has no write target; rendered read-only.`
5007
+ );
5008
+ }
5009
+ function canComposerBind(field, ref, editName) {
5010
+ if (isReadOnlyRowField(field) || isFieldEnvelope(field) && field.readOnly) {
5011
+ warnReadOnlyRowField(editName || (isFieldEnvelope(field) ? field.name : ""));
5012
+ return false;
5013
+ }
5014
+ return true;
5015
+ }
4862
5016
  function warnProjectionProps(component, hasField, hasValue, name) {
4863
5017
  if (process.env.NODE_ENV === "production") return;
4864
5018
  if (!hasField && !hasValue && !name) {
@@ -4876,16 +5030,20 @@ function Field(props) {
4876
5030
  const composerEdit = useComposerEditMode();
4877
5031
  const hasField = isFieldEnvelope(field);
4878
5032
  const hasValue = hasOwnProp(props, "value");
4879
- const { editName, fieldValue } = useBoundField(field, value, name, "Field");
5033
+ const { editName, fieldValue, ref } = useBoundField(field, value, name, "Field");
4880
5034
  warnProjectionProps("Field", hasField, hasValue, name);
5035
+ if (warnNotScalarField("Field", field, fieldValue)) {
5036
+ return null;
5037
+ }
4881
5038
  if (!hasField && !hasValue && !name) {
4882
5039
  return null;
4883
5040
  }
4884
- if (Array.isArray(fieldValue)) {
5041
+ if (isBrandedFields(field) || Array.isArray(fieldValue)) {
4885
5042
  return null;
4886
5043
  }
4887
5044
  const empty = isComposerEmptyText(fieldValue);
4888
- if (composerEdit && editName && context && canInlineEdit(editName, fieldValue)) {
5045
+ const bindComposer = composerEdit && editName && context && canInlineEdit(editName, fieldValue) && canComposerBind(field, ref, editName);
5046
+ if (bindComposer) {
4889
5047
  return /* @__PURE__ */ jsx(
4890
5048
  ComposerInlineField,
4891
5049
  {
@@ -4894,6 +5052,7 @@ function Field(props) {
4894
5052
  className,
4895
5053
  layoutNodeId: context.layoutNodeId,
4896
5054
  componentId: context.componentId,
5055
+ fieldRef: ref,
4897
5056
  rich: looksLikeRichCanvasField(editName)
4898
5057
  }
4899
5058
  );
@@ -4903,186 +5062,6 @@ function Field(props) {
4903
5062
  if (!text) return null;
4904
5063
  return /* @__PURE__ */ jsx("span", { className, children: text });
4905
5064
  }
4906
- var COMPOSER_EMPTY_COLLECTION_HINT = "Click here to add an item";
4907
- function CollectionChromeButton({
4908
- label,
4909
- onClick
4910
- }) {
4911
- return /* @__PURE__ */ jsx(
4912
- "button",
4913
- {
4914
- type: "button",
4915
- onMouseDown: (event) => event.stopPropagation(),
4916
- onClick: (event) => {
4917
- event.preventDefault();
4918
- event.stopPropagation();
4919
- onClick();
4920
- },
4921
- style: {
4922
- fontSize: 11,
4923
- lineHeight: 1.2,
4924
- padding: "4px 8px",
4925
- border: "1px solid color-mix(in srgb, currentColor 28%, transparent)",
4926
- borderRadius: 6,
4927
- background: "transparent",
4928
- cursor: "pointer",
4929
- color: "inherit"
4930
- },
4931
- children: label
4932
- }
4933
- );
4934
- }
4935
- function Collection({
4936
- field,
4937
- children,
4938
- className
4939
- }) {
4940
- const context = useContext(ComponentContext);
4941
- const store = useComposerStore();
4942
- const composerEdit = useComposerEditMode();
4943
- const name = isFieldEnvelope(field) ? field.name : "";
4944
- const overlay = useComposerFieldValue(name, void 0);
4945
- const items = resolveCollectionItems(field, overlay);
4946
- const writeItems = (nextPlain) => {
4947
- if (name && store) {
4948
- const prefixed = name ? store.keys().filter((key) => key === name || key.startsWith(`${name}.`)) : [];
4949
- for (const key of prefixed) store.delete(key);
4950
- store.set(name, nextPlain);
4951
- for (const patch of collectFieldPatches(
4952
- toFieldEnvelopes({ [name]: nextPlain })
4953
- )) {
4954
- store.set(patch.name, patch.value);
4955
- }
4956
- }
4957
- commitComposerField(
4958
- context?.layoutNodeId,
4959
- context?.componentId,
4960
- name,
4961
- nextPlain
4962
- );
4963
- };
4964
- const plainItems = () => items.map((item, index) => {
4965
- const base = unwrapFieldsBag(item);
4966
- if (!store || !name) return base;
4967
- const out = { ...base };
4968
- for (const key of Object.keys(out)) {
4969
- const overlayValue = store.get(`${name}.${index}.${key}`);
4970
- if (overlayValue !== void 0) out[key] = overlayValue;
4971
- }
4972
- return out;
4973
- });
4974
- const addItem = () => writeItems([...plainItems(), {}]);
4975
- const removeItem = (index) => writeItems(plainItems().filter((_, i) => i !== index));
4976
- const moveItem = (index, delta) => {
4977
- const next = plainItems();
4978
- const target = index + delta;
4979
- if (target < 0 || target >= next.length) return;
4980
- const swap = next[index];
4981
- next[index] = next[target];
4982
- next[target] = swap;
4983
- writeItems(next);
4984
- };
4985
- if (!composerEdit) {
4986
- if (items.length === 0) return null;
4987
- return /* @__PURE__ */ jsx("span", { className, style: { display: "contents" }, children: items.map((item, index) => /* @__PURE__ */ jsx(React2.Fragment, { children: children(item, index) }, item && typeof item === "object" ? `${name}.${index}` : index)) });
4988
- }
4989
- if (items.length === 0) {
4990
- return /* @__PURE__ */ jsx(
4991
- "span",
4992
- {
4993
- className,
4994
- "data-amplifyup-collection": name || void 0,
4995
- "data-amplifyup-collection-empty": "true",
4996
- "data-amplifyup-node-id": context?.layoutNodeId || void 0,
4997
- role: "button",
4998
- tabIndex: 0,
4999
- onMouseDown: (event) => event.stopPropagation(),
5000
- onClick: (event) => {
5001
- event.preventDefault();
5002
- event.stopPropagation();
5003
- addItem();
5004
- },
5005
- onKeyDown: (event) => {
5006
- if (event.key === "Enter" || event.key === " ") {
5007
- event.preventDefault();
5008
- addItem();
5009
- }
5010
- },
5011
- style: {
5012
- display: "block",
5013
- cursor: "pointer",
5014
- outline: "1px dashed color-mix(in srgb, currentColor 40%, transparent)",
5015
- outlineOffset: 2,
5016
- minHeight: "3em",
5017
- opacity: 0.5
5018
- },
5019
- children: /* @__PURE__ */ jsx(EmptySlotHint, { label: COMPOSER_EMPTY_COLLECTION_HINT, compact: true })
5020
- }
5021
- );
5022
- }
5023
- return /* @__PURE__ */ jsxs(
5024
- "span",
5025
- {
5026
- className,
5027
- "data-amplifyup-collection": name || void 0,
5028
- "data-amplifyup-node-id": context?.layoutNodeId || void 0,
5029
- style: { display: "flex", flexDirection: "column", gap: 8 },
5030
- children: [
5031
- items.map((item, index) => /* @__PURE__ */ jsxs(
5032
- "span",
5033
- {
5034
- "data-amplifyup-collection-item": `${name}.${index}`,
5035
- style: {
5036
- display: "block",
5037
- outline: "1px dashed color-mix(in srgb, currentColor 28%, transparent)",
5038
- outlineOffset: 2,
5039
- padding: 8
5040
- },
5041
- children: [
5042
- /* @__PURE__ */ jsxs(
5043
- "span",
5044
- {
5045
- style: {
5046
- display: "flex",
5047
- gap: 6,
5048
- justifyContent: "flex-end",
5049
- marginBottom: 8
5050
- },
5051
- children: [
5052
- /* @__PURE__ */ jsx(
5053
- CollectionChromeButton,
5054
- {
5055
- label: "Move up",
5056
- onClick: () => moveItem(index, -1)
5057
- }
5058
- ),
5059
- /* @__PURE__ */ jsx(
5060
- CollectionChromeButton,
5061
- {
5062
- label: "Move down",
5063
- onClick: () => moveItem(index, 1)
5064
- }
5065
- ),
5066
- /* @__PURE__ */ jsx(
5067
- CollectionChromeButton,
5068
- {
5069
- label: "Remove",
5070
- onClick: () => removeItem(index)
5071
- }
5072
- )
5073
- ]
5074
- }
5075
- ),
5076
- children(item, index)
5077
- ]
5078
- },
5079
- `${name}.${index}`
5080
- )),
5081
- /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(CollectionChromeButton, { label: "Add item", onClick: addItem }) })
5082
- ]
5083
- }
5084
- );
5085
- }
5086
5065
  function markdownToHtml(markdown) {
5087
5066
  try {
5088
5067
  const result = marked.parse(markdown, { async: false });
@@ -5106,7 +5085,7 @@ function RichText(props) {
5106
5085
  const hasField = isFieldEnvelope(field);
5107
5086
  const hasValueProp = hasOwnProp(props, "value") || childrenValue !== void 0;
5108
5087
  const boundValue = hasOwnProp(props, "value") ? value : childrenValue !== void 0 ? childrenValue : value;
5109
- const { editName, fieldValue } = useBoundField(
5088
+ const { editName, fieldValue, ref } = useBoundField(
5110
5089
  field,
5111
5090
  boundValue,
5112
5091
  name,
@@ -5116,7 +5095,10 @@ function RichText(props) {
5116
5095
  return null;
5117
5096
  }
5118
5097
  warnProjectionProps("RichText", hasField, hasValueProp, name);
5119
- const canEdit = !!context && canInlineEditRichText(editName, fieldValue);
5098
+ if (warnNotScalarField("RichText", field, fieldValue)) {
5099
+ return null;
5100
+ }
5101
+ const canEdit = !!context && canInlineEditRichText(editName, fieldValue) && canComposerBind(field, ref, editName);
5120
5102
  if (isComposerEmptyText(fieldValue)) {
5121
5103
  if (composerEdit && canEdit) {
5122
5104
  return /* @__PURE__ */ jsx(
@@ -5127,6 +5109,7 @@ function RichText(props) {
5127
5109
  className,
5128
5110
  layoutNodeId: context?.layoutNodeId,
5129
5111
  componentId: context?.componentId,
5112
+ fieldRef: ref,
5130
5113
  rich: true
5131
5114
  }
5132
5115
  );
@@ -5158,7 +5141,8 @@ function RichText(props) {
5158
5141
  className,
5159
5142
  Tag,
5160
5143
  layoutNodeId: context?.layoutNodeId,
5161
- componentId: context?.componentId
5144
+ componentId: context?.componentId,
5145
+ fieldRef: ref
5162
5146
  }
5163
5147
  );
5164
5148
  }
@@ -5188,9 +5172,12 @@ function Image(props) {
5188
5172
  const context = useContext(ComponentContext);
5189
5173
  const hasField = isFieldEnvelope(field);
5190
5174
  const hasValue = hasOwnProp(props, "value");
5191
- const { editName, fieldValue } = useBoundField(field, value, name, "Image");
5175
+ const { editName, fieldValue, ref } = useBoundField(field, value, name, "Image");
5192
5176
  const composerEdit = useComposerEditMode();
5193
5177
  warnProjectionProps("Image", hasField, hasValue, name);
5178
+ if (warnNotScalarField("Image", field, fieldValue)) {
5179
+ return null;
5180
+ }
5194
5181
  if (!hasField && !hasValue && !name) {
5195
5182
  return null;
5196
5183
  }
@@ -5199,10 +5186,13 @@ function Image(props) {
5199
5186
  const alt = altProp ?? portable?.alt ?? "";
5200
5187
  const width = widthProp ?? portable?.width;
5201
5188
  const height = heightProp ?? portable?.height;
5202
- const fieldAttrs = composerEdit && editName ? {
5189
+ const fieldAttrs = composerEdit && editName && canComposerBind(field, ref, editName) ? {
5203
5190
  "data-amplifyup-field": editName,
5204
5191
  "data-amplifyup-field-type": "image",
5205
- "data-amplifyup-node-id": context?.layoutNodeId || void 0
5192
+ "data-amplifyup-node-id": context?.layoutNodeId || void 0,
5193
+ "data-amplifyup-ref-provider": ref?.providerId || void 0,
5194
+ "data-amplifyup-ref-entity": ref?.entity || void 0,
5195
+ "data-amplifyup-ref-id": ref?.id || void 0
5206
5196
  } : null;
5207
5197
  if (composerEdit && !editName && (hasField || hasValue)) {
5208
5198
  console.warn(
@@ -5471,6 +5461,6 @@ function useAmplifyUp() {
5471
5461
  return context;
5472
5462
  }
5473
5463
 
5474
- export { AmplifyPageContent, AmplifyRenderer, AmplifyUpProvider, Collection, ComponentContextProvider, ComposerProvider, Field, Image, MaybeComposerProvider, QueryContentError, RichText, SelectVariantsError, Slot, isBrandedFields, isComposerPreview, isFieldEnvelope, isPreviewRequest, isWorkingContentRequest, nextPageSpec, queryContent, searchSpec, selectVariants, toFieldEnvelopes, useAmplifyUp };
5464
+ export { AmplifyPageContent, AmplifyRenderer, AmplifyUpProvider, ComponentContextProvider, ComposerProvider, Field, Image, MaybeComposerProvider, QueryContentError, RichText, SelectVariantsError, Slot, attachFieldEnvelopes, composerStoreKey, envelopeListRows, isBrandedFields, isComposerPreview, isFieldEnvelope, isPreviewRequest, isReadOnlyRowField, isWorkingContentRequest, listOwnerFromSpec, nextPageSpec, queryContent, searchSpec, selectVariants, toFieldEnvelopes, useAmplifyUp };
5475
5465
  //# sourceMappingURL=react.mjs.map
5476
5466
  //# sourceMappingURL=react.mjs.map