@aphexcms/cms-core 6.0.0 → 8.0.0

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.
Files changed (132) hide show
  1. package/dist/api/documents.d.ts +19 -0
  2. package/dist/api/documents.d.ts.map +1 -1
  3. package/dist/api/documents.js +21 -0
  4. package/dist/api/schemas/documents.d.ts +3 -0
  5. package/dist/api/schemas/documents.d.ts.map +1 -1
  6. package/dist/api/schemas/documents.js +12 -0
  7. package/dist/cli/generate-types.d.ts.map +1 -1
  8. package/dist/cli/generate-types.js +133 -19
  9. package/dist/cli/generate-types.js.map +1 -1
  10. package/dist/components/AdminApp.svelte +74 -47
  11. package/dist/components/AdminApp.svelte.d.ts.map +1 -1
  12. package/dist/components/admin/DocumentEditor.svelte +65 -2
  13. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
  14. package/dist/components/admin/confirm-dialog/ConfirmDialogHost.svelte +8 -6
  15. package/dist/components/admin/confirm-dialog/ConfirmDialogHost.svelte.d.ts.map +1 -1
  16. package/dist/components/admin/fields/ArrayField.svelte +149 -2
  17. package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
  18. package/dist/components/admin/fields/ReferenceField.svelte +198 -125
  19. package/dist/components/admin/fields/ReferenceField.svelte.d.ts +9 -2
  20. package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
  21. package/dist/components/layout/Sidebar.svelte +1 -1
  22. package/dist/components/layout/sidebar/AppSidebar.svelte +2 -1
  23. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +1 -1
  24. package/dist/components/layout/sidebar/NavMain.svelte +9 -2
  25. package/dist/components/layout/sidebar/NavMain.svelte.d.ts +2 -0
  26. package/dist/components/layout/sidebar/NavMain.svelte.d.ts.map +1 -1
  27. package/dist/db/interfaces/index.d.ts +3 -1
  28. package/dist/db/interfaces/index.d.ts.map +1 -1
  29. package/dist/db/interfaces/reference.d.ts +43 -0
  30. package/dist/db/interfaces/reference.d.ts.map +1 -0
  31. package/dist/db/interfaces/reference.js +4 -0
  32. package/dist/db/utils/reference-resolver.d.ts +4 -6
  33. package/dist/db/utils/reference-resolver.d.ts.map +1 -1
  34. package/dist/db/utils/reference-resolver.js +33 -31
  35. package/dist/document-refresh.svelte.d.ts +3 -0
  36. package/dist/document-refresh.svelte.d.ts.map +1 -0
  37. package/dist/document-refresh.svelte.js +21 -0
  38. package/dist/graphql/resolvers.d.ts.map +1 -1
  39. package/dist/graphql/resolvers.js +61 -1
  40. package/dist/graphql/schema.d.ts.map +1 -1
  41. package/dist/graphql/schema.js +10 -0
  42. package/dist/lib/api/documents.d.ts +19 -0
  43. package/dist/lib/api/documents.d.ts.map +1 -1
  44. package/dist/lib/api/documents.js +21 -0
  45. package/dist/lib/api/documents.js.map +1 -1
  46. package/dist/lib/api/schemas/documents.d.ts +3 -0
  47. package/dist/lib/api/schemas/documents.d.ts.map +1 -1
  48. package/dist/lib/api/schemas/documents.js +12 -0
  49. package/dist/lib/api/schemas/documents.js.map +1 -1
  50. package/dist/lib/db/interfaces/index.d.ts +3 -1
  51. package/dist/lib/db/interfaces/index.d.ts.map +1 -1
  52. package/dist/lib/db/interfaces/reference.d.ts +43 -0
  53. package/dist/lib/db/interfaces/reference.d.ts.map +1 -0
  54. package/dist/lib/db/interfaces/reference.js +5 -0
  55. package/dist/lib/db/interfaces/reference.js.map +1 -0
  56. package/dist/lib/db/utils/reference-resolver.d.ts +4 -6
  57. package/dist/lib/db/utils/reference-resolver.d.ts.map +1 -1
  58. package/dist/lib/db/utils/reference-resolver.js +33 -31
  59. package/dist/lib/db/utils/reference-resolver.js.map +1 -1
  60. package/dist/lib/document-refresh.svelte.d.ts +3 -0
  61. package/dist/lib/document-refresh.svelte.d.ts.map +1 -0
  62. package/dist/lib/document-refresh.svelte.js +22 -0
  63. package/dist/lib/document-refresh.svelte.js.map +1 -0
  64. package/dist/lib/graphql/resolvers.d.ts.map +1 -1
  65. package/dist/lib/graphql/resolvers.js +61 -1
  66. package/dist/lib/graphql/resolvers.js.map +1 -1
  67. package/dist/lib/graphql/schema.d.ts.map +1 -1
  68. package/dist/lib/graphql/schema.js +10 -0
  69. package/dist/lib/graphql/schema.js.map +1 -1
  70. package/dist/lib/local-api/collection-api.d.ts +10 -1
  71. package/dist/lib/local-api/collection-api.d.ts.map +1 -1
  72. package/dist/lib/local-api/collection-api.js +39 -1
  73. package/dist/lib/local-api/collection-api.js.map +1 -1
  74. package/dist/lib/local-api/index.d.ts +29 -0
  75. package/dist/lib/local-api/index.d.ts.map +1 -1
  76. package/dist/lib/local-api/index.js +66 -2
  77. package/dist/lib/local-api/index.js.map +1 -1
  78. package/dist/lib/routes/assets-cdn.d.ts.map +1 -1
  79. package/dist/lib/routes/assets-cdn.js +14 -1
  80. package/dist/lib/routes/assets-cdn.js.map +1 -1
  81. package/dist/lib/schema-utils/validator.js +21 -11
  82. package/dist/lib/schema-utils/validator.js.map +1 -1
  83. package/dist/lib/server/api/index.d.ts.map +1 -1
  84. package/dist/lib/server/api/index.js +3 -1
  85. package/dist/lib/server/api/index.js.map +1 -1
  86. package/dist/lib/server/api/routes/documents-by-id.d.ts.map +1 -1
  87. package/dist/lib/server/api/routes/documents-by-id.js +23 -0
  88. package/dist/lib/server/api/routes/documents-by-id.js.map +1 -1
  89. package/dist/lib/server/api/routes/documents.d.ts.map +1 -1
  90. package/dist/lib/server/api/routes/documents.js +29 -1
  91. package/dist/lib/server/api/routes/documents.js.map +1 -1
  92. package/dist/lib/services/references-service.d.ts +35 -0
  93. package/dist/lib/services/references-service.d.ts.map +1 -0
  94. package/dist/lib/services/references-service.js +60 -0
  95. package/dist/lib/services/references-service.js.map +1 -0
  96. package/dist/lib/types/schemas.d.ts +8 -0
  97. package/dist/lib/types/schemas.d.ts.map +1 -1
  98. package/dist/lib/utils/preview.d.ts +2 -3
  99. package/dist/lib/utils/preview.d.ts.map +1 -1
  100. package/dist/lib/utils/preview.js +35 -8
  101. package/dist/lib/utils/preview.js.map +1 -1
  102. package/dist/lib/utils/reference-walk.d.ts +8 -0
  103. package/dist/lib/utils/reference-walk.d.ts.map +1 -0
  104. package/dist/lib/utils/reference-walk.js +39 -0
  105. package/dist/lib/utils/reference-walk.js.map +1 -0
  106. package/dist/local-api/collection-api.d.ts +10 -1
  107. package/dist/local-api/collection-api.d.ts.map +1 -1
  108. package/dist/local-api/collection-api.js +39 -1
  109. package/dist/local-api/index.d.ts +29 -0
  110. package/dist/local-api/index.d.ts.map +1 -1
  111. package/dist/local-api/index.js +66 -2
  112. package/dist/routes/assets-cdn.d.ts.map +1 -1
  113. package/dist/routes/assets-cdn.js +14 -1
  114. package/dist/schema-utils/validator.js +21 -11
  115. package/dist/server/api/index.d.ts.map +1 -1
  116. package/dist/server/api/index.js +3 -1
  117. package/dist/server/api/routes/documents-by-id.d.ts.map +1 -1
  118. package/dist/server/api/routes/documents-by-id.js +23 -0
  119. package/dist/server/api/routes/documents.d.ts.map +1 -1
  120. package/dist/server/api/routes/documents.js +29 -1
  121. package/dist/services/references-service.d.ts +35 -0
  122. package/dist/services/references-service.d.ts.map +1 -0
  123. package/dist/services/references-service.js +59 -0
  124. package/dist/types/schemas.d.ts +8 -0
  125. package/dist/types/schemas.d.ts.map +1 -1
  126. package/dist/utils/preview.d.ts +2 -3
  127. package/dist/utils/preview.d.ts.map +1 -1
  128. package/dist/utils/preview.js +35 -8
  129. package/dist/utils/reference-walk.d.ts +8 -0
  130. package/dist/utils/reference-walk.d.ts.map +1 -0
  131. package/dist/utils/reference-walk.js +38 -0
  132. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"AdminApp.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AdminApp.svelte.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AA4BnE,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAA;KAAE,CAAC;IACxD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAChD;AA0/CF,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"AdminApp.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AdminApp.svelte.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AA4BnE,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAA;KAAE,CAAC;IACxD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAChD;AAkhDF,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -13,6 +13,8 @@
13
13
  import { setSaveStateContext } from '../../save-state-context.svelte';
14
14
  import { usePermissions } from '../../permissions-context.svelte';
15
15
  import { getDefaultValueForFieldType } from '../../utils/field-defaults';
16
+ import { notifyDocumentChanged } from '../../document-refresh.svelte';
17
+ import { collectReferenceIds } from '../../utils/reference-walk';
16
18
  import { cmsLogger } from '../../utils/logger';
17
19
  import { toast } from 'svelte-sonner';
18
20
  import { confirmDialog } from './confirm-dialog/confirm-dialog.svelte';
@@ -645,6 +647,7 @@
645
647
  if (response?.success) {
646
648
  lastSaved = new Date();
647
649
  hasUnsavedChanges = false;
650
+ if (response.data?.id) notifyDocumentChanged(response.data.id);
648
651
  if (isAutoSave) {
649
652
  // Trigger validation on all fields after auto-save
650
653
  validateAllFields(); // Update validation status
@@ -716,6 +719,47 @@
716
719
  return;
717
720
  }
718
721
 
722
+ // Referential integrity guard: every referenced document must itself be
723
+ // published. Without this, publishing a doc that points at drafts would
724
+ // expose dangling references in the published perspective. One batched
725
+ // HTTP call regardless of ref count.
726
+ const refIds = collectReferenceIds(documentData, schema, schemas);
727
+ if (refIds.length > 0) {
728
+ let fetched: any[] = [];
729
+ try {
730
+ const res = await documents.getMany(refIds);
731
+ if (res.success && res.data) fetched = res.data;
732
+ } catch {
733
+ // fall through — missing fetched IDs become "blockers" below
734
+ }
735
+ const fetchedById = new Map(fetched.map((d) => [d.id, d]));
736
+ const checks = refIds.map((id) => ({ id, doc: fetchedById.get(id) ?? null }));
737
+ const blockers = checks.filter(
738
+ (c) => !c.doc || (c.doc as any)._meta?.status !== 'published'
739
+ );
740
+ if (blockers.length > 0) {
741
+ const titles = blockers
742
+ .slice(0, 3)
743
+ .map((b) => {
744
+ if (!b.doc) return `Missing (${b.id.slice(0, 8)})`;
745
+ const d = b.doc as any;
746
+ const label = d.title ?? d.name ?? d.heading ?? d.label ?? b.id;
747
+ const type = d._meta?.type;
748
+ return type ? `"${label}" (${type})` : `"${label}"`;
749
+ })
750
+ .join(', ');
751
+ const remainder = blockers.length > 3 ? ` and ${blockers.length - 3} more` : '';
752
+ saveError = `Cannot publish — unpublished references: ${titles}${remainder}`;
753
+ toast.error(
754
+ `${blockers.length} referenced document${blockers.length === 1 ? '' : 's'} ${blockers.length === 1 ? 'is' : 'are'} not published`,
755
+ {
756
+ description: 'Publish the referenced documents first, then try again.'
757
+ }
758
+ );
759
+ return;
760
+ }
761
+ }
762
+
719
763
  saving = true;
720
764
  saveError = null;
721
765
 
@@ -733,6 +777,7 @@
733
777
  fullDocument = response.data;
734
778
  lastSaved = new Date();
735
779
  publishSuccess = new Date();
780
+ notifyDocumentChanged(documentId);
736
781
  cmsLogger.debug('[Document Editor]', '✅ Document published successfully');
737
782
  cmsLogger.debug('[Document Editor]', '📄 New published hash:', response.data.publishedHash);
738
783
 
@@ -790,10 +835,27 @@
790
835
  async function unpublishDocument() {
791
836
  if (!documentId || saving) return;
792
837
 
838
+ // Surface published back-references so the user knows what will end up
839
+ // with dangling refs in the published perspective. Best-effort —
840
+ // network errors fall through to the standard confirm.
841
+ let backRefDescription =
842
+ 'It will be removed from published queries, but the data is preserved and you can re-publish anytime.';
843
+ try {
844
+ const backRefRes = await documents.getBackReferences(documentId);
845
+ if (backRefRes.success && backRefRes.data) {
846
+ const publishedBackRefs = backRefRes.data.filter((r) => r.status === 'published');
847
+ if (publishedBackRefs.length > 0) {
848
+ const count = publishedBackRefs.length;
849
+ backRefDescription = `${count} published document${count === 1 ? '' : 's'} reference${count === 1 ? 's' : ''} this one — their references will be left dangling in the published perspective until you re-publish them. Continue?`;
850
+ }
851
+ }
852
+ } catch {
853
+ // Silently fall through to the generic confirm; ref index may be unpopulated.
854
+ }
855
+
793
856
  const confirmUnpublish = await confirmDialog({
794
857
  title: 'Unpublish this document?',
795
- description:
796
- 'It will be removed from published queries, but the data is preserved and you can re-publish anytime.',
858
+ description: backRefDescription,
797
859
  confirmText: 'Unpublish',
798
860
  variant: 'destructive'
799
861
  });
@@ -809,6 +871,7 @@
809
871
  ...fullDocument,
810
872
  _meta: { ...fullDocument?._meta, status: 'unpublished' }
811
873
  };
874
+ notifyDocumentChanged(documentId);
812
875
  toast.success('Document unpublished — you can re-publish anytime');
813
876
  if (onUnpublished && documentId) {
814
877
  onUnpublished(documentId);
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentEditor.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAaxD,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,CAAC,EAAE;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IACT,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAm+CF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"DocumentEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentEditor.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAexD,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,CAAC,EAAE;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IACT,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAkiDF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import * as AlertDialog from '@aphexcms/ui/shadcn/alert-dialog';
3
- import { Button } from '@aphexcms/ui/shadcn/button';
3
+ import { buttonVariants } from '@aphexcms/ui/shadcn/button';
4
4
  import { confirmDialogState, resolveConfirmDialog } from './confirm-dialog.svelte';
5
5
 
6
6
  function handleOpenChange(open: boolean) {
@@ -21,15 +21,17 @@
21
21
  {/if}
22
22
  </AlertDialog.Header>
23
23
  <AlertDialog.Footer>
24
- <Button variant="outline" onclick={() => resolveConfirmDialog(false)}>
24
+ <AlertDialog.Cancel onclick={() => resolveConfirmDialog(false)}>
25
25
  {confirmDialogState.cancelText}
26
- </Button>
27
- <Button
28
- variant={confirmDialogState.variant === 'destructive' ? 'destructive' : 'default'}
26
+ </AlertDialog.Cancel>
27
+ <AlertDialog.Action
28
+ class={confirmDialogState.variant === 'destructive'
29
+ ? buttonVariants({ variant: 'destructive' })
30
+ : undefined}
29
31
  onclick={() => resolveConfirmDialog(true)}
30
32
  >
31
33
  {confirmDialogState.confirmText}
32
- </Button>
34
+ </AlertDialog.Action>
33
35
  </AlertDialog.Footer>
34
36
  </AlertDialog.Content>
35
37
  </AlertDialog.Root>
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmDialogHost.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/confirm-dialog/ConfirmDialogHost.svelte.ts"],"names":[],"mappings":"AA0CA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,iBAAiB;;kBAA+E,CAAC;AACrF,KAAK,iBAAiB,GAAG,YAAY,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAClE,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"ConfirmDialogHost.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/confirm-dialog/ConfirmDialogHost.svelte.ts"],"names":[],"mappings":"AA4CA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,iBAAiB;;kBAA+E,CAAC;AACrF,KAAK,iBAAiB,GAAG,YAAY,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAClE,eAAe,iBAAiB,CAAC"}
@@ -10,6 +10,7 @@
10
10
  import { getSchemaContext } from '../../../schema-context.svelte';
11
11
  import ObjectModal from '../ObjectModal.svelte';
12
12
  import ImageField from './ImageField.svelte';
13
+ import ReferenceField from './ReferenceField.svelte';
13
14
  import { getDefaultValueForFieldType } from '../../../utils/field-defaults';
14
15
  import { DragDropProvider } from '@dnd-kit/svelte';
15
16
  import { createSortable, isSortable } from '@dnd-kit/svelte/sortable';
@@ -29,6 +30,9 @@
29
30
  import { toast } from 'svelte-sonner';
30
31
  import AssetBrowserModal from '../AssetBrowserModal.svelte';
31
32
  import { resolvePreviewTitle, resolvePreviewSubtitle } from '../../../utils/preview';
33
+ import { documents } from '../../../api/documents';
34
+ import { SvelteMap } from 'svelte/reactivity';
35
+ import { getDocumentVersion } from '../../../document-refresh.svelte';
32
36
 
33
37
  interface Props {
34
38
  field: ArrayFieldType;
@@ -70,8 +74,12 @@
70
74
  }
71
75
 
72
76
  const isGridLayout = $derived(field.options?.layout === 'grid');
77
+ const isReferenceArray = $derived(
78
+ field.of && field.of.length > 0 && field.of[0]?.type === 'reference'
79
+ );
73
80
  const isPrimitiveArray = $derived(
74
- field.of &&
81
+ !isReferenceArray &&
82
+ field.of &&
75
83
  field.of.length > 0 &&
76
84
  field.of[0]?.type &&
77
85
  !field.of[0].fields &&
@@ -80,6 +88,48 @@
80
88
  const primitiveType = $derived(isPrimitiveArray ? field.of?.[0]?.type : null);
81
89
  const availableTypes = $derived(getArrayTypes(schemas, field));
82
90
 
91
+ // Synthetic ReferenceField definition reused for every row in an
92
+ // array-of-references. We forward the array's first `of[]` entry's `to` so
93
+ // the row picker honors the schema-author's allowed target types.
94
+ const referenceFieldShape = $derived(
95
+ isReferenceArray
96
+ ? {
97
+ name: field.name,
98
+ type: 'reference' as const,
99
+ title: field.title,
100
+ to: (field.of?.[0] as any)?.to ?? []
101
+ }
102
+ : null
103
+ );
104
+
105
+ // Batched hydration cache for reference rows. Without this, each row's
106
+ // ReferenceField fires its own getById on mount → N round-trips for an
107
+ // array of N refs. Instead we fetch all populated refs in a single call
108
+ // and hand each row its preloaded doc. The cache invalidates whenever
109
+ // any row's referenced doc bumps its document-refresh version.
110
+ const referenceCache = new SvelteMap<string, any>();
111
+ $effect(() => {
112
+ if (!isReferenceArray) return;
113
+ const ids = (Array.isArray(value) ? value : [])
114
+ .map((item: any) => item?._ref)
115
+ .filter((id: any): id is string => typeof id === 'string' && id.length > 0);
116
+ if (ids.length === 0) return;
117
+ // Subscribe to each id's version so external saves trigger a refetch.
118
+ for (const id of ids) getDocumentVersion(id);
119
+ (async () => {
120
+ try {
121
+ const res = await documents.getMany(ids);
122
+ if (res.success && res.data) {
123
+ for (const doc of res.data as any[]) {
124
+ if (doc?.id) referenceCache.set(doc.id, doc);
125
+ }
126
+ }
127
+ } catch {
128
+ // Per-row ReferenceField will fall back to its own fetch on miss.
129
+ }
130
+ })();
131
+ });
132
+
83
133
  // Modal state
84
134
  let modalOpen = $state(false);
85
135
  let editingIndex = $state<number | null>(null);
@@ -376,6 +426,29 @@
376
426
  onUpdate(newArray);
377
427
  }
378
428
 
429
+ // Reference array — append an empty reference row. The row renders the
430
+ // ReferenceField empty state (search picker), so the user picks the target
431
+ // document inline without an extra modal step.
432
+ function handleAddReference() {
433
+ if (readonly) return;
434
+ const newItem = { _type: 'reference', _key: generateKey(), _ref: null };
435
+ onUpdate([...keyedItems, newItem]);
436
+ }
437
+
438
+ function handleUpdateReference(
439
+ index: number,
440
+ newRef: { _type: 'reference'; _ref: string; _key?: string } | null
441
+ ) {
442
+ if (readonly) return;
443
+ const newArray = [...keyedItems];
444
+ // Preserve the row's _key (stable across drag/drop) and only swap the _ref.
445
+ newArray[index] = {
446
+ ...newArray[index],
447
+ _ref: newRef?._ref ?? null
448
+ };
449
+ onUpdate(newArray);
450
+ }
451
+
379
452
  // Write-through: propagate every field change from the modal to the array immediately
380
453
  function handleModalUpdate(updatedData: Record<string, any>) {
381
454
  if (editingIndex === null || !editingType) return;
@@ -421,7 +494,81 @@
421
494
  }
422
495
  </script>
423
496
 
424
- {#if isPrimitiveArray}
497
+ {#if isReferenceArray && referenceFieldShape}
498
+ <!-- Reference array — each row reuses ReferenceField. Drag handle on the
499
+ left, ReferenceField fills the row, an X button on empty rows lets the
500
+ user discard a never-picked row (populated rows use ReferenceField's
501
+ own context menu, which we override via onRemove to drop the row). -->
502
+ {#if keyedItems.length > 0}
503
+ {#key dndKey}
504
+ <DragDropProvider onDragEnd={handleDragEnd}>
505
+ <div class="space-y-1">
506
+ {#each keyedItems as item, index (item._key)}
507
+ {@const sortable = createSortable({ id: item._key, index, disabled: readonly })}
508
+ <div
509
+ {@attach sortable.attach}
510
+ class="flex items-center gap-1"
511
+ class:opacity-50={sortable.isDragging}
512
+ >
513
+ {#if !readonly}
514
+ <button
515
+ {@attach sortable.attachHandle}
516
+ class="text-muted-foreground hover:text-foreground flex h-9 w-7 shrink-0 cursor-grab items-center justify-center active:cursor-grabbing"
517
+ >
518
+ <GripVertical class="h-4 w-4" />
519
+ </button>
520
+ {/if}
521
+ <div class="min-w-0 flex-1">
522
+ <ReferenceField
523
+ field={referenceFieldShape}
524
+ value={item._ref ? { _type: 'reference', _ref: item._ref } : null}
525
+ onUpdate={(newRef) => handleUpdateReference(index, newRef)}
526
+ {onOpenReference}
527
+ {readonly}
528
+ onRemove={() => handleRemoveItem(index)}
529
+ preloadedDoc={item._ref ? referenceCache.get(item._ref) : undefined}
530
+ />
531
+ </div>
532
+ {#if !readonly && !item._ref}
533
+ <DropdownMenu.Root>
534
+ <DropdownMenu.Trigger>
535
+ {#snippet child({ props })}
536
+ <button
537
+ {...props}
538
+ class="text-muted-foreground hover:text-foreground flex h-9 w-7 shrink-0 items-center justify-center rounded transition-colors"
539
+ aria-label="Row options"
540
+ >
541
+ <Ellipsis class="h-4 w-4" />
542
+ </button>
543
+ {/snippet}
544
+ </DropdownMenu.Trigger>
545
+ <DropdownMenu.Content align="end">
546
+ <DropdownMenu.Item
547
+ class="text-destructive focus:text-destructive"
548
+ onclick={() => handleRemoveItem(index)}
549
+ >
550
+ <Trash2 class="mr-2 h-4 w-4" />
551
+ Remove
552
+ </DropdownMenu.Item>
553
+ </DropdownMenu.Content>
554
+ </DropdownMenu.Root>
555
+ {/if}
556
+ </div>
557
+ {/each}
558
+ </div>
559
+ </DragDropProvider>
560
+ {/key}
561
+ {/if}
562
+ {#if !readonly}
563
+ <button
564
+ class="border-border/50 text-muted-foreground hover:text-foreground hover:bg-muted/50 mt-1 flex h-10 w-full items-center justify-center gap-2 rounded border border-dashed text-sm transition-colors"
565
+ onclick={handleAddReference}
566
+ >
567
+ <Plus class="h-4 w-4" />
568
+ Add reference...
569
+ </button>
570
+ {/if}
571
+ {:else if isPrimitiveArray}
425
572
  <!-- Primitive array UI with DnD -->
426
573
  {#if arrayValue.length === 0}
427
574
  <div
@@ -1 +1 @@
1
- {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AA0BtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA8xBF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AA8BtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAq5BF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}