pi-outpost 0.32.4 → 0.34.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 (188) hide show
  1. package/dist/contract/README.md +120 -1
  2. package/dist/contract/conformance/README.md +29 -5
  3. package/dist/contract/conformance/index.json +115 -0
  4. package/dist/contract/conformance/invalid/unknown-version.json +1 -1
  5. package/dist/contract/conformance/invalid/v3-timeline-comparison-without-reference.json +123 -0
  6. package/dist/contract/conformance/invalid/v3-timeline-contradictory-change.json +128 -0
  7. package/dist/contract/conformance/invalid/v3-timeline-dependency-cycle.json +124 -0
  8. package/dist/contract/conformance/invalid/v3-timeline-dependency-on-removed.json +125 -0
  9. package/dist/contract/conformance/invalid/v3-timeline-duplicate-dependency.json +125 -0
  10. package/dist/contract/conformance/invalid/v3-timeline-duplicate-identifier.json +120 -0
  11. package/dist/contract/conformance/invalid/v3-timeline-empty-task-endpoint.json +124 -0
  12. package/dist/contract/conformance/invalid/v3-timeline-impossible-date.json +101 -0
  13. package/dist/contract/conformance/invalid/v3-timeline-inverted-activity.json +120 -0
  14. package/dist/contract/conformance/invalid/v3-timeline-inverted-range.json +120 -0
  15. package/dist/contract/conformance/invalid/v3-timeline-milestone-outside-range.json +120 -0
  16. package/dist/contract/conformance/invalid/v3-timeline-period-outside-range.json +139 -0
  17. package/dist/contract/conformance/invalid/v3-timeline-presentation-property.json +121 -0
  18. package/dist/contract/conformance/invalid/v3-timeline-previous-outside-range.json +128 -0
  19. package/dist/contract/conformance/invalid/v3-timeline-previous-wrong-shape.json +128 -0
  20. package/dist/contract/conformance/invalid/v3-timeline-reference-outside-range.json +140 -0
  21. package/dist/contract/conformance/invalid/v3-timeline-self-dependency.json +124 -0
  22. package/dist/contract/conformance/invalid/v3-timeline-under-version-2.json +120 -0
  23. package/dist/contract/conformance/invalid/v3-timeline-unknown-dependency-type.json +121 -0
  24. package/dist/contract/conformance/invalid/v3-timeline-unresolved-dependency-endpoint.json +120 -0
  25. package/dist/contract/conformance/invalid/v3-timeline-unsupported-scale.json +120 -0
  26. package/dist/contract/conformance/invalid/v3-timeline-with-target.json +123 -0
  27. package/dist/contract/conformance/valid/v3-timeline-calendar.json +140 -0
  28. package/dist/contract/conformance/valid/v3-timeline-compared.json +142 -0
  29. package/dist/contract/conformance/valid/v3-timeline-empty-task-and-anonymous-separator.json +35 -0
  30. package/dist/contract/conformance/valid/v3-timeline-four-dependency-types.json +61 -0
  31. package/dist/contract/conformance/valid/v3-timeline-programme.json +44 -0
  32. package/dist/contract/conformance/valid/v3-timeline-quarter-scale.json +42 -0
  33. package/dist/contract/conformance/valid/v3-timeline-single-day-activity.json +27 -0
  34. package/dist/contract/conformance/valid/v3-timeline-unsatisfied-dependency.json +45 -0
  35. package/dist/contract/conformance/valid/v3-timeline-week-scale.json +35 -0
  36. package/dist/contract/conformance/version-1.lock.json +2 -2
  37. package/dist/contract/schemas/structured-exchange-3.json +1263 -0
  38. package/dist/contract/schemas/structured-exchange-profile-registry-2.json +94 -0
  39. package/dist/contract/structured-exchange-project-setup.md +36 -0
  40. package/dist/contract/validate-structured-exchange.mjs +1981 -174
  41. package/dist/pi-outpost-tools.mjs +4556 -1130
  42. package/dist/pi-outpost.mjs +5733 -1898
  43. package/dist/pi-outpost.sea.mjs +2 -2
  44. package/dist/sea-prep.blob +2 -2
  45. package/dist/skills/structured-exchange/SKILL.md +90 -378
  46. package/dist/skills/structured-exchange/references/enriched-contract.md +158 -0
  47. package/dist/skills/structured-exchange/references/figures.md +52 -0
  48. package/dist/skills/structured-exchange/references/graphs-and-tables.md +115 -0
  49. package/dist/skills/structured-exchange/references/proposals.md +86 -0
  50. package/dist/skills/structured-exchange/references/timelines.md +87 -0
  51. package/dist/skills/structured-exchange/structured-exchange-3.json +1263 -0
  52. package/dist/skills/structured-exchange/structured-exchange-profile-registry-2.json +94 -0
  53. package/dist/skills/structured-exchange-project/SKILL.md +22 -0
  54. package/dist/web/assets/{PdfViewer-DHIy06i5.js → PdfViewer-9kadnb9W.js} +2 -2
  55. package/dist/web/assets/{abnfDiagram-VCTEODGH-0IqI620v.js → abnfDiagram-YUKMZFIV-BUZ7jF1I.js} +1 -1
  56. package/dist/web/assets/architecture-WOLXFQ4H-CxBmyj2Z.js +1 -0
  57. package/dist/web/assets/architectureDiagram-47P4ROYG-BAvert-J.js +36 -0
  58. package/dist/web/assets/{blockDiagram-OSKFZWR5-sw0nzjYC.js → blockDiagram-E7TT5TSB-CXQT_J2x.js} +16 -5
  59. package/dist/web/assets/c4Diagram-CRA5TL53-Byqo5aDs.js +38 -0
  60. package/dist/web/assets/channel-D3nE3SyW.js +1 -0
  61. package/dist/web/assets/chunk-2BW5OAIV-D9dSVCv2.js +10 -0
  62. package/dist/web/assets/chunk-3YJQHVM4-D6IQcIQB.js +2 -0
  63. package/dist/web/assets/chunk-4EA7E6EY-BxZ_5HMn.js +1 -0
  64. package/dist/web/assets/{chunk-F27PBJKO-C6rqX2gT.js → chunk-7TKQ45FW-G9TsFldk.js} +1 -1
  65. package/dist/web/assets/{chunk-SVP7TREG-CYXVRfc0.js → chunk-AW2ZBBNX-nmg5rsd5.js} +3 -3
  66. package/dist/web/assets/{chunk-GVQU2GXP-BtSmWzHA.js → chunk-DBDB3WZW-BwVIGLtu.js} +1 -1
  67. package/dist/web/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
  68. package/dist/web/assets/chunk-E2ZNV5FY-VD95O25Z.js +72 -0
  69. package/dist/web/assets/chunk-EU5HNXII-CSnybkPb.js +213 -0
  70. package/dist/web/assets/chunk-FVRAUYC3-TBqluQ8N.js +2 -0
  71. package/dist/web/assets/{chunk-PWAF6VOD-Dup_NAkl.js → chunk-HJ2JQQFS-BiLMpVsO.js} +1 -1
  72. package/dist/web/assets/chunk-HTAEGDNF-QDOAuhR5.js +1 -0
  73. package/dist/web/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
  74. package/dist/web/assets/{chunk-POPQ4Y6H-BrnPOTT-.js → chunk-KQW6MTUR-_WxFaX97.js} +1 -1
  75. package/dist/web/assets/chunk-NGNAAXSQ-BW1P8EP7.js +136 -0
  76. package/dist/web/assets/chunk-NTY3LDVX-BI0Pj8F6.js +1 -0
  77. package/dist/web/assets/chunk-VPRB5NB3-BBDlNNll.js +129 -0
  78. package/dist/web/assets/chunk-XC4XBNZT-Ba1ra4Jd.js +62 -0
  79. package/dist/web/assets/classDiagram-v2-K4WV4PDN-CksBjG_q.js +217 -0
  80. package/dist/web/assets/{conversationExport-CfUybHMU.js → conversationExport-q-9NUHuj.js} +1 -1
  81. package/dist/web/assets/cynefin-EF2NZ3EQ-ucJDz_S5.js +1 -0
  82. package/dist/web/assets/{cynefinDiagram-5FMLGOSQ-BKvXEDID.js → cynefinDiagram-3GCD6N5R-qPxcNdaa.js} +1 -1
  83. package/dist/web/assets/dagre-W4DXFKR2-ChflYJGj.js +4 -0
  84. package/dist/web/assets/diagram-2UJZ2QOL-Bi9ngdu2.js +200 -0
  85. package/dist/web/assets/{diagram-VX7I27RA-D5POkb-9.js → diagram-OVF4WLC6-iSSbF5Cd.js} +10 -10
  86. package/dist/web/assets/{diagram-S7CK7UJ4-DTvEAe9r.js → diagram-PFPMY2P6-BeEOD8rR.js} +2 -2
  87. package/dist/web/assets/{diagram-Z3DM3KII-CC2Mxm4H.js → diagram-UMYRVEAY-BckO6iZI.js} +1 -1
  88. package/dist/web/assets/{diagram-UQ7AKVKN-CVGEkml-.js → diagram-YEKJPTXX-BzzDIAmO.js} +2 -2
  89. package/dist/web/assets/diagram-ZIFT7M5P-ByjWOPbN.js +3 -0
  90. package/dist/web/assets/{docxExport-ErgwApAZ.js → docxExport-BO7j3Xnv.js} +1 -1
  91. package/dist/web/assets/{ebnfDiagram-PWID7BFC-CbDy2vxX.js → ebnfDiagram-VR2GEFS7-DRp-A5JC.js} +1 -1
  92. package/dist/web/assets/elk-IJKZMXRS-QzeiQ3l1.js +27 -0
  93. package/dist/web/assets/{erDiagram-2YWLMYGG-DUdStx-7.js → erDiagram-O2IAPWRE-BewZvI56.js} +15 -15
  94. package/dist/web/assets/eventmodeling-K75KTNOO-CX7X0bsf.js +1 -0
  95. package/dist/web/assets/flowDiagram-OXPTDLAJ-CSIHFoeR.js +1 -0
  96. package/dist/web/assets/ganttDiagram-R7TSEDQI-BT8Lcdwc.js +292 -0
  97. package/dist/web/assets/gitGraph-VSP46ZUC-BxYbaxeg.js +1 -0
  98. package/dist/web/assets/gitGraphDiagram-XJZIOB7I-B0fqVCqP.js +106 -0
  99. package/dist/web/assets/index-DcqvxkHN.css +2 -0
  100. package/dist/web/assets/index-eTp_lQUc.js +350 -0
  101. package/dist/web/assets/info-OHQRW6UA-DV3PDKXA.js +1 -0
  102. package/dist/web/assets/infoDiagram-5W2HQ5XZ-DfHpZ9RG.js +2 -0
  103. package/dist/web/assets/{ishikawaDiagram-5VMMS53U-D-gYpOUM.js → ishikawaDiagram-K3B6WC7H-DuwLwL9k.js} +2 -2
  104. package/dist/web/assets/{journeyDiagram-EYS64GPL-B0WqOtZ4.js → journeyDiagram-COXZFDF6-CMOfweZO.js} +4 -4
  105. package/dist/web/assets/{kanban-definition-UXKFOSKX-DIrVFtP3.js → kanban-definition-P3RFRI5V-DjHhlDbk.js} +9 -9
  106. package/dist/web/assets/{line-CuEhAU65.js → line-CGIAMPkR.js} +1 -1
  107. package/dist/web/assets/loadReferencedImage-DZ5eApE4.js +2 -0
  108. package/dist/web/assets/{mermaid-parser.core-CzCh9EE1.js → mermaid-parser.core-C18KmsD9.js} +6 -6
  109. package/dist/web/assets/mermaid.core-C9srNL3b.js +44 -0
  110. package/dist/web/assets/{mindmap-definition-THT77NOG-CBH0Z9AJ.js → mindmap-definition-LZFPQGKD-C38HXkG2.js} +24 -24
  111. package/dist/web/assets/packet-JDAUHWVQ-Br-8ADK6.js +1 -0
  112. package/dist/web/assets/{pdf-6mG9havR.js → pdf-C3f6Vjz7.js} +1 -1
  113. package/dist/web/assets/{pegDiagram-XKGWAZYB-BNpTWHo8.js → pegDiagram-BGZESJAR-Be49dM_b.js} +1 -1
  114. package/dist/web/assets/pie-XZMESJXO-68ykK6i1.js +1 -0
  115. package/dist/web/assets/{pieDiagram-E7YTZNPT-CZ34Q1W-.js → pieDiagram-CAPJLFHJ-DVOWjXQ_.js} +2 -2
  116. package/dist/web/assets/{quadrantDiagram-AXDQQJYC-C5aFQsIe.js → quadrantDiagram-GDMTTRAM-8la_R53U.js} +3 -3
  117. package/dist/web/assets/radar-ABXABTNO-K00ZGu13.js +1 -0
  118. package/dist/web/assets/railroad-I3PHUGI6-W7QuDysT.js +1 -0
  119. package/dist/web/assets/railroad-abnf-LNEFI6M7-DR6CNkaK.js +1 -0
  120. package/dist/web/assets/railroad-ebnf-DWYD2UWJ-dAhaGv5L.js +1 -0
  121. package/dist/web/assets/railroad-peg-JR7BVNR7-DwoiiDdE.js +1 -0
  122. package/dist/web/assets/{railroadDiagram-O6MQD6OU-B0ctRtRT.js → railroadDiagram-SM67HX2B-BuSjCs02.js} +1 -1
  123. package/dist/web/assets/{requirementDiagram-IS5BZ75X-BuOtNPGm.js → requirementDiagram-X7JNWC4A-3uq8m_5E.js} +11 -11
  124. package/dist/web/assets/{sankeyDiagram-P5KCCOFB-CybICPpQ.js → sankeyDiagram-UM26HJRW-BB9_G8bN.js} +3 -3
  125. package/dist/web/assets/sequenceDiagram-ZO4K6R2Y-CGehVq9k.js +169 -0
  126. package/dist/web/assets/stateDiagram-v2-TBUQTH76-CwMywh5s.js +291 -0
  127. package/dist/web/assets/swimlanes-N4OXWK64-CqjihurI.js +1 -0
  128. package/dist/web/assets/swimlanesDiagram-K3J5GTZL-Blk5ohUI.js +8 -0
  129. package/dist/web/assets/timeline-definition-YOQAKGHF-BtKnuY5Y.js +120 -0
  130. package/dist/web/assets/treeView-D4JQ5SDB-Dy4jC4wa.js +1 -0
  131. package/dist/web/assets/treemap-SAJKECNS-CHtGJD8P.js +1 -0
  132. package/dist/web/assets/usecaseDiagram-VIAY4XPW-BjnstWbR.js +328 -0
  133. package/dist/web/assets/vennDiagram-BLWOH2XV-o_EtJWVA.js +34 -0
  134. package/dist/web/assets/wardley-7MLQ67FV-BTheL3FW.js +1 -0
  135. package/dist/web/assets/{wardleyDiagram-VM6X3IG4-CzB2tR_t.js → wardleyDiagram-YMQ3BMBF-Bp_sv2v4.js} +3 -3
  136. package/dist/web/assets/xychartDiagram-TAQBALBS-C0IpEfij.js +7 -0
  137. package/dist/web/index.html +2 -2
  138. package/package.json +3 -3
  139. package/dist/web/assets/architecture-7GRP2DOG-BqTZwqCY.js +0 -1
  140. package/dist/web/assets/architectureDiagram-5GKGNRK7-BEGSAGSn.js +0 -36
  141. package/dist/web/assets/c4Diagram-7LVT6UL2-C6wQYxBC.js +0 -38
  142. package/dist/web/assets/channel-CbMG5PZq.js +0 -1
  143. package/dist/web/assets/chunk-3NF5O7KM-Dni31-nE.js +0 -168
  144. package/dist/web/assets/chunk-4HAMMTFA-hiqoLVtf.js +0 -62
  145. package/dist/web/assets/chunk-75Z2AOVW-1k7HRGrm.js +0 -2
  146. package/dist/web/assets/chunk-DU6HZSFF-Dy05V--k.js +0 -127
  147. package/dist/web/assets/chunk-FOHPRMQF-DHwB1DNv.js +0 -161
  148. package/dist/web/assets/chunk-GMAD6QVW-Bi5Ne8OE.js +0 -72
  149. package/dist/web/assets/chunk-HLEWEB6X-yamDqy1M.js +0 -206
  150. package/dist/web/assets/chunk-L3NEJ4N5-CQSamtuJ.js +0 -1
  151. package/dist/web/assets/chunk-OSK3NFVY-S95igUko.js +0 -10
  152. package/dist/web/assets/chunk-P2QGCYS3-BPor-azX.js +0 -1
  153. package/dist/web/assets/chunk-ZLD2IHE6-D0uZMMWb.js +0 -231
  154. package/dist/web/assets/classDiagram-CYGNFDIV-2Yl201cv.js +0 -1
  155. package/dist/web/assets/classDiagram-v2-TLXNO2FR-2Yl201cv.js +0 -1
  156. package/dist/web/assets/cynefin-OW5HDTMX-ua6bZIWp.js +0 -1
  157. package/dist/web/assets/dagre-OS7QT2EB-BRxaZdm2.js +0 -4
  158. package/dist/web/assets/dagre-PrKaheQc.js +0 -1
  159. package/dist/web/assets/diagram-VSXAHHWV-Dkh-RvzE.js +0 -3
  160. package/dist/web/assets/eventmodeling-NTZA5JFV-Bw8wULv_.js +0 -1
  161. package/dist/web/assets/flowDiagram-T62WH6J4-Dp4v9IDN.js +0 -1
  162. package/dist/web/assets/ganttDiagram-EL5Y4UJY-6dI1H52q.js +0 -292
  163. package/dist/web/assets/gitGraph-4MIJSDKK-UXnB0IFr.js +0 -1
  164. package/dist/web/assets/gitGraphDiagram-WWUBYQGX-B_wXPpx0.js +0 -106
  165. package/dist/web/assets/index-9y9n2Dul.css +0 -2
  166. package/dist/web/assets/index-COu8CnNa.js +0 -350
  167. package/dist/web/assets/info-A6RAGUB7-C2WWyNzF.js +0 -1
  168. package/dist/web/assets/infoDiagram-FKFFQAWI-DY65_ndC.js +0 -2
  169. package/dist/web/assets/loadReferencedImage-DgHMXQNZ.js +0 -2
  170. package/dist/web/assets/mermaid.core-BmGW_v8T.js +0 -44
  171. package/dist/web/assets/packet-AYTQ26CC-BYG_KViO.js +0 -1
  172. package/dist/web/assets/pie-WAS4IAKB-wBoUHr8R.js +0 -1
  173. package/dist/web/assets/radar-RG4KPBEZ-D4K5XP7X.js +0 -1
  174. package/dist/web/assets/railroad-74A4TZTK-Haum25Us.js +0 -1
  175. package/dist/web/assets/railroad-abnf-HS5TGJTU-D-kbOsoZ.js +0 -1
  176. package/dist/web/assets/railroad-ebnf-LZEXJU2U-CWtixpZf.js +0 -1
  177. package/dist/web/assets/railroad-peg-WCYAUIDC-BeY7arxf.js +0 -1
  178. package/dist/web/assets/sequenceDiagram-WJ2MYXX4-Dvo8sl8e.js +0 -162
  179. package/dist/web/assets/stateDiagram-XQSTLZYL-BcM-hGJa.js +0 -1
  180. package/dist/web/assets/stateDiagram-v2-IH3M54BS-hYgwFLzv.js +0 -1
  181. package/dist/web/assets/swimlanes-V6O3JKXN-mOOjKnlm.js +0 -1
  182. package/dist/web/assets/swimlanesDiagram-JKAHXJPX-C_yinM3f.js +0 -8
  183. package/dist/web/assets/timeline-definition-24CTP7MA-DuwMztTy.js +0 -120
  184. package/dist/web/assets/treeView-Q6P3EWNA-BT4vRttT.js +0 -1
  185. package/dist/web/assets/treemap-WGGIJYW6-Df6GHCE2.js +0 -1
  186. package/dist/web/assets/vennDiagram-4TSXK5OY-BbcbGn7o.js +0 -34
  187. package/dist/web/assets/wardley-WFR3VGLG-L_G3ydHA.js +0 -1
  188. package/dist/web/assets/xychartDiagram-S5SC5T6Z-CKHwJboP.js +0 -7
@@ -13,7 +13,10 @@ import path2 from "node:path";
13
13
  var STRUCTURED_EXCHANGE_SCHEMA_V1 = "urn:structured-exchange:1";
14
14
  var PROPOSABLE_KINDS = ["graph", "sequence"];
15
15
  function proposableKinds(schema2) {
16
- return schema2 === STRUCTURED_EXCHANGE_SCHEMA_V2 ? ["graph", "sequence", "table"] : PROPOSABLE_KINDS;
16
+ return isEnrichedSchema(schema2) ? ["graph", "sequence", "table"] : PROPOSABLE_KINDS;
17
+ }
18
+ function isEnrichedSchema(schema2) {
19
+ return schema2 === STRUCTURED_EXCHANGE_SCHEMA_V2 || schema2 === STRUCTURED_EXCHANGE_SCHEMA_V3;
17
20
  }
18
21
  function readTableRow(row) {
19
22
  if (Array.isArray(row)) return { cells: row };
@@ -95,9 +98,11 @@ var STRUCTURED_EXCHANGE_CEILINGS_2 = {
95
98
  viewpointConcern: 500
96
99
  };
97
100
  var STRUCTURED_EXCHANGE_BYTES_CEILING_2 = 8e6;
101
+ var STRUCTURED_EXCHANGE_SCHEMA_V3 = "urn:structured-exchange:3";
98
102
  var STRUCTURED_EXCHANGE_SUPPORTED_SCHEMAS = [
99
103
  STRUCTURED_EXCHANGE_SCHEMA_V1,
100
- STRUCTURED_EXCHANGE_SCHEMA_V2
104
+ STRUCTURED_EXCHANGE_SCHEMA_V2,
105
+ STRUCTURED_EXCHANGE_SCHEMA_V3
101
106
  ];
102
107
  function supportedSchemaOf(declared) {
103
108
  return STRUCTURED_EXCHANGE_SUPPORTED_SCHEMAS.find((supported2) => supported2 === declared);
@@ -110,7 +115,7 @@ var STRUCTURED_EXCHANGE_BYTES_CEILING_ANY = Math.max(
110
115
  STRUCTURED_EXCHANGE_BYTES_CEILING_2
111
116
  );
112
117
  function bytesCeilingFor(declared) {
113
- return declared === STRUCTURED_EXCHANGE_SCHEMA_V2 ? STRUCTURED_EXCHANGE_BYTES_CEILING_2 : STRUCTURED_EXCHANGE_BYTES_CEILING;
118
+ return isEnrichedSchema(declared) ? STRUCTURED_EXCHANGE_BYTES_CEILING_2 : STRUCTURED_EXCHANGE_BYTES_CEILING;
114
119
  }
115
120
  var utf8Bytes = (value) => new TextEncoder().encode(value).length;
116
121
  function ceilingFor(bound) {
@@ -174,11 +179,339 @@ function measureAccepted(envelope, serialized) {
174
179
  return { kind: envelope.kind, bytes: utf8Bytes(serialized), counts };
175
180
  }
176
181
 
182
+ // shared/src/structuredExchangeTimeline.ts
183
+ var MS_PER_DAY = 864e5;
184
+ var CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
185
+ function dayNumber(date) {
186
+ const match = CALENDAR_DATE.exec(date);
187
+ if (match === null) return void 0;
188
+ const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])];
189
+ const instant = new Date(Date.UTC(year, month - 1, day));
190
+ instant.setUTCFullYear(year, month - 1, day);
191
+ if (instant.getUTCFullYear() !== year || instant.getUTCMonth() !== month - 1 || instant.getUTCDate() !== day) {
192
+ return void 0;
193
+ }
194
+ return Math.round(instant.getTime() / MS_PER_DAY);
195
+ }
196
+ function itemDays(item) {
197
+ const start = dayNumber(item.type === "activity" ? item.start : item.date);
198
+ const end = dayNumber(item.type === "activity" ? item.end : item.date);
199
+ return start === void 0 || end === void 0 ? void 0 : { start, end };
200
+ }
201
+ function timelineEndpoints(data) {
202
+ const endpoints = /* @__PURE__ */ new Map();
203
+ data.rows.forEach((row, rowIndex) => {
204
+ if (row.type !== "task") return;
205
+ if (!endpoints.has(row.id)) endpoints.set(row.id, { id: row.id, type: "task", row: rowIndex, days: taskDays(row) });
206
+ row.items.forEach((item, itemIndex) => {
207
+ if (item.id === void 0 || endpoints.has(item.id)) return;
208
+ endpoints.set(item.id, { id: item.id, type: item.type, row: rowIndex, item: itemIndex, days: itemDays(item) });
209
+ });
210
+ });
211
+ return endpoints;
212
+ }
213
+ function taskDays(task) {
214
+ let span;
215
+ for (const item of task.items) {
216
+ const days = itemDays(item);
217
+ if (days === void 0) continue;
218
+ span = span === void 0 ? { ...days } : { start: Math.min(span.start, days.start), end: Math.max(span.end, days.end) };
219
+ }
220
+ return span;
221
+ }
222
+ function dependencyType(dependency) {
223
+ return dependency.type ?? "finish-to-start";
224
+ }
225
+ var TIMELINE_TODAY_BAND = 18;
226
+ var TIMELINE_YEAR_BAND = 20;
227
+ var TIMELINE_MONTH_BAND = 20;
228
+ var TIMELINE_HEADER_HEIGHT = TIMELINE_TODAY_BAND + TIMELINE_YEAR_BAND + TIMELINE_MONTH_BAND;
229
+
230
+ // shared/src/structuredExchangeTimelineValidation.ts
231
+ function timelineIssues(data) {
232
+ const issues = [];
233
+ const day = (value, at) => {
234
+ const found = dayNumber(value);
235
+ if (found === void 0) {
236
+ issues.push({ rule: "invalid-date", path: at, message: `"${value}" is not a day of the calendar` });
237
+ }
238
+ return found;
239
+ };
240
+ const rangeStart = day(data.time.start, "/data/time/start");
241
+ const rangeEnd = day(data.time.end, "/data/time/end");
242
+ const range = rangeStart !== void 0 && rangeEnd !== void 0 && rangeStart <= rangeEnd ? { start: rangeStart, end: rangeEnd } : void 0;
243
+ if (rangeStart !== void 0 && rangeEnd !== void 0 && rangeStart > rangeEnd) {
244
+ issues.push({
245
+ rule: "inverted-range",
246
+ path: "/data/time",
247
+ message: `the timeline starts on ${data.time.start}, after it ends on ${data.time.end}`
248
+ });
249
+ }
250
+ const declaredRange = `${data.time.start} to ${data.time.end}`;
251
+ const seen = /* @__PURE__ */ new Map();
252
+ const identify = (id, at) => {
253
+ if (id === void 0) return;
254
+ const first = seen.get(id);
255
+ if (first === void 0) seen.set(id, at);
256
+ else {
257
+ issues.push({
258
+ rule: "duplicate-identifier",
259
+ path: `${at}/id`,
260
+ message: `identifier "${id}" is already declared at ${first}`
261
+ });
262
+ }
263
+ };
264
+ const compared = data.comparedTo !== void 0;
265
+ if (data.comparedTo?.date !== void 0) day(data.comparedTo.date, "/data/comparedTo/date");
266
+ const withoutReference = (at) => issues.push({
267
+ rule: "comparison-without-reference",
268
+ path: at,
269
+ message: "previous dates and change roles describe a comparison; declare `comparedTo` naming the plan they compare with"
270
+ });
271
+ const kinds = /* @__PURE__ */ new Set();
272
+ data.rows.forEach((row, rowIndex) => {
273
+ if (row.type !== "task") return;
274
+ const rowAt = `/data/rows/${rowIndex}`;
275
+ identify(row.id, rowAt);
276
+ if (row.role !== void 0 && !compared) withoutReference(`${rowAt}/role`);
277
+ row.items.forEach((item, itemIndex) => {
278
+ const at = `${rowAt}/items/${itemIndex}`;
279
+ identify(item.id, at);
280
+ if (item.kind !== void 0) kinds.add(item.kind);
281
+ if (item.role !== void 0 && !compared) withoutReference(`${at}/role`);
282
+ if (item.previous !== void 0) {
283
+ if (!compared) withoutReference(`${at}/previous`);
284
+ if (item.role !== void 0 || row.role !== void 0) {
285
+ issues.push({
286
+ rule: "contradictory-change",
287
+ path: `${at}/previous`,
288
+ message: `an item that is ${item.role ?? `in a task that is ${row.role}`} has no previous dates; it is new or dropped, not moved`
289
+ });
290
+ }
291
+ const previous = item.previous;
292
+ const before = item.type === "activity" ? { first: day(previous.start, `${at}/previous/start`), last: day(previous.end, `${at}/previous/end`) } : (() => {
293
+ const only = day(previous.date, `${at}/previous/date`);
294
+ return { first: only, last: only };
295
+ })();
296
+ if (before.first !== void 0 && before.last !== void 0) {
297
+ if (before.first > before.last) {
298
+ issues.push({ rule: "inverted-range", path: `${at}/previous`, message: `the previous dates start on ${previous.start}, after they end on ${previous.end}` });
299
+ } else if (range !== void 0 && (before.first < range.start || before.last > range.end)) {
300
+ issues.push({
301
+ rule: "item-outside-range",
302
+ path: `${at}/previous`,
303
+ message: `the previous dates fall outside the timeline's range ${declaredRange}; the range covers both plans, so widen \`time\``
304
+ });
305
+ }
306
+ }
307
+ }
308
+ if (item.role !== void 0 && row.role !== void 0 && item.role !== row.role) {
309
+ issues.push({
310
+ rule: "contradictory-change",
311
+ path: `${at}/role`,
312
+ message: `an item ${item.role} in a task that is ${row.role}`
313
+ });
314
+ }
315
+ let first;
316
+ let last;
317
+ if (item.type === "activity") {
318
+ first = day(item.start, `${at}/start`);
319
+ last = day(item.end, `${at}/end`);
320
+ if (first !== void 0 && last !== void 0 && first > last) {
321
+ issues.push({
322
+ rule: "inverted-range",
323
+ path: at,
324
+ message: `the activity starts on ${item.start}, after it ends on ${item.end}`
325
+ });
326
+ return;
327
+ }
328
+ } else {
329
+ first = last = day(item.date, `${at}/date`);
330
+ }
331
+ if (range === void 0 || first === void 0 || last === void 0) return;
332
+ if (first < range.start || last > range.end) {
333
+ issues.push({
334
+ rule: "item-outside-range",
335
+ path: at,
336
+ message: item.type === "activity" ? `the activity runs ${item.start} to ${item.end}, outside the timeline's range ${declaredRange}; widen \`time\` or move the activity` : `the milestone falls on ${item.date}, outside the timeline's range ${declaredRange}; widen \`time\` or move the milestone`
337
+ });
338
+ }
339
+ });
340
+ });
341
+ (data.periods ?? []).forEach((period, index3) => {
342
+ const at = `/data/periods/${index3}`;
343
+ if (period.kind !== void 0) kinds.add(period.kind);
344
+ const first = day(period.start, `${at}/start`);
345
+ const last = day(period.end, `${at}/end`);
346
+ if (first === void 0 || last === void 0) return;
347
+ if (first > last) {
348
+ issues.push({ rule: "inverted-range", path: at, message: `the period starts on ${period.start}, after it ends on ${period.end}` });
349
+ } else if (range !== void 0 && (last < range.start || first > range.end)) {
350
+ issues.push({
351
+ rule: "period-outside-range",
352
+ path: at,
353
+ message: `the period ${period.start} to ${period.end} lies wholly outside the timeline's range ${declaredRange}, so nothing of it could be drawn`
354
+ });
355
+ }
356
+ });
357
+ (data.references ?? []).forEach((reference, index3) => {
358
+ const at = `/data/references/${index3}`;
359
+ if (reference.kind !== void 0) kinds.add(reference.kind);
360
+ const only = day(reference.date, `${at}/date`);
361
+ if (only !== void 0 && range !== void 0 && (only < range.start || only > range.end)) {
362
+ issues.push({
363
+ rule: "item-outside-range",
364
+ path: at,
365
+ message: `the reference date ${reference.date} is outside the timeline's range ${declaredRange}; widen \`time\` or move it`
366
+ });
367
+ }
368
+ });
369
+ if (kinds.size > STRUCTURED_EXCHANGE_CEILINGS.kindsPerVocabulary) {
370
+ issues.push({
371
+ rule: "too-many-kinds",
372
+ path: "/data/rows",
373
+ message: `${kinds.size} distinct item types, and no more than ${STRUCTURED_EXCHANGE_CEILINGS.kindsPerVocabulary} can be told apart in a rendering`,
374
+ observed: kinds.size,
375
+ limit: STRUCTURED_EXCHANGE_CEILINGS.kindsPerVocabulary
376
+ });
377
+ }
378
+ issues.push(...dependencyIssues(data, timelineEndpoints(data)));
379
+ issues.push(...removedDependencyIssues(data));
380
+ return issues;
381
+ }
382
+ function removedDependencyIssues(data) {
383
+ const removed = /* @__PURE__ */ new Set();
384
+ for (const row of data.rows) {
385
+ if (row.type !== "task") continue;
386
+ if (row.role === "removed") removed.add(row.id);
387
+ for (const item of row.items) {
388
+ if (item.id !== void 0 && (item.role === "removed" || row.role === "removed")) removed.add(item.id);
389
+ }
390
+ }
391
+ const issues = [];
392
+ (data.dependencies ?? []).forEach((dependency, index3) => {
393
+ for (const side of ["from", "to"]) {
394
+ if (removed.has(dependency[side])) {
395
+ issues.push({
396
+ rule: "dependency-on-removed",
397
+ path: `/data/dependencies/${index3}/${side}`,
398
+ message: `"${dependency[side]}" was dropped from the plan, so nothing can depend on it any more; remove the dependency`
399
+ });
400
+ }
401
+ }
402
+ });
403
+ return issues;
404
+ }
405
+ function dependencyIssues(data, endpoints) {
406
+ const issues = [];
407
+ const dependencies = data.dependencies ?? [];
408
+ const usable = [];
409
+ const declared = /* @__PURE__ */ new Map();
410
+ dependencies.forEach((dependency, index3) => {
411
+ const at = `/data/dependencies/${index3}`;
412
+ let resolved = true;
413
+ for (const side of ["from", "to"]) {
414
+ const named = dependency[side];
415
+ const endpoint = endpoints.get(named);
416
+ if (endpoint === void 0) {
417
+ resolved = false;
418
+ issues.push({
419
+ rule: "unresolved-endpoint",
420
+ path: `${at}/${side}`,
421
+ message: `"${named}" is not the identifier of any task or item this timeline declares`
422
+ });
423
+ } else if (endpoint.type === "task" && endpoint.days === void 0) {
424
+ resolved = false;
425
+ const task = data.rows[endpoint.row];
426
+ if (task.type === "task" && task.items.length > 0) continue;
427
+ issues.push({
428
+ rule: "empty-task-endpoint",
429
+ path: `${at}/${side}`,
430
+ message: `task "${named}" holds no item, so it has no start or finish for a dependency to link`
431
+ });
432
+ }
433
+ }
434
+ if (!resolved) return;
435
+ const from = endpoints.get(dependency.from);
436
+ const to = endpoints.get(dependency.to);
437
+ const contains = (outer, inner) => outer.type === "task" && inner.type !== "task" && inner.row === outer.row;
438
+ if (from.id === to.id || contains(from, to) || contains(to, from)) {
439
+ issues.push({
440
+ rule: "self-dependency",
441
+ path: at,
442
+ message: from.id === to.id ? `"${from.id}" cannot depend on itself` : `a task and one of its own items cannot depend on each other: the task's span is made of its items`
443
+ });
444
+ return;
445
+ }
446
+ const key = JSON.stringify([dependency.from, dependency.to, dependencyType(dependency)]);
447
+ const first = declared.get(key);
448
+ if (first !== void 0) {
449
+ issues.push({
450
+ rule: "duplicate-dependency",
451
+ path: at,
452
+ message: `the same dependency is already declared at /data/dependencies/${first}`
453
+ });
454
+ return;
455
+ }
456
+ declared.set(key, index3);
457
+ usable.push({ from: dependency.from, to: dependency.to, index: index3 });
458
+ });
459
+ const cycle = findCycle(usable);
460
+ if (cycle !== void 0) {
461
+ issues.push({
462
+ rule: "dependency-cycle",
463
+ path: `/data/dependencies/${cycle.index}`,
464
+ message: `dependencies form a cycle, which no dates can honour: ${cycle.ids.join(" \u2192 ")}`
465
+ });
466
+ }
467
+ return issues;
468
+ }
469
+ function findCycle(edges) {
470
+ const outgoing = /* @__PURE__ */ new Map();
471
+ for (const edge of edges) {
472
+ const list = outgoing.get(edge.from) ?? [];
473
+ list.push({ to: edge.to, index: edge.index });
474
+ outgoing.set(edge.from, list);
475
+ }
476
+ const state2 = /* @__PURE__ */ new Map();
477
+ const path3 = [];
478
+ for (const root of outgoing.keys()) {
479
+ if (state2.has(root)) continue;
480
+ const stack = [{ id: root, next: 0 }];
481
+ state2.set(root, "open");
482
+ path3.push({ id: root, index: -1 });
483
+ while (stack.length > 0) {
484
+ const frame = stack[stack.length - 1];
485
+ const edgesOut = outgoing.get(frame.id) ?? [];
486
+ if (frame.next >= edgesOut.length) {
487
+ state2.set(frame.id, "done");
488
+ stack.pop();
489
+ path3.pop();
490
+ continue;
491
+ }
492
+ const edge = edgesOut[frame.next++];
493
+ const seen = state2.get(edge.to);
494
+ if (seen === "open") {
495
+ const start = path3.findIndex((step) => step.id === edge.to);
496
+ const ids = [...path3.slice(start).map((step) => step.id), edge.to];
497
+ return { ids, index: edge.index };
498
+ }
499
+ if (seen === void 0) {
500
+ state2.set(edge.to, "open");
501
+ stack.push({ id: edge.to, next: 0 });
502
+ path3.push({ id: edge.to, index: edge.index });
503
+ }
504
+ }
505
+ }
506
+ return void 0;
507
+ }
508
+
177
509
  // shared/src/structuredExchangeValidation.ts
178
510
  function dataMatchesKind(envelope) {
179
511
  const data = envelope.data;
180
512
  if (envelope.kind === "graph") return Array.isArray(data.nodes) && Array.isArray(data.edges);
181
513
  if (envelope.kind === "sequence") return Array.isArray(data.participants) && Array.isArray(data.messages);
514
+ if (envelope.kind === "timeline") return typeof data.time === "object" && data.time !== null && Array.isArray(data.rows);
182
515
  return Array.isArray(data.columns) && Array.isArray(data.rows);
183
516
  }
184
517
  function elementsOf(envelope) {
@@ -203,6 +536,7 @@ function relationshipsPointer(envelope) {
203
536
  return envelope.kind === "graph" ? "/data/edges" : "/data/messages";
204
537
  }
205
538
  function enrichedItemsOf(envelope) {
539
+ if (envelope.kind === "timeline") return [];
206
540
  const data = envelope.data;
207
541
  const items = [];
208
542
  for (const collection of ["nodes", "edges", "participants", "messages", "rows", "relations"]) {
@@ -254,6 +588,12 @@ function rowRelationsOf(envelope) {
254
588
  const relations = envelope.data.relations;
255
589
  return Array.isArray(relations) ? relations : [];
256
590
  }
591
+ function notProposable(kind, consequence) {
592
+ if (kind === "timeline") {
593
+ return `a timeline is maintained by presenting the revised timeline again, not proposed as a change, ${consequence}`;
594
+ }
595
+ return `a ${kind} is a projection and cannot be proposed, ${consequence}`;
596
+ }
257
597
  function validateStructuredExchangeSemantics(envelope) {
258
598
  const issues = [];
259
599
  if (!dataMatchesKind(envelope)) {
@@ -269,7 +609,7 @@ function validateStructuredExchangeSemantics(envelope) {
269
609
  issues.push({
270
610
  rule: "kind-not-proposable",
271
611
  path: "/target",
272
- message: `a ${envelope.kind} is a projection and cannot be proposed, so it carries no target`
612
+ message: notProposable(envelope.kind, "so it carries no target")
273
613
  });
274
614
  }
275
615
  if (envelope.removals !== void 0) {
@@ -277,7 +617,7 @@ function validateStructuredExchangeSemantics(envelope) {
277
617
  issues.push({
278
618
  rule: "kind-not-proposable",
279
619
  path: "/removals",
280
- message: `a ${envelope.kind} is a projection and cannot be proposed, so it declares no removals`
620
+ message: notProposable(envelope.kind, "so it declares no removals")
281
621
  });
282
622
  } else if (envelope.target === void 0) {
283
623
  issues.push({
@@ -338,15 +678,15 @@ function validateStructuredExchangeSemantics(envelope) {
338
678
  }
339
679
  });
340
680
  const claimed = /* @__PURE__ */ new Map();
341
- const claim = (type, ref, at2) => {
681
+ const claim = (type, ref, at) => {
342
682
  if (ref === void 0) return;
343
683
  const key = `${type}:${ref}`;
344
684
  const first = claimed.get(key);
345
- if (first === void 0) claimed.set(key, at2);
685
+ if (first === void 0) claimed.set(key, at);
346
686
  else {
347
687
  issues.push({
348
688
  rule: "duplicate-reference",
349
- path: at2,
689
+ path: at,
350
690
  message: `${type} "${ref}" is already addressed at ${first}; a proposal states one intention per thing`
351
691
  });
352
692
  }
@@ -376,7 +716,7 @@ function validateStructuredExchangeSemantics(envelope) {
376
716
  issues.push({
377
717
  rule: "kind-not-proposable",
378
718
  path: `${candidate.at}/set`,
379
- message: `a ${envelope.kind} is a projection and cannot be proposed, so nothing in it declares a change`
719
+ message: notProposable(envelope.kind, "so nothing in it declares a change")
380
720
  });
381
721
  } else if (envelope.target === void 0) {
382
722
  issues.push({
@@ -399,11 +739,11 @@ function validateStructuredExchangeSemantics(envelope) {
399
739
  countKinds(relationshipsOf(envelope))
400
740
  ]
401
741
  ];
402
- for (const [noun, at2, kinds] of vocabularies) {
742
+ for (const [noun, at, kinds] of vocabularies) {
403
743
  if (kinds.size > STRUCTURED_EXCHANGE_CEILINGS.kindsPerVocabulary) {
404
744
  issues.push({
405
745
  rule: "too-many-kinds",
406
- path: at2,
746
+ path: at,
407
747
  message: `${kinds.size} distinct ${noun} types, and no more than ${STRUCTURED_EXCHANGE_CEILINGS.kindsPerVocabulary} can be told apart in a rendering`,
408
748
  observed: kinds.size,
409
749
  limit: STRUCTURED_EXCHANGE_CEILINGS.kindsPerVocabulary
@@ -425,21 +765,21 @@ function validateStructuredExchangeSemantics(envelope) {
425
765
  );
426
766
  const seenViewpoints = /* @__PURE__ */ new Map();
427
767
  viewpoints.forEach((viewpoint, index3) => {
428
- const at2 = `/viewpoints/${index3}`;
768
+ const at = `/viewpoints/${index3}`;
429
769
  const { id, elementKinds: retainedElements, relationshipKinds: retainedRelationships } = viewpoint;
430
770
  const first = seenViewpoints.get(id);
431
771
  if (first === void 0) seenViewpoints.set(id, index3);
432
772
  else {
433
773
  issues.push({
434
774
  rule: "duplicate-viewpoint-identifier",
435
- path: `${at2}/id`,
775
+ path: `${at}/id`,
436
776
  message: `viewpoint "${id}" is already declared at /viewpoints/${first}`
437
777
  });
438
778
  }
439
779
  if (retainedElements === void 0 && retainedRelationships === void 0) {
440
780
  issues.push({
441
781
  rule: "empty-viewpoint",
442
- path: at2,
782
+ path: at,
443
783
  message: `viewpoint "${id}" retains no kind, so it would show nothing it was not already showing; name elementKinds, relationshipKinds, or both`
444
784
  });
445
785
  }
@@ -452,7 +792,7 @@ function validateStructuredExchangeSemantics(envelope) {
452
792
  if (present.has(kind)) return;
453
793
  issues.push({
454
794
  rule: "unresolved-viewpoint-kind",
455
- path: `${at2}/${list}/${position}`,
795
+ path: `${at}/${list}/${position}`,
456
796
  message: `viewpoint "${id}" retains ${noun} kind "${kind}", and no ${noun} of this document has it` + (other.has(kind) ? ` (it is a ${otherNoun} kind here, which is a different vocabulary)` : "")
457
797
  });
458
798
  });
@@ -476,6 +816,7 @@ function validateStructuredExchangeSemantics(envelope) {
476
816
  }
477
817
  });
478
818
  }
819
+ if (envelope.kind === "timeline") issues.push(...timelineIssues(envelope.data));
479
820
  for (const item of enrichedItemsOf(envelope)) {
480
821
  if (item.expect !== void 0) {
481
822
  if (envelope.target === void 0) {
@@ -9728,127 +10069,1452 @@ var structured_exchange_2_default = {
9728
10069
  }
9729
10070
  };
9730
10071
 
9731
- // shared/src/structuredExchangeDocument.ts
9732
- var STRUCTURED_EXCHANGE_SCHEMA_PREFIX = "urn:structured-exchange:";
9733
- function declaredSchemaOf(document) {
9734
- if (typeof document !== "object" || document === null || Array.isArray(document)) return void 0;
9735
- const schema2 = document.schema;
9736
- if (typeof schema2 !== "string" || !schema2.startsWith(STRUCTURED_EXCHANGE_SCHEMA_PREFIX)) return void 0;
9737
- return schema2;
9738
- }
9739
-
9740
- // shared/src/structuredExchangeSchemaNode.ts
9741
- function unwrapSchemaModule(module) {
9742
- const raw = module;
9743
- const looksLikeSchema = "$id" in raw || "type" in raw;
9744
- const root = looksLikeSchema ? raw : raw.default;
9745
- if (root.default !== root) return root;
9746
- const { default: _interop, ...rest } = root;
9747
- return rest;
9748
- }
9749
- var schemas = {
9750
- "urn:structured-exchange:1": unwrapSchemaModule(structured_exchange_1_default),
9751
- "urn:structured-exchange:2": unwrapSchemaModule(structured_exchange_2_default)
9752
- };
9753
- var schema = schemas[STRUCTURED_EXCHANGE_SCHEMA_V1];
9754
- var compiled = /* @__PURE__ */ new Map();
9755
- function schemaFor(version, kind) {
9756
- const published = schemas[version];
9757
- const variants = published.properties.data?.oneOf ?? [];
9758
- const required = { graph: "nodes", sequence: "participants", table: "columns" };
9759
- const marker = typeof kind === "string" ? required[kind] : void 0;
9760
- const variant = marker === void 0 ? void 0 : variants.find((candidate) => candidate.properties?.[marker] !== void 0);
9761
- if (variant === void 0) return published;
9762
- return {
9763
- ...published,
9764
- properties: { ...published.properties, data: variant }
9765
- };
9766
- }
9767
- var NARROWABLE_KINDS = ["graph", "sequence", "table"];
9768
- function validator(version, kind) {
9769
- const narrowable = typeof kind === "string" && NARROWABLE_KINDS.includes(kind);
9770
- const key = `${version}:${narrowable ? kind : "*"}`;
9771
- const existing = compiled.get(key);
9772
- if (existing !== void 0) return existing;
9773
- const built = Compile(schemaFor(version, narrowable ? kind : void 0));
9774
- compiled.set(key, built);
9775
- return built;
9776
- }
9777
- function observedFor(keyword, value) {
9778
- if (typeof value === "string" && (keyword === "maxLength" || keyword === "minLength")) return value.length;
9779
- if (Array.isArray(value) && (keyword === "maxItems" || keyword === "minItems")) return value.length;
9780
- return void 0;
9781
- }
9782
- function at(document, pointer) {
9783
- if (pointer === "") return document;
9784
- let current = document;
9785
- for (const rawSegment of pointer.slice(1).split("/")) {
9786
- if (current === null || typeof current !== "object") return void 0;
9787
- const segment4 = rawSegment.replace(/~1/g, "/").replace(/~0/g, "~");
9788
- current = current[segment4];
9789
- }
9790
- return current;
9791
- }
9792
- var checkStructuredExchangeSchema = (document) => {
9793
- const declared = declaredSchemaOf(document);
9794
- const version = supportedSchemaOf(declared);
9795
- if (version === void 0 && declared !== void 0) {
9796
- return [
9797
- {
9798
- rule: "unsupported-version",
9799
- path: "/schema",
9800
- message: `this build validates ${STRUCTURED_EXCHANGE_SUPPORTED_SCHEMAS.join(" and ")}`
10072
+ // shared/schemas/structured-exchange-3.json
10073
+ var structured_exchange_3_default = {
10074
+ $schema: "https://json-schema.org/draft/2020-12/schema",
10075
+ $id: "urn:structured-exchange:3",
10076
+ title: "Structured exchange envelope v3",
10077
+ description: "Version 2 plus one kind, the timeline. Every version 2 construct keeps its definition, so a version 2 document means the same thing once its identifier is changed to this one.",
10078
+ type: "object",
10079
+ additionalProperties: false,
10080
+ required: [
10081
+ "schema",
10082
+ "kind",
10083
+ "data"
10084
+ ],
10085
+ $defs: {
10086
+ ref: {
10087
+ type: "string",
10088
+ minLength: 1,
10089
+ maxLength: 200
10090
+ },
10091
+ localId: {
10092
+ type: "string",
10093
+ minLength: 1,
10094
+ maxLength: 200
10095
+ },
10096
+ label: {
10097
+ type: "string",
10098
+ maxLength: 500
10099
+ },
10100
+ kind: {
10101
+ description: "The producing tool's type or stereotype for this thing \u2014 'block', 'sensor', 'requirement', 'power'. Free text, because the vocabulary belongs to the domain and not to this contract; a profile names which domain owns it.",
10102
+ type: "string",
10103
+ minLength: 1,
10104
+ maxLength: 100
10105
+ },
10106
+ reference: {
10107
+ description: "Something owned elsewhere, named by the identifier its authority knows it by. Opaque: never parsed, resolved or fetched.",
10108
+ type: "object",
10109
+ additionalProperties: false,
10110
+ required: [
10111
+ "ref"
10112
+ ],
10113
+ properties: {
10114
+ ref: {
10115
+ $ref: "#/$defs/ref"
10116
+ }
9801
10117
  }
9802
- ];
9803
- }
9804
- const issues = [];
9805
- const contract = version ?? STRUCTURED_EXCHANGE_SCHEMA_V1;
9806
- const published = [...validator(contract, void 0).Errors(document)];
9807
- if (published.length === 0) return issues;
9808
- const kind = document?.kind;
9809
- const narrowed = [...validator(contract, kind).Errors(document)];
9810
- const seen = new Set(narrowed.map((error) => `${error.keyword}@${error.instancePath}`));
9811
- const combined = [
9812
- ...narrowed,
9813
- ...published.filter((error) => !seen.has(`${error.keyword}@${error.instancePath}`))
9814
- ];
9815
- for (const error of combined) {
9816
- const keyword = String(error.keyword ?? "schema");
9817
- const limit = error.params?.limit;
9818
- issues.push({
9819
- rule: `schema/${keyword}`,
9820
- path: error.instancePath ?? "",
9821
- message: error.message ?? "does not conform to the published schema",
9822
- ...limit !== void 0 ? { limit, level: "ceiling" } : {},
9823
- ...(() => {
9824
- const observed = observedFor(keyword, at(document, error.instancePath ?? ""));
9825
- return observed === void 0 ? {} : { observed };
9826
- })()
9827
- });
9828
- }
9829
- return issues.sort((left, right) => right.path.split("/").length - left.path.split("/").length);
9830
- };
9831
-
9832
- // shared/src/structuredExchangeProfile.ts
9833
- var STRUCTURED_EXCHANGE_PROFILE_REGISTRY_PATH = ".pi-outpost/structured-exchange.json";
9834
- var STRUCTURED_EXCHANGE_CONFORMITY_REPORT_PROFILE = "urn:structured-exchange-conformity-report:1";
9835
- var STRUCTURED_EXCHANGE_RULES_REGISTER_PROFILE = "urn:structured-exchange-rules-register:1";
9836
- var STRUCTURED_EXCHANGE_RULE_PATTERNS_PROFILE = "urn:structured-exchange-rule-patterns:1";
9837
- var RESERVED_PROFILE_IDENTIFIERS = /* @__PURE__ */ new Set([
9838
- STRUCTURED_EXCHANGE_CONFORMITY_REPORT_PROFILE,
9839
- STRUCTURED_EXCHANGE_RULES_REGISTER_PROFILE,
9840
- STRUCTURED_EXCHANGE_RULE_PATTERNS_PROFILE
9841
- ]);
9842
- var STRUCTURED_EXCHANGE_PROFILE_CEILINGS = {
9843
- /** A profile's identifier, bounded like the `profile` a document names it by. */
9844
- id: 200,
9845
- label: 500,
9846
- description: 2e3,
9847
- /** Kinds per vocabulary, as a document's `kindsPerVocabulary`. */
9848
- kindsPerVocabulary: 64,
9849
- /** Attributes one kind declares, as a document's `attributesPerItem`. */
9850
- attributesPerKind: 50,
9851
- attributeName: 200,
10118
+ },
10119
+ attributeScalar: {
10120
+ type: [
10121
+ "string",
10122
+ "number",
10123
+ "boolean",
10124
+ "null"
10125
+ ],
10126
+ maxLength: 1e3
10127
+ },
10128
+ attributeValue: {
10129
+ description: "A scalar, an opaque reference, or one flat list of those. Lists never nest and no other object shape is allowed: recursive values would weaken size analysis, generic rendering and deterministic comparison.",
10130
+ oneOf: [
10131
+ {
10132
+ $ref: "#/$defs/attributeScalar"
10133
+ },
10134
+ {
10135
+ $ref: "#/$defs/reference"
10136
+ },
10137
+ {
10138
+ type: "array",
10139
+ maxItems: 50,
10140
+ items: {
10141
+ oneOf: [
10142
+ {
10143
+ $ref: "#/$defs/attributeScalar"
10144
+ },
10145
+ {
10146
+ $ref: "#/$defs/reference"
10147
+ }
10148
+ ]
10149
+ }
10150
+ }
10151
+ ]
10152
+ },
10153
+ attributes: {
10154
+ description: "Domain-owned properties of this item. Names belong to the profile's vocabulary; this contract bounds them and reads none of them.",
10155
+ type: "object",
10156
+ maxProperties: 50,
10157
+ propertyNames: {
10158
+ minLength: 1,
10159
+ maxLength: 200
10160
+ },
10161
+ additionalProperties: {
10162
+ $ref: "#/$defs/attributeValue"
10163
+ }
10164
+ },
10165
+ removeAttributes: {
10166
+ description: "Attribute names this proposal takes away. Deletion is explicit because null is an ordinary value: a producer that means 'unset' says so here.",
10167
+ type: "array",
10168
+ maxItems: 50,
10169
+ items: {
10170
+ $ref: "#/$defs/localId"
10171
+ }
10172
+ },
10173
+ revision: {
10174
+ description: "The revision an authority knows its state by. Opaque: compared by the authority, never interpreted here.",
10175
+ type: "string",
10176
+ minLength: 1,
10177
+ maxLength: 200
10178
+ },
10179
+ position: {
10180
+ type: "integer",
10181
+ minimum: 0,
10182
+ maximum: 1e6
10183
+ },
10184
+ location: {
10185
+ description: "Where this item can be found, as a hint for a reader. Never an identity: locations take no part in endpoint resolution, duplicate detection, or what a proposal addresses, and moving the thing does not change what it is.",
10186
+ type: "object",
10187
+ additionalProperties: false,
10188
+ required: [
10189
+ "uri"
10190
+ ],
10191
+ properties: {
10192
+ uri: {
10193
+ type: "string",
10194
+ minLength: 1,
10195
+ maxLength: 2e3
10196
+ },
10197
+ revision: {
10198
+ $ref: "#/$defs/revision"
10199
+ },
10200
+ range: {
10201
+ type: "object",
10202
+ additionalProperties: false,
10203
+ required: [
10204
+ "startLine",
10205
+ "endLine"
10206
+ ],
10207
+ description: "A zero-based range inside the resource. Ordering is checked after the schema, which cannot conveniently compare two positions.",
10208
+ properties: {
10209
+ startLine: {
10210
+ $ref: "#/$defs/position"
10211
+ },
10212
+ startCharacter: {
10213
+ $ref: "#/$defs/position"
10214
+ },
10215
+ endLine: {
10216
+ $ref: "#/$defs/position"
10217
+ },
10218
+ endCharacter: {
10219
+ $ref: "#/$defs/position"
10220
+ }
10221
+ }
10222
+ }
10223
+ }
10224
+ },
10225
+ locations: {
10226
+ type: "array",
10227
+ maxItems: 10,
10228
+ items: {
10229
+ $ref: "#/$defs/location"
10230
+ }
10231
+ },
10232
+ artifact: {
10233
+ description: "A related artifact \u2014 an implementation, a report, a piece of evidence \u2014 named rather than carried. The digest is mandatory so an approval refers to stable bytes even when the URI is not: nothing here is retrieved by validation or by rendering.",
10234
+ type: "object",
10235
+ additionalProperties: false,
10236
+ required: [
10237
+ "rel",
10238
+ "uri",
10239
+ "sha256"
10240
+ ],
10241
+ properties: {
10242
+ rel: {
10243
+ type: "string",
10244
+ minLength: 1,
10245
+ maxLength: 100
10246
+ },
10247
+ uri: {
10248
+ type: "string",
10249
+ minLength: 1,
10250
+ maxLength: 2e3
10251
+ },
10252
+ sha256: {
10253
+ type: "string",
10254
+ pattern: "^sha256:[0-9a-f]{64}$"
10255
+ },
10256
+ mediaType: {
10257
+ type: "string",
10258
+ minLength: 1,
10259
+ maxLength: 100
10260
+ },
10261
+ label: {
10262
+ $ref: "#/$defs/label"
10263
+ }
10264
+ }
10265
+ },
10266
+ artifacts: {
10267
+ type: "array",
10268
+ maxItems: 20,
10269
+ items: {
10270
+ $ref: "#/$defs/artifact"
10271
+ }
10272
+ },
10273
+ expect: {
10274
+ description: "What the producer believes is true of this item right now. A declaration for the receiving authority to check immediately before applying, never a claim this application has established.",
10275
+ type: "object",
10276
+ additionalProperties: false,
10277
+ minProperties: 1,
10278
+ properties: {
10279
+ label: {
10280
+ $ref: "#/$defs/label"
10281
+ },
10282
+ kind: {
10283
+ $ref: "#/$defs/kind"
10284
+ },
10285
+ container: {
10286
+ $ref: "#/$defs/localId"
10287
+ },
10288
+ revision: {
10289
+ $ref: "#/$defs/revision"
10290
+ },
10291
+ attributes: {
10292
+ $ref: "#/$defs/attributes"
10293
+ }
10294
+ }
10295
+ },
10296
+ elementChange: {
10297
+ type: "object",
10298
+ additionalProperties: false,
10299
+ minProperties: 1,
10300
+ properties: {
10301
+ label: {
10302
+ $ref: "#/$defs/label"
10303
+ },
10304
+ kind: {
10305
+ $ref: "#/$defs/kind"
10306
+ },
10307
+ container: {
10308
+ $ref: "#/$defs/localId"
10309
+ },
10310
+ attributes: {
10311
+ $ref: "#/$defs/attributes"
10312
+ },
10313
+ removeAttributes: {
10314
+ $ref: "#/$defs/removeAttributes"
10315
+ }
10316
+ }
10317
+ },
10318
+ edgeChange: {
10319
+ type: "object",
10320
+ additionalProperties: false,
10321
+ minProperties: 1,
10322
+ properties: {
10323
+ kind: {
10324
+ $ref: "#/$defs/kind"
10325
+ },
10326
+ label: {
10327
+ $ref: "#/$defs/label"
10328
+ },
10329
+ attributes: {
10330
+ $ref: "#/$defs/attributes"
10331
+ },
10332
+ removeAttributes: {
10333
+ $ref: "#/$defs/removeAttributes"
10334
+ }
10335
+ }
10336
+ },
10337
+ messageChange: {
10338
+ type: "object",
10339
+ additionalProperties: false,
10340
+ minProperties: 1,
10341
+ properties: {
10342
+ label: {
10343
+ $ref: "#/$defs/label"
10344
+ },
10345
+ attributes: {
10346
+ $ref: "#/$defs/attributes"
10347
+ },
10348
+ removeAttributes: {
10349
+ $ref: "#/$defs/removeAttributes"
10350
+ }
10351
+ }
10352
+ },
10353
+ rowChange: {
10354
+ type: "object",
10355
+ additionalProperties: false,
10356
+ minProperties: 1,
10357
+ properties: {
10358
+ label: {
10359
+ $ref: "#/$defs/label"
10360
+ },
10361
+ kind: {
10362
+ $ref: "#/$defs/kind"
10363
+ },
10364
+ cells: {
10365
+ $ref: "#/$defs/cells"
10366
+ },
10367
+ attributes: {
10368
+ $ref: "#/$defs/attributes"
10369
+ },
10370
+ removeAttributes: {
10371
+ $ref: "#/$defs/removeAttributes"
10372
+ }
10373
+ }
10374
+ },
10375
+ container: {
10376
+ description: "A named group of elements or participants. Grouping only: relationships connect elements and cross container boundaries freely, and a container carries no relationship of its own.",
10377
+ type: "object",
10378
+ additionalProperties: false,
10379
+ required: [
10380
+ "id",
10381
+ "label"
10382
+ ],
10383
+ properties: {
10384
+ id: {
10385
+ $ref: "#/$defs/localId"
10386
+ },
10387
+ label: {
10388
+ $ref: "#/$defs/label"
10389
+ },
10390
+ kind: {
10391
+ $ref: "#/$defs/kind"
10392
+ }
10393
+ }
10394
+ },
10395
+ element: {
10396
+ type: "object",
10397
+ additionalProperties: false,
10398
+ required: [
10399
+ "id"
10400
+ ],
10401
+ properties: {
10402
+ id: {
10403
+ $ref: "#/$defs/localId"
10404
+ },
10405
+ ref: {
10406
+ $ref: "#/$defs/ref"
10407
+ },
10408
+ label: {
10409
+ $ref: "#/$defs/label"
10410
+ },
10411
+ kind: {
10412
+ $ref: "#/$defs/kind"
10413
+ },
10414
+ set: {
10415
+ $ref: "#/$defs/elementChange"
10416
+ },
10417
+ container: {
10418
+ $ref: "#/$defs/localId"
10419
+ },
10420
+ attributes: {
10421
+ $ref: "#/$defs/attributes"
10422
+ },
10423
+ expect: {
10424
+ $ref: "#/$defs/expect"
10425
+ },
10426
+ locations: {
10427
+ $ref: "#/$defs/locations"
10428
+ },
10429
+ artifacts: {
10430
+ $ref: "#/$defs/artifacts"
10431
+ }
10432
+ },
10433
+ if: {
10434
+ not: {
10435
+ required: [
10436
+ "ref"
10437
+ ]
10438
+ }
10439
+ },
10440
+ then: {
10441
+ required: [
10442
+ "label"
10443
+ ]
10444
+ }
10445
+ },
10446
+ edge: {
10447
+ type: "object",
10448
+ additionalProperties: false,
10449
+ required: [
10450
+ "from",
10451
+ "to"
10452
+ ],
10453
+ properties: {
10454
+ from: {
10455
+ $ref: "#/$defs/localId"
10456
+ },
10457
+ to: {
10458
+ $ref: "#/$defs/localId"
10459
+ },
10460
+ kind: {
10461
+ $ref: "#/$defs/kind"
10462
+ },
10463
+ ref: {
10464
+ $ref: "#/$defs/ref"
10465
+ },
10466
+ label: {
10467
+ $ref: "#/$defs/label"
10468
+ },
10469
+ set: {
10470
+ $ref: "#/$defs/edgeChange"
10471
+ },
10472
+ attributes: {
10473
+ $ref: "#/$defs/attributes"
10474
+ },
10475
+ expect: {
10476
+ $ref: "#/$defs/expect"
10477
+ },
10478
+ locations: {
10479
+ $ref: "#/$defs/locations"
10480
+ },
10481
+ artifacts: {
10482
+ $ref: "#/$defs/artifacts"
10483
+ }
10484
+ },
10485
+ if: {
10486
+ not: {
10487
+ required: [
10488
+ "ref"
10489
+ ]
10490
+ }
10491
+ },
10492
+ then: {
10493
+ required: [
10494
+ "kind"
10495
+ ]
10496
+ }
10497
+ },
10498
+ message: {
10499
+ type: "object",
10500
+ additionalProperties: false,
10501
+ required: [
10502
+ "from",
10503
+ "to"
10504
+ ],
10505
+ properties: {
10506
+ from: {
10507
+ $ref: "#/$defs/localId"
10508
+ },
10509
+ to: {
10510
+ $ref: "#/$defs/localId"
10511
+ },
10512
+ ref: {
10513
+ $ref: "#/$defs/ref"
10514
+ },
10515
+ label: {
10516
+ $ref: "#/$defs/label"
10517
+ },
10518
+ set: {
10519
+ $ref: "#/$defs/messageChange"
10520
+ },
10521
+ attributes: {
10522
+ $ref: "#/$defs/attributes"
10523
+ },
10524
+ expect: {
10525
+ $ref: "#/$defs/expect"
10526
+ },
10527
+ locations: {
10528
+ $ref: "#/$defs/locations"
10529
+ },
10530
+ artifacts: {
10531
+ $ref: "#/$defs/artifacts"
10532
+ }
10533
+ },
10534
+ if: {
10535
+ not: {
10536
+ required: [
10537
+ "ref"
10538
+ ]
10539
+ }
10540
+ },
10541
+ then: {
10542
+ required: [
10543
+ "label"
10544
+ ]
10545
+ }
10546
+ },
10547
+ removal: {
10548
+ description: "Something to take out of the target, named by the reference the authority knows it by. The other fields describe it and never identify it: they exist so a reader can see what they are approving the removal of.",
10549
+ type: "object",
10550
+ additionalProperties: false,
10551
+ required: [
10552
+ "type",
10553
+ "ref"
10554
+ ],
10555
+ properties: {
10556
+ type: {
10557
+ enum: [
10558
+ "element",
10559
+ "relationship",
10560
+ "row"
10561
+ ]
10562
+ },
10563
+ ref: {
10564
+ $ref: "#/$defs/ref"
10565
+ },
10566
+ label: {
10567
+ $ref: "#/$defs/label"
10568
+ },
10569
+ kind: {
10570
+ $ref: "#/$defs/kind"
10571
+ },
10572
+ from: {
10573
+ $ref: "#/$defs/localId"
10574
+ },
10575
+ to: {
10576
+ $ref: "#/$defs/localId"
10577
+ }
10578
+ }
10579
+ },
10580
+ cells: {
10581
+ type: "array",
10582
+ items: {
10583
+ type: [
10584
+ "string",
10585
+ "number",
10586
+ "boolean",
10587
+ "null"
10588
+ ],
10589
+ maxLength: 1e3
10590
+ },
10591
+ maxItems: 50
10592
+ },
10593
+ role: {
10594
+ enum: [
10595
+ "added",
10596
+ "changed",
10597
+ "context",
10598
+ "removed"
10599
+ ]
10600
+ },
10601
+ dataRow: {
10602
+ description: "A row of the table. It may carry an identity of its own \u2014 a document-local id, the reference its authority knows it by, and the kind of thing it is \u2014 which is what lets a relation point at it and a patch address it.",
10603
+ type: "object",
10604
+ additionalProperties: false,
10605
+ required: [
10606
+ "cells"
10607
+ ],
10608
+ properties: {
10609
+ cells: {
10610
+ $ref: "#/$defs/cells"
10611
+ },
10612
+ role: {
10613
+ $ref: "#/$defs/role"
10614
+ },
10615
+ id: {
10616
+ $ref: "#/$defs/localId"
10617
+ },
10618
+ ref: {
10619
+ $ref: "#/$defs/ref"
10620
+ },
10621
+ kind: {
10622
+ $ref: "#/$defs/kind"
10623
+ },
10624
+ label: {
10625
+ $ref: "#/$defs/label"
10626
+ },
10627
+ set: {
10628
+ $ref: "#/$defs/rowChange"
10629
+ },
10630
+ attributes: {
10631
+ $ref: "#/$defs/attributes"
10632
+ },
10633
+ expect: {
10634
+ $ref: "#/$defs/expect"
10635
+ },
10636
+ locations: {
10637
+ $ref: "#/$defs/locations"
10638
+ },
10639
+ artifacts: {
10640
+ $ref: "#/$defs/artifacts"
10641
+ }
10642
+ }
10643
+ },
10644
+ structuralRow: {
10645
+ description: "A heading that organises the table \u2014 a chapter, a section \u2014 rather than a row of data. It spans the table and is not aligned to its columns; it is never inferred from a data row whose cells are empty.",
10646
+ type: "object",
10647
+ additionalProperties: false,
10648
+ required: [
10649
+ "heading"
10650
+ ],
10651
+ properties: {
10652
+ heading: {
10653
+ type: "string",
10654
+ minLength: 1,
10655
+ maxLength: 500
10656
+ },
10657
+ depth: {
10658
+ type: "integer",
10659
+ minimum: 1,
10660
+ maximum: 6
10661
+ },
10662
+ role: {
10663
+ $ref: "#/$defs/role"
10664
+ },
10665
+ id: {
10666
+ $ref: "#/$defs/localId"
10667
+ },
10668
+ ref: {
10669
+ $ref: "#/$defs/ref"
10670
+ }
10671
+ }
10672
+ },
10673
+ endpoint: {
10674
+ description: "One end of a relation: a row of this document by its local id, or something outside it by the reference its authority knows. Stated explicitly so an unresolvable local endpoint is an error rather than silently reread as external.",
10675
+ type: "object",
10676
+ additionalProperties: false,
10677
+ minProperties: 1,
10678
+ maxProperties: 1,
10679
+ oneOf: [
10680
+ {
10681
+ required: [
10682
+ "id"
10683
+ ]
10684
+ },
10685
+ {
10686
+ required: [
10687
+ "ref"
10688
+ ]
10689
+ }
10690
+ ],
10691
+ properties: {
10692
+ id: {
10693
+ $ref: "#/$defs/localId"
10694
+ },
10695
+ ref: {
10696
+ $ref: "#/$defs/ref"
10697
+ }
10698
+ }
10699
+ },
10700
+ relation: {
10701
+ description: "A relation between rows \u2014 traceability. Carries an opaque kind from the profile's vocabulary, exactly as a relationship between graph elements does; unlike one, an end may leave the document, because the test that verifies a requirement usually lives elsewhere.",
10702
+ type: "object",
10703
+ additionalProperties: false,
10704
+ required: [
10705
+ "from",
10706
+ "to",
10707
+ "kind"
10708
+ ],
10709
+ properties: {
10710
+ from: {
10711
+ $ref: "#/$defs/endpoint"
10712
+ },
10713
+ to: {
10714
+ $ref: "#/$defs/endpoint"
10715
+ },
10716
+ kind: {
10717
+ $ref: "#/$defs/kind"
10718
+ },
10719
+ ref: {
10720
+ $ref: "#/$defs/ref"
10721
+ },
10722
+ label: {
10723
+ $ref: "#/$defs/label"
10724
+ },
10725
+ attributes: {
10726
+ $ref: "#/$defs/attributes"
10727
+ }
10728
+ }
10729
+ },
10730
+ viewpoint: {
10731
+ description: "A named reading of a graph: the concern it frames, and the element and relationship kinds that address it. An inclusion \u2014 showing it shows what it retains and hides the rest, so a kind added to the model later is not shown unannounced. A vocabulary it names no kinds for is left whole. The term is ISO/IEC/IEEE 42010's.",
10732
+ type: "object",
10733
+ additionalProperties: false,
10734
+ required: [
10735
+ "id",
10736
+ "label",
10737
+ "concern"
10738
+ ],
10739
+ properties: {
10740
+ id: {
10741
+ $ref: "#/$defs/localId"
10742
+ },
10743
+ label: {
10744
+ description: "What a reader selects it by.",
10745
+ type: "string",
10746
+ minLength: 1,
10747
+ maxLength: 500
10748
+ },
10749
+ concern: {
10750
+ description: "What this reading is for \u2014 stated inside every figure drawn for it, so a figure taken out of its report still says what it is a reading of.",
10751
+ type: "string",
10752
+ minLength: 1,
10753
+ maxLength: 500
10754
+ },
10755
+ elementKinds: {
10756
+ description: "Element kinds this viewpoint retains. Absent leaves the element vocabulary whole.",
10757
+ type: "array",
10758
+ minItems: 1,
10759
+ maxItems: 64,
10760
+ uniqueItems: true,
10761
+ items: {
10762
+ $ref: "#/$defs/kind"
10763
+ }
10764
+ },
10765
+ relationshipKinds: {
10766
+ description: "Relationship kinds this viewpoint retains. Absent leaves the relationship vocabulary whole.",
10767
+ type: "array",
10768
+ minItems: 1,
10769
+ maxItems: 64,
10770
+ uniqueItems: true,
10771
+ items: {
10772
+ $ref: "#/$defs/kind"
10773
+ }
10774
+ }
10775
+ }
10776
+ },
10777
+ calendarDate: {
10778
+ description: "A calendar date, YYYY-MM-DD. A day, not an instant: no time and no zone, so a position derived from it is the same for every reader.",
10779
+ type: "string",
10780
+ pattern: "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"
10781
+ },
10782
+ timelineActivity: {
10783
+ description: "A duration on a task, drawn as a bar from the start of its start day to the end of its end day.",
10784
+ type: "object",
10785
+ additionalProperties: false,
10786
+ required: [
10787
+ "type",
10788
+ "start",
10789
+ "end"
10790
+ ],
10791
+ properties: {
10792
+ type: {
10793
+ const: "activity"
10794
+ },
10795
+ start: {
10796
+ $ref: "#/$defs/calendarDate"
10797
+ },
10798
+ end: {
10799
+ $ref: "#/$defs/calendarDate"
10800
+ },
10801
+ id: {
10802
+ $ref: "#/$defs/localId"
10803
+ },
10804
+ label: {
10805
+ $ref: "#/$defs/label"
10806
+ },
10807
+ kind: {
10808
+ $ref: "#/$defs/kind"
10809
+ },
10810
+ previous: {
10811
+ description: "Where the activity stood in the plan named by comparedTo.",
10812
+ type: "object",
10813
+ additionalProperties: false,
10814
+ required: [
10815
+ "start",
10816
+ "end"
10817
+ ],
10818
+ properties: {
10819
+ start: {
10820
+ $ref: "#/$defs/calendarDate"
10821
+ },
10822
+ end: {
10823
+ $ref: "#/$defs/calendarDate"
10824
+ }
10825
+ }
10826
+ },
10827
+ role: {
10828
+ $ref: "#/$defs/timelineChangeRole"
10829
+ }
10830
+ }
10831
+ },
10832
+ timelineMilestone: {
10833
+ description: "A point in time on a task, drawn as a star.",
10834
+ type: "object",
10835
+ additionalProperties: false,
10836
+ required: [
10837
+ "type",
10838
+ "date"
10839
+ ],
10840
+ properties: {
10841
+ type: {
10842
+ const: "milestone"
10843
+ },
10844
+ date: {
10845
+ $ref: "#/$defs/calendarDate"
10846
+ },
10847
+ id: {
10848
+ $ref: "#/$defs/localId"
10849
+ },
10850
+ label: {
10851
+ $ref: "#/$defs/label"
10852
+ },
10853
+ kind: {
10854
+ $ref: "#/$defs/kind"
10855
+ },
10856
+ previous: {
10857
+ description: "Where the milestone stood in the plan named by comparedTo.",
10858
+ type: "object",
10859
+ additionalProperties: false,
10860
+ required: [
10861
+ "date"
10862
+ ],
10863
+ properties: {
10864
+ date: {
10865
+ $ref: "#/$defs/calendarDate"
10866
+ }
10867
+ }
10868
+ },
10869
+ role: {
10870
+ $ref: "#/$defs/timelineChangeRole"
10871
+ }
10872
+ }
10873
+ },
10874
+ timelineTask: {
10875
+ description: "A semantic row: a named task holding any number of activities and milestones, in any order.",
10876
+ type: "object",
10877
+ additionalProperties: false,
10878
+ required: [
10879
+ "type",
10880
+ "id",
10881
+ "label",
10882
+ "items"
10883
+ ],
10884
+ properties: {
10885
+ type: {
10886
+ const: "task"
10887
+ },
10888
+ id: {
10889
+ $ref: "#/$defs/localId"
10890
+ },
10891
+ label: {
10892
+ $ref: "#/$defs/label"
10893
+ },
10894
+ items: {
10895
+ type: "array",
10896
+ maxItems: 100,
10897
+ items: {
10898
+ $ref: "#/$defs/timelineItem"
10899
+ }
10900
+ },
10901
+ role: {
10902
+ $ref: "#/$defs/timelineChangeRole"
10903
+ }
10904
+ }
10905
+ },
10906
+ timelineSeparator: {
10907
+ description: "A horizontal divider between groups of tasks, with an optional title. Carries no dates and takes no part in the time axis.",
10908
+ type: "object",
10909
+ additionalProperties: false,
10910
+ required: [
10911
+ "type"
10912
+ ],
10913
+ properties: {
10914
+ type: {
10915
+ const: "separator"
10916
+ },
10917
+ label: {
10918
+ $ref: "#/$defs/label"
10919
+ }
10920
+ }
10921
+ },
10922
+ timelineDependency: {
10923
+ description: "A Gantt dependency: the successor `to` waits on the predecessor `from`. Endpoints name a task or an item by id; a task stands for the span of its items. Without a type it is finish-to-start.",
10924
+ type: "object",
10925
+ additionalProperties: false,
10926
+ required: [
10927
+ "from",
10928
+ "to"
10929
+ ],
10930
+ properties: {
10931
+ from: {
10932
+ $ref: "#/$defs/localId"
10933
+ },
10934
+ to: {
10935
+ $ref: "#/$defs/localId"
10936
+ },
10937
+ type: {
10938
+ enum: [
10939
+ "finish-to-start",
10940
+ "start-to-start",
10941
+ "finish-to-finish",
10942
+ "start-to-finish"
10943
+ ]
10944
+ }
10945
+ }
10946
+ },
10947
+ timelineItem: {
10948
+ description: "An activity or a milestone, selected by its type so a refusal speaks about the one it is.",
10949
+ type: "object",
10950
+ required: [
10951
+ "type"
10952
+ ],
10953
+ properties: {
10954
+ type: {
10955
+ enum: [
10956
+ "activity",
10957
+ "milestone"
10958
+ ]
10959
+ }
10960
+ },
10961
+ allOf: [
10962
+ {
10963
+ if: {
10964
+ properties: {
10965
+ type: {
10966
+ const: "activity"
10967
+ }
10968
+ }
10969
+ },
10970
+ then: {
10971
+ $ref: "#/$defs/timelineActivity"
10972
+ }
10973
+ },
10974
+ {
10975
+ if: {
10976
+ properties: {
10977
+ type: {
10978
+ const: "milestone"
10979
+ }
10980
+ }
10981
+ },
10982
+ then: {
10983
+ $ref: "#/$defs/timelineMilestone"
10984
+ }
10985
+ }
10986
+ ]
10987
+ },
10988
+ timelineRow: {
10989
+ description: "A task or a separator, selected by its type so a refusal speaks about the one it is.",
10990
+ type: "object",
10991
+ required: [
10992
+ "type"
10993
+ ],
10994
+ properties: {
10995
+ type: {
10996
+ enum: [
10997
+ "task",
10998
+ "separator"
10999
+ ]
11000
+ }
11001
+ },
11002
+ allOf: [
11003
+ {
11004
+ if: {
11005
+ properties: {
11006
+ type: {
11007
+ const: "task"
11008
+ }
11009
+ }
11010
+ },
11011
+ then: {
11012
+ $ref: "#/$defs/timelineTask"
11013
+ }
11014
+ },
11015
+ {
11016
+ if: {
11017
+ properties: {
11018
+ type: {
11019
+ const: "separator"
11020
+ }
11021
+ }
11022
+ },
11023
+ then: {
11024
+ $ref: "#/$defs/timelineSeparator"
11025
+ }
11026
+ }
11027
+ ]
11028
+ },
11029
+ timelineChangeRole: {
11030
+ description: "In a compared timeline: new since the previous plan, or dropped from it and kept so it can be shown.",
11031
+ enum: [
11032
+ "added",
11033
+ "removed"
11034
+ ]
11035
+ },
11036
+ timelinePeriod: {
11037
+ description: "A span of the calendar the plan runs in \u2014 a closure, holidays. Drawn as a band across every row; constrains nothing.",
11038
+ type: "object",
11039
+ additionalProperties: false,
11040
+ required: [
11041
+ "start",
11042
+ "end"
11043
+ ],
11044
+ properties: {
11045
+ start: {
11046
+ $ref: "#/$defs/calendarDate"
11047
+ },
11048
+ end: {
11049
+ $ref: "#/$defs/calendarDate"
11050
+ },
11051
+ label: {
11052
+ $ref: "#/$defs/label"
11053
+ },
11054
+ kind: {
11055
+ $ref: "#/$defs/kind"
11056
+ }
11057
+ }
11058
+ },
11059
+ timelineReference: {
11060
+ description: "A named day the plan is judged against \u2014 a contractual date, a delivery. Drawn as a line across every row.",
11061
+ type: "object",
11062
+ additionalProperties: false,
11063
+ required: [
11064
+ "date",
11065
+ "label"
11066
+ ],
11067
+ properties: {
11068
+ date: {
11069
+ $ref: "#/$defs/calendarDate"
11070
+ },
11071
+ label: {
11072
+ type: "string",
11073
+ minLength: 1,
11074
+ maxLength: 500
11075
+ },
11076
+ kind: {
11077
+ $ref: "#/$defs/kind"
11078
+ }
11079
+ }
11080
+ }
11081
+ },
11082
+ properties: {
11083
+ schema: {
11084
+ const: "urn:structured-exchange:3"
11085
+ },
11086
+ kind: {
11087
+ enum: [
11088
+ "graph",
11089
+ "sequence",
11090
+ "table",
11091
+ "timeline"
11092
+ ]
11093
+ },
11094
+ profile: {
11095
+ description: "The vocabulary that owns the kinds and attribute names in this document. Opaque: never fetched, executed, or required to be known \u2014 an unknown profile is presented generically, not refused.",
11096
+ type: "string",
11097
+ minLength: 1,
11098
+ maxLength: 200
11099
+ },
11100
+ target: {
11101
+ description: "The artifact this document proposes a change to, and the revision it was prepared against. Its presence is what makes the document a proposal.",
11102
+ type: "object",
11103
+ additionalProperties: false,
11104
+ required: [
11105
+ "ref"
11106
+ ],
11107
+ properties: {
11108
+ ref: {
11109
+ $ref: "#/$defs/ref"
11110
+ },
11111
+ revision: {
11112
+ $ref: "#/$defs/revision"
11113
+ }
11114
+ }
11115
+ },
11116
+ removals: {
11117
+ type: "array",
11118
+ maxItems: 500,
11119
+ items: {
11120
+ $ref: "#/$defs/removal"
11121
+ }
11122
+ },
11123
+ artifacts: {
11124
+ type: "array",
11125
+ maxItems: 50,
11126
+ items: {
11127
+ $ref: "#/$defs/artifact"
11128
+ }
11129
+ },
11130
+ viewpoints: {
11131
+ description: "Named readings of this document. Only a graph may declare them. Presentation only: selecting one never alters the document.",
11132
+ type: "array",
11133
+ maxItems: 20,
11134
+ items: {
11135
+ $ref: "#/$defs/viewpoint"
11136
+ }
11137
+ },
11138
+ data: {
11139
+ oneOf: [
11140
+ {
11141
+ type: "object",
11142
+ additionalProperties: false,
11143
+ required: [
11144
+ "nodes",
11145
+ "edges"
11146
+ ],
11147
+ properties: {
11148
+ nodes: {
11149
+ type: "array",
11150
+ minItems: 1,
11151
+ maxItems: 500,
11152
+ items: {
11153
+ $ref: "#/$defs/element"
11154
+ }
11155
+ },
11156
+ edges: {
11157
+ type: "array",
11158
+ maxItems: 2e3,
11159
+ items: {
11160
+ $ref: "#/$defs/edge"
11161
+ }
11162
+ },
11163
+ containers: {
11164
+ type: "array",
11165
+ maxItems: 50,
11166
+ items: {
11167
+ $ref: "#/$defs/container"
11168
+ }
11169
+ }
11170
+ }
11171
+ },
11172
+ {
11173
+ type: "object",
11174
+ additionalProperties: false,
11175
+ required: [
11176
+ "participants",
11177
+ "messages"
11178
+ ],
11179
+ properties: {
11180
+ participants: {
11181
+ type: "array",
11182
+ minItems: 1,
11183
+ maxItems: 100,
11184
+ items: {
11185
+ $ref: "#/$defs/element"
11186
+ }
11187
+ },
11188
+ messages: {
11189
+ type: "array",
11190
+ maxItems: 1e3,
11191
+ items: {
11192
+ $ref: "#/$defs/message"
11193
+ }
11194
+ },
11195
+ containers: {
11196
+ type: "array",
11197
+ maxItems: 50,
11198
+ items: {
11199
+ $ref: "#/$defs/container"
11200
+ }
11201
+ }
11202
+ }
11203
+ },
11204
+ {
11205
+ type: "object",
11206
+ additionalProperties: false,
11207
+ required: [
11208
+ "columns",
11209
+ "rows"
11210
+ ],
11211
+ properties: {
11212
+ columns: {
11213
+ type: "array",
11214
+ minItems: 1,
11215
+ maxItems: 50,
11216
+ items: {
11217
+ type: "string",
11218
+ minLength: 1,
11219
+ maxLength: 200
11220
+ }
11221
+ },
11222
+ rows: {
11223
+ type: "array",
11224
+ maxItems: 5e3,
11225
+ items: {
11226
+ oneOf: [
11227
+ {
11228
+ $ref: "#/$defs/cells"
11229
+ },
11230
+ {
11231
+ $ref: "#/$defs/dataRow"
11232
+ },
11233
+ {
11234
+ $ref: "#/$defs/structuralRow"
11235
+ }
11236
+ ]
11237
+ }
11238
+ },
11239
+ relations: {
11240
+ type: "array",
11241
+ maxItems: 2e3,
11242
+ items: {
11243
+ $ref: "#/$defs/relation"
11244
+ }
11245
+ }
11246
+ }
11247
+ },
11248
+ {
11249
+ description: "A planning timeline: a calendar range and an ordered list of rows. Never coordinates, colours or a current date \u2014 the renderer derives those.",
11250
+ type: "object",
11251
+ additionalProperties: false,
11252
+ required: [
11253
+ "time",
11254
+ "rows"
11255
+ ],
11256
+ properties: {
11257
+ title: {
11258
+ $ref: "#/$defs/label"
11259
+ },
11260
+ time: {
11261
+ type: "object",
11262
+ additionalProperties: false,
11263
+ required: [
11264
+ "start",
11265
+ "end",
11266
+ "scale"
11267
+ ],
11268
+ properties: {
11269
+ start: {
11270
+ $ref: "#/$defs/calendarDate"
11271
+ },
11272
+ end: {
11273
+ $ref: "#/$defs/calendarDate"
11274
+ },
11275
+ scale: {
11276
+ enum: [
11277
+ "week",
11278
+ "month",
11279
+ "quarter"
11280
+ ]
11281
+ }
11282
+ }
11283
+ },
11284
+ rows: {
11285
+ type: "array",
11286
+ maxItems: 500,
11287
+ items: {
11288
+ $ref: "#/$defs/timelineRow"
11289
+ }
11290
+ },
11291
+ dependencies: {
11292
+ type: "array",
11293
+ maxItems: 2e3,
11294
+ items: {
11295
+ $ref: "#/$defs/timelineDependency"
11296
+ }
11297
+ },
11298
+ comparedTo: {
11299
+ description: "The plan this timeline is compared with. Its presence makes the timeline a comparison: items may then carry previous dates and roles.",
11300
+ type: "object",
11301
+ additionalProperties: false,
11302
+ required: [
11303
+ "label"
11304
+ ],
11305
+ properties: {
11306
+ label: {
11307
+ type: "string",
11308
+ minLength: 1,
11309
+ maxLength: 500
11310
+ },
11311
+ date: {
11312
+ $ref: "#/$defs/calendarDate"
11313
+ }
11314
+ }
11315
+ },
11316
+ periods: {
11317
+ type: "array",
11318
+ maxItems: 100,
11319
+ items: {
11320
+ $ref: "#/$defs/timelinePeriod"
11321
+ }
11322
+ },
11323
+ references: {
11324
+ type: "array",
11325
+ maxItems: 50,
11326
+ items: {
11327
+ $ref: "#/$defs/timelineReference"
11328
+ }
11329
+ }
11330
+ }
11331
+ }
11332
+ ]
11333
+ }
11334
+ }
11335
+ };
11336
+
11337
+ // shared/src/structuredExchangeDocument.ts
11338
+ var STRUCTURED_EXCHANGE_SCHEMA_PREFIX = "urn:structured-exchange:";
11339
+ function declaredSchemaOf(document) {
11340
+ if (typeof document !== "object" || document === null || Array.isArray(document)) return void 0;
11341
+ const schema2 = document.schema;
11342
+ if (typeof schema2 !== "string" || !schema2.startsWith(STRUCTURED_EXCHANGE_SCHEMA_PREFIX)) return void 0;
11343
+ return schema2;
11344
+ }
11345
+
11346
+ // shared/src/structuredExchangeSchemaNode.ts
11347
+ function unwrapSchemaModule(module) {
11348
+ const raw = module;
11349
+ const looksLikeSchema = "$id" in raw || "type" in raw;
11350
+ const root = looksLikeSchema ? raw : raw.default;
11351
+ if (root.default !== root) return root;
11352
+ const { default: _interop, ...rest } = root;
11353
+ return rest;
11354
+ }
11355
+ var schemas = {
11356
+ "urn:structured-exchange:1": unwrapSchemaModule(structured_exchange_1_default),
11357
+ "urn:structured-exchange:2": unwrapSchemaModule(structured_exchange_2_default),
11358
+ "urn:structured-exchange:3": unwrapSchemaModule(structured_exchange_3_default)
11359
+ };
11360
+ var schema = schemas[STRUCTURED_EXCHANGE_SCHEMA_V1];
11361
+ var compiled = /* @__PURE__ */ new Map();
11362
+ function schemaFor(version, kind) {
11363
+ const published = schemas[version];
11364
+ const variants = published.properties.data?.oneOf ?? [];
11365
+ const required = {
11366
+ graph: "nodes",
11367
+ sequence: "participants",
11368
+ table: "columns",
11369
+ timeline: "time"
11370
+ };
11371
+ const marker = typeof kind === "string" ? required[kind] : void 0;
11372
+ const variant = marker === void 0 ? void 0 : variants.find((candidate) => candidate.properties?.[marker] !== void 0);
11373
+ if (variant === void 0) return published;
11374
+ return {
11375
+ ...published,
11376
+ properties: { ...published.properties, data: variant }
11377
+ };
11378
+ }
11379
+ var NARROWABLE_KINDS = ["graph", "sequence", "table", "timeline"];
11380
+ function validator(version, kind) {
11381
+ const narrowable = typeof kind === "string" && NARROWABLE_KINDS.includes(kind);
11382
+ const key = `${version}:${narrowable ? kind : "*"}`;
11383
+ const existing = compiled.get(key);
11384
+ if (existing !== void 0) return existing;
11385
+ const built = Compile(schemaFor(version, narrowable ? kind : void 0));
11386
+ compiled.set(key, built);
11387
+ return built;
11388
+ }
11389
+ var TIMELINE_BRANCHES = {
11390
+ task: "timelineTask",
11391
+ separator: "timelineSeparator",
11392
+ activity: "timelineActivity",
11393
+ milestone: "timelineMilestone"
11394
+ };
11395
+ var branchValidators = /* @__PURE__ */ new Map();
11396
+ function branchValidator(definition) {
11397
+ const existing = branchValidators.get(definition);
11398
+ if (existing !== void 0) return existing;
11399
+ const defs = schemas[STRUCTURED_EXCHANGE_SCHEMA_V3].$defs;
11400
+ const built = Compile({ $defs: defs, $ref: `#/$defs/${definition}` });
11401
+ branchValidators.set(definition, built);
11402
+ return built;
11403
+ }
11404
+ function expandTimelineBranches(document, errors) {
11405
+ return errors.flatMap((error) => {
11406
+ if (error.keyword === "additionalProperties") return namedExtras(error);
11407
+ if (error.keyword === "enum") return [withAllowedValues(error)];
11408
+ if (error.keyword !== "if") return [error];
11409
+ const at = error.instancePath ?? "";
11410
+ const value = atPointer(document, at);
11411
+ const definition = typeof value?.type === "string" ? TIMELINE_BRANCHES[value.type] : void 0;
11412
+ if (definition === void 0) return [error];
11413
+ const inner = [...branchValidator(definition).Errors(value)].map((found) => ({
11414
+ ...found,
11415
+ instancePath: `${at}${found.instancePath ?? ""}`
11416
+ }));
11417
+ return inner.length === 0 ? [error] : expandTimelineBranches(document, inner);
11418
+ });
11419
+ }
11420
+ function withAllowedValues(error) {
11421
+ const allowed = error.params?.allowedValues;
11422
+ if (!Array.isArray(allowed)) return error;
11423
+ return { ...error, message: `must be one of ${allowed.map((value) => JSON.stringify(value)).join(", ")}` };
11424
+ }
11425
+ var PRESENTATION_NAMES = /* @__PURE__ */ new Set(["x", "y", "color", "colour", "fill", "stroke", "shape", "width", "height", "position", "style", "dash"]);
11426
+ function namedExtras(error) {
11427
+ const names2 = error.params?.additionalProperties;
11428
+ if (!Array.isArray(names2) || names2.length === 0) return [error];
11429
+ return names2.map(
11430
+ (name) => ({
11431
+ ...error,
11432
+ instancePath: `${error.instancePath ?? ""}/${String(name).replace(/~/g, "~0").replace(/\//g, "~1")}`,
11433
+ message: PRESENTATION_NAMES.has(String(name).toLowerCase()) ? `"${String(name)}" is not defined here; a timeline carries what the plan means, and the renderer derives positions, colours and shapes` : `"${String(name)}" is not defined here`
11434
+ })
11435
+ );
11436
+ }
11437
+ function observedFor(keyword, value) {
11438
+ if (typeof value === "string" && (keyword === "maxLength" || keyword === "minLength")) return value.length;
11439
+ if (Array.isArray(value) && (keyword === "maxItems" || keyword === "minItems")) return value.length;
11440
+ return void 0;
11441
+ }
11442
+ function atPointer(document, pointer) {
11443
+ if (pointer === "") return document;
11444
+ let current = document;
11445
+ for (const rawSegment of pointer.slice(1).split("/")) {
11446
+ if (current === null || typeof current !== "object") return void 0;
11447
+ const segment4 = rawSegment.replace(/~1/g, "/").replace(/~0/g, "~");
11448
+ current = current[segment4];
11449
+ }
11450
+ return current;
11451
+ }
11452
+ var checkStructuredExchangeSchema = (document) => {
11453
+ const declared = declaredSchemaOf(document);
11454
+ const version = supportedSchemaOf(declared);
11455
+ if (version === void 0 && declared !== void 0) {
11456
+ return [
11457
+ {
11458
+ rule: "unsupported-version",
11459
+ path: "/schema",
11460
+ message: `this build validates ${STRUCTURED_EXCHANGE_SUPPORTED_SCHEMAS.join(" and ")}`
11461
+ }
11462
+ ];
11463
+ }
11464
+ const issues = [];
11465
+ const contract = version ?? STRUCTURED_EXCHANGE_SCHEMA_V1;
11466
+ const published = [...validator(contract, void 0).Errors(document)];
11467
+ if (published.length === 0) return issues;
11468
+ const kind = document?.kind;
11469
+ if (contract === STRUCTURED_EXCHANGE_SCHEMA_V3 && typeof kind === "string" && ["graph", "sequence", "table"].includes(kind)) {
11470
+ const asVersionTwo = checkStructuredExchangeSchema({
11471
+ ...document,
11472
+ schema: STRUCTURED_EXCHANGE_SCHEMA_V2
11473
+ });
11474
+ if (asVersionTwo.length > 0) return asVersionTwo;
11475
+ }
11476
+ const narrowed = [...validator(contract, kind).Errors(document)];
11477
+ const seen = new Set(narrowed.map((error) => `${error.keyword}@${error.instancePath}`));
11478
+ const timeline = contract === STRUCTURED_EXCHANGE_SCHEMA_V3 && kind === "timeline";
11479
+ const combined = timeline ? expandTimelineBranches(document, narrowed) : [...narrowed, ...published.filter((error) => !seen.has(`${error.keyword}@${error.instancePath}`))];
11480
+ for (const error of combined) {
11481
+ const keyword = String(error.keyword ?? "schema");
11482
+ const limit = error.params?.limit;
11483
+ issues.push({
11484
+ rule: `schema/${keyword}`,
11485
+ path: error.instancePath ?? "",
11486
+ message: error.message ?? "does not conform to the published schema",
11487
+ ...limit !== void 0 ? { limit, level: "ceiling" } : {},
11488
+ ...(() => {
11489
+ const observed = observedFor(keyword, atPointer(document, error.instancePath ?? ""));
11490
+ return observed === void 0 ? {} : { observed };
11491
+ })()
11492
+ });
11493
+ }
11494
+ return issues.sort((left, right) => right.path.split("/").length - left.path.split("/").length);
11495
+ };
11496
+
11497
+ // shared/src/structuredExchangeProfile.ts
11498
+ var STRUCTURED_EXCHANGE_PROFILE_REGISTRY_SCHEMA_V2 = "urn:structured-exchange-profile-registry:2";
11499
+ var STRUCTURED_EXCHANGE_PROFILE_REGISTRY_PATH = ".pi-outpost/structured-exchange.json";
11500
+ var STRUCTURED_EXCHANGE_CONFORMITY_REPORT_PROFILE = "urn:structured-exchange-conformity-report:1";
11501
+ var STRUCTURED_EXCHANGE_RULES_REGISTER_PROFILE = "urn:structured-exchange-rules-register:1";
11502
+ var STRUCTURED_EXCHANGE_RULE_PATTERNS_PROFILE = "urn:structured-exchange-rule-patterns:1";
11503
+ var RESERVED_PROFILE_IDENTIFIERS = /* @__PURE__ */ new Set([
11504
+ STRUCTURED_EXCHANGE_CONFORMITY_REPORT_PROFILE,
11505
+ STRUCTURED_EXCHANGE_RULES_REGISTER_PROFILE,
11506
+ STRUCTURED_EXCHANGE_RULE_PATTERNS_PROFILE
11507
+ ]);
11508
+ var STRUCTURED_EXCHANGE_PROFILE_CEILINGS = {
11509
+ /** A profile's identifier, bounded like the `profile` a document names it by. */
11510
+ id: 200,
11511
+ label: 500,
11512
+ description: 2e3,
11513
+ /** Kinds per vocabulary, as a document's `kindsPerVocabulary`. */
11514
+ kindsPerVocabulary: 64,
11515
+ /** Attributes one kind declares, as a document's `attributesPerItem`. */
11516
+ attributesPerKind: 50,
11517
+ attributeName: 200,
9852
11518
  /**
9853
11519
  * Values one enumeration lists. Generous: a requirements tool's enumerations
9854
11520
  * include long ones — components, suppliers — and a profile that cannot hold its
@@ -10190,9 +11856,106 @@ var structured_exchange_profile_registry_1_default = {
10190
11856
  }
10191
11857
  };
10192
11858
 
11859
+ // shared/schemas/structured-exchange-profile-registry-2.json
11860
+ var structured_exchange_profile_registry_2_default = {
11861
+ $schema: "https://json-schema.org/draft/2020-12/schema",
11862
+ $id: "urn:structured-exchange-profile-registry:2",
11863
+ title: "Structured exchange profile registry v2",
11864
+ description: "Version 1 plus the project's appearance: colours for the kinds its documents use, applied wherever a kind is coloured and never written into a document. Profiles become optional, so a project may declare only its colours. Kept at .pi-outpost/structured-exchange.json in the project.",
11865
+ type: "object",
11866
+ additionalProperties: false,
11867
+ required: [
11868
+ "schema"
11869
+ ],
11870
+ properties: {
11871
+ schema: {
11872
+ description: "Which registry format this is.",
11873
+ const: "urn:structured-exchange-profile-registry:2"
11874
+ },
11875
+ profiles: {
11876
+ description: "Profile files, by path relative to the project directory. Each must stay inside the project. May be empty or absent: a registry without profiles constrains no document.",
11877
+ type: "array",
11878
+ minItems: 0,
11879
+ maxItems: 20,
11880
+ items: {
11881
+ type: "string",
11882
+ minLength: 1,
11883
+ maxLength: 500
11884
+ }
11885
+ },
11886
+ rules: {
11887
+ description: "Rules files, by path relative to the project directory. Each names the registered profile it applies to, and is checked against that profile's vocabulary whenever it is read.",
11888
+ type: "array",
11889
+ maxItems: 20,
11890
+ items: {
11891
+ type: "string",
11892
+ minLength: 1,
11893
+ maxLength: 500
11894
+ }
11895
+ },
11896
+ default: {
11897
+ description: "The identifier of a registered profile that documents naming no profile are held to. Declaring one also refuses documents that name an unregistered profile, and version 1 documents.",
11898
+ type: "string",
11899
+ minLength: 1,
11900
+ maxLength: 200
11901
+ },
11902
+ appearance: {
11903
+ description: "The project's colours for kinds, by vocabulary. A name in one map does not colour the other vocabulary; a kind not named keeps its automatic colour.",
11904
+ type: "object",
11905
+ additionalProperties: false,
11906
+ properties: {
11907
+ kinds: {
11908
+ type: "object",
11909
+ maxProperties: 64,
11910
+ propertyNames: {
11911
+ type: "string",
11912
+ minLength: 1,
11913
+ maxLength: 100
11914
+ },
11915
+ additionalProperties: {
11916
+ $ref: "#/$defs/kindAppearance"
11917
+ },
11918
+ description: "Kinds of elements, participants, table rows and timeline items."
11919
+ },
11920
+ relationshipKinds: {
11921
+ type: "object",
11922
+ maxProperties: 64,
11923
+ propertyNames: {
11924
+ type: "string",
11925
+ minLength: 1,
11926
+ maxLength: 100
11927
+ },
11928
+ additionalProperties: {
11929
+ $ref: "#/$defs/kindAppearance"
11930
+ },
11931
+ description: "Kinds of relationships and table relations."
11932
+ }
11933
+ }
11934
+ }
11935
+ },
11936
+ $defs: {
11937
+ kindAppearance: {
11938
+ description: "How one kind is drawn. Colour only; the renderer derives lighter fills from it.",
11939
+ type: "object",
11940
+ additionalProperties: false,
11941
+ required: [
11942
+ "color"
11943
+ ],
11944
+ properties: {
11945
+ color: {
11946
+ description: "A six-digit hexadecimal colour, #rrggbb.",
11947
+ type: "string",
11948
+ pattern: "^#[0-9a-fA-F]{6}$"
11949
+ }
11950
+ }
11951
+ }
11952
+ }
11953
+ };
11954
+
10193
11955
  // shared/src/structuredExchangeProfileValidation.ts
10194
11956
  var profileCheck;
10195
11957
  var registryCheck;
11958
+ var registryCheckV2;
10196
11959
  function segment(name) {
10197
11960
  return name.replace(/~/g, "~0").replace(/\//g, "~1");
10198
11961
  }
@@ -10225,27 +11988,27 @@ function profileRuleIssues(profile) {
10225
11988
  for (const list of ["elementKinds", "relationshipKinds"]) {
10226
11989
  const seenKinds = /* @__PURE__ */ new Map();
10227
11990
  (profile[list] ?? []).forEach((declaration, k) => {
10228
- const at2 = `/${list}/${k}`;
11991
+ const at = `/${list}/${k}`;
10229
11992
  declared[list].add(declaration.kind);
10230
11993
  const firstKind = seenKinds.get(declaration.kind);
10231
11994
  if (firstKind === void 0) seenKinds.set(declaration.kind, k);
10232
11995
  else {
10233
11996
  issues.push({
10234
11997
  rule: "profile-format/duplicate-kind",
10235
- path: `${at2}/kind`,
11998
+ path: `${at}/kind`,
10236
11999
  message: `kind "${declaration.kind}" is already declared at /${list}/${firstKind}; declare each kind once, with all its attributes`
10237
12000
  });
10238
12001
  }
10239
12002
  const seenAttributes = /* @__PURE__ */ new Map();
10240
12003
  (declaration.attributes ?? []).forEach((attribute, a) => {
10241
- const attributeAt = `${at2}/attributes/${a}`;
12004
+ const attributeAt = `${at}/attributes/${a}`;
10242
12005
  const firstAttribute = seenAttributes.get(attribute.name);
10243
12006
  if (firstAttribute === void 0) seenAttributes.set(attribute.name, a);
10244
12007
  else {
10245
12008
  issues.push({
10246
12009
  rule: "profile-format/duplicate-attribute",
10247
12010
  path: `${attributeAt}/name`,
10248
- message: `kind "${declaration.kind}" already declares attribute "${attribute.name}" at ${at2}/attributes/${firstAttribute}`
12011
+ message: `kind "${declaration.kind}" already declares attribute "${attribute.name}" at ${at}/attributes/${firstAttribute}`
10249
12012
  });
10250
12013
  }
10251
12014
  if (attribute.type === "enumeration") {
@@ -10290,12 +12053,12 @@ function profileRuleIssues(profile) {
10290
12053
  for (const end of ["from", "to"]) {
10291
12054
  const seen = /* @__PURE__ */ new Map();
10292
12055
  declaration[end]?.forEach((kind, position) => {
10293
- const at2 = `/relationshipKinds/${k}/${end}/${position}`;
12056
+ const at = `/relationshipKinds/${k}/${end}/${position}`;
10294
12057
  const first = seen.get(kind);
10295
12058
  if (first !== void 0) {
10296
12059
  issues.push({
10297
12060
  rule: "profile-format/repeated-end-kind",
10298
- path: at2,
12061
+ path: at,
10299
12062
  message: `relationship kind "${declaration.kind}" already names "${kind}" at /relationshipKinds/${k}/${end}/${first}`
10300
12063
  });
10301
12064
  return;
@@ -10304,7 +12067,7 @@ function profileRuleIssues(profile) {
10304
12067
  if (declared.elementKinds.has(kind)) return;
10305
12068
  issues.push({
10306
12069
  rule: "profile-format/unresolved-end-kind",
10307
- path: at2,
12070
+ path: at,
10308
12071
  message: `relationship kind "${declaration.kind}" allows element kind "${kind}" at its ${end === "from" ? "source" : "target"}, which this profile does not declare; it declares ${declaredElementKinds}` + (declared.relationshipKinds.has(kind) ? ` ("${kind}" is a relationship kind here, and a relationship joins elements)` : "")
10309
12072
  });
10310
12073
  });
@@ -10312,20 +12075,20 @@ function profileRuleIssues(profile) {
10312
12075
  });
10313
12076
  const seenViewpoints = /* @__PURE__ */ new Map();
10314
12077
  (profile.viewpoints ?? []).forEach((viewpoint, index3) => {
10315
- const at2 = `/viewpoints/${index3}`;
12078
+ const at = `/viewpoints/${index3}`;
10316
12079
  const first = seenViewpoints.get(viewpoint.id);
10317
12080
  if (first === void 0) seenViewpoints.set(viewpoint.id, index3);
10318
12081
  else {
10319
12082
  issues.push({
10320
12083
  rule: "profile-format/duplicate-viewpoint-identifier",
10321
- path: `${at2}/id`,
12084
+ path: `${at}/id`,
10322
12085
  message: `viewpoint "${viewpoint.id}" is already declared at /viewpoints/${first}`
10323
12086
  });
10324
12087
  }
10325
12088
  if (viewpoint.elementKinds === void 0 && viewpoint.relationshipKinds === void 0) {
10326
12089
  issues.push({
10327
12090
  rule: "profile-format/empty-viewpoint",
10328
- path: at2,
12091
+ path: at,
10329
12092
  message: `viewpoint "${viewpoint.id}" retains no kind; name elementKinds, relationshipKinds, or both`
10330
12093
  });
10331
12094
  }
@@ -10338,7 +12101,7 @@ function profileRuleIssues(profile) {
10338
12101
  if (declared[list].has(kind)) return;
10339
12102
  issues.push({
10340
12103
  rule: "profile-format/unresolved-viewpoint-kind",
10341
- path: `${at2}/${list}/${position}`,
12104
+ path: `${at}/${list}/${position}`,
10342
12105
  message: `viewpoint "${viewpoint.id}" retains ${noun} kind "${kind}", which this profile does not declare` + (declared[otherList].has(kind) ? ` (it is a ${otherNoun} kind here, which is a different vocabulary)` : "")
10343
12106
  });
10344
12107
  });
@@ -10356,19 +12119,62 @@ function validateProfile(value) {
10356
12119
  return rules.length > 0 ? { valid: false, issues: rules } : { valid: true, profile, issues: [] };
10357
12120
  }
10358
12121
  function validateRegistry(value) {
10359
- registryCheck ??= Compile(unwrapSchemaModule(structured_exchange_profile_registry_1_default));
10360
- const schema2 = schemaIssues(registryCheck, value, "registry");
10361
- if (schema2.length > 0) return { valid: false, issues: schema2 };
12122
+ const declared = value?.schema;
12123
+ let check;
12124
+ if (declared === STRUCTURED_EXCHANGE_PROFILE_REGISTRY_SCHEMA_V2) {
12125
+ registryCheckV2 ??= Compile(unwrapSchemaModule(structured_exchange_profile_registry_2_default));
12126
+ check = registryCheckV2;
12127
+ } else {
12128
+ registryCheck ??= Compile(unwrapSchemaModule(structured_exchange_profile_registry_1_default));
12129
+ check = registryCheck;
12130
+ }
12131
+ const schema2 = schemaIssues(check, value, "registry");
12132
+ if (schema2.length > 0) return { valid: false, issues: preciseAppearanceIssues(value, schema2) };
10362
12133
  const registry = value;
10363
12134
  const issues = [];
10364
12135
  const seenPaths = /* @__PURE__ */ new Map();
10365
- registry.profiles.forEach((listed, index3) => {
12136
+ (registry.profiles ?? []).forEach((listed, index3) => {
10366
12137
  const first = seenPaths.get(listed);
10367
12138
  if (first === void 0) seenPaths.set(listed, index3);
10368
12139
  else issues.push({ rule: "registry/repeated-profile-path", path: `/profiles/${index3}`, message: `"${listed}" is already listed at /profiles/${first}` });
10369
12140
  });
10370
12141
  return issues.length > 0 ? { valid: false, issues } : { valid: true, registry, issues: [] };
10371
12142
  }
12143
+ function preciseAppearanceIssues(value, issues) {
12144
+ const appearance = value?.appearance;
12145
+ if (appearance === null || typeof appearance !== "object") return issues;
12146
+ const precise = [];
12147
+ const explained = /* @__PURE__ */ new Set();
12148
+ for (const vocabulary of ["kinds", "relationshipKinds"]) {
12149
+ const map = appearance[vocabulary];
12150
+ if (map === null || typeof map !== "object" || Array.isArray(map)) continue;
12151
+ const at = `/appearance/${vocabulary}`;
12152
+ for (const [kind, entry] of Object.entries(map)) {
12153
+ const where = `${at}/${segment(kind)}`;
12154
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
12155
+ precise.push({ rule: "registry/schema/type", path: where, message: `the appearance of "${kind}" must be an object declaring a color` });
12156
+ explained.add(at);
12157
+ continue;
12158
+ }
12159
+ const { color, ...rest } = entry;
12160
+ if (typeof color !== "string" || !/^#[0-9a-fA-F]{6}$/.test(color)) {
12161
+ precise.push({
12162
+ rule: "registry/appearance/color",
12163
+ path: `${where}/color`,
12164
+ message: `the colour of "${kind}" must be six hexadecimal digits after "#", such as "#dc2626"; got ${JSON.stringify(color)}`
12165
+ });
12166
+ explained.add(at);
12167
+ }
12168
+ for (const name of Object.keys(rest)) {
12169
+ precise.push({ rule: "registry/schema/additionalProperties", path: `${where}/${segment(name)}`, message: `"${name}" is not part of a kind's appearance; only "color" is` });
12170
+ explained.add(at);
12171
+ }
12172
+ }
12173
+ }
12174
+ if (precise.length === 0) return issues;
12175
+ const within = (issue) => [...explained].some((map) => issue.path === map || issue.path.startsWith(`${map}/`));
12176
+ return [...issues.filter((issue) => !within(issue)), ...precise];
12177
+ }
10372
12178
  function registryConsistencyIssues(registry, loaded) {
10373
12179
  const issues = [];
10374
12180
  const firstById = /* @__PURE__ */ new Map();
@@ -10384,7 +12190,7 @@ function registryConsistencyIssues(registry, loaded) {
10384
12190
  }
10385
12191
  });
10386
12192
  if (registry.default !== void 0 && !firstById.has(registry.default)) {
10387
- const registered = [...firstById.keys()].map((id) => `"${id}"`).join(", ");
12193
+ const registered = firstById.size === 0 ? "none" : [...firstById.keys()].map((id) => `"${id}"`).join(", ");
10388
12194
  issues.push({
10389
12195
  rule: "registry/unregistered-default",
10390
12196
  path: "/default",
@@ -10592,7 +12398,7 @@ function selectProfile(envelope, context) {
10592
12398
  } else {
10593
12399
  selected = fallback;
10594
12400
  }
10595
- if (selected === void 0 || kind === "sequence") return { outcome: "unconstrained" };
12401
+ if (selected === void 0 || kind === "sequence" || kind === "timeline") return { outcome: "unconstrained" };
10596
12402
  return { outcome: "held", profile: selected };
10597
12403
  }
10598
12404
  function checkAgainstProfile(envelope, profile, options = {}) {
@@ -10663,18 +12469,18 @@ function checkAgainstProfile(envelope, profile, options = {}) {
10663
12469
  if (Array.isArray(value)) value.forEach((item, index3) => checkScalar(attribute, kind, item, `${path3}/${index3}`));
10664
12470
  else checkScalar(attribute, kind, value, path3);
10665
12471
  }
10666
- function checkItem(item, at2, vocabulary, noun) {
12472
+ function checkItem(item, at, vocabulary, noun) {
10667
12473
  const kinds = vocabularies[vocabulary];
10668
12474
  const other = vocabularies[vocabulary === "element" ? "relationship" : "element"];
10669
12475
  const set = item.set ?? void 0;
10670
12476
  const setKind = typeof set?.kind === "string" ? set.kind : void 0;
10671
12477
  const ownKind = typeof item.kind === "string" ? item.kind : void 0;
10672
12478
  const kind = setKind ?? ownKind;
10673
- const kindAt = setKind !== void 0 ? `${at2}/set/kind` : `${at2}/kind`;
12479
+ const kindAt = setKind !== void 0 ? `${at}/set/kind` : `${at}/kind`;
10674
12480
  if (kind === void 0) {
10675
12481
  issues.push({
10676
12482
  rule: "profile/missing-kind",
10677
- path: at2,
12483
+ path: at,
10678
12484
  message: `this ${noun} carries no kind; profile "${profile.id}" holds every ${noun} to one of its ${vocabulary} kinds: ${quoted([...kinds.keys()])}` + (item.ref !== void 0 ? ". A changed item states its kind too, so its attributes can be checked" : "")
10679
12485
  });
10680
12486
  return;
@@ -10705,8 +12511,8 @@ function checkAgainstProfile(envelope, profile, options = {}) {
10705
12511
  checkValue(attribute, kind, value, path3);
10706
12512
  }
10707
12513
  };
10708
- checkAttributes(item.attributes, `${at2}/attributes`);
10709
- checkAttributes(set?.attributes, `${at2}/set/attributes`);
12514
+ checkAttributes(item.attributes, `${at}/attributes`);
12515
+ checkAttributes(set?.attributes, `${at}/set/attributes`);
10710
12516
  const required = (declaration.attributes ?? []).filter((attribute) => attribute.required === true);
10711
12517
  const whole = !proposal || item.ref === void 0;
10712
12518
  if (whole) {
@@ -10715,7 +12521,7 @@ function checkAgainstProfile(envelope, profile, options = {}) {
10715
12521
  if (Object.hasOwn(carried, attribute.name)) continue;
10716
12522
  issues.push({
10717
12523
  rule: "profile/missing-required-attribute",
10718
- path: at2,
12524
+ path: at,
10719
12525
  message: `kind "${kind}" requires attribute "${attribute.name}", and this ${noun} does not carry it`
10720
12526
  });
10721
12527
  }
@@ -10726,7 +12532,7 @@ function checkAgainstProfile(envelope, profile, options = {}) {
10726
12532
  if (!required.some((attribute) => attribute.name === name)) return;
10727
12533
  issues.push({
10728
12534
  rule: "profile/required-attribute-removed",
10729
- path: `${at2}/set/removeAttributes/${index3}`,
12535
+ path: `${at}/set/removeAttributes/${index3}`,
10730
12536
  message: `attribute "${String(name)}" is required on kind "${kind}", so a proposal cannot remove it`
10731
12537
  });
10732
12538
  });
@@ -10952,17 +12758,17 @@ function formIssues(file) {
10952
12758
  const issues = [];
10953
12759
  const seen = /* @__PURE__ */ new Map();
10954
12760
  file.rules.forEach((rule, index3) => {
10955
- const at2 = `/rules/${index3}`;
12761
+ const at = `/rules/${index3}`;
10956
12762
  const first = seen.get(rule.id);
10957
12763
  if (first === void 0) seen.set(rule.id, index3);
10958
12764
  else {
10959
- issues.push({ rule: "rules-format/duplicate-rule-identifier", path: `${at2}/id`, message: `rule "${rule.id}" is already declared at /rules/${first}` });
12765
+ issues.push({ rule: "rules-format/duplicate-rule-identifier", path: `${at}/id`, message: `rule "${rule.id}" is already declared at /rules/${first}` });
10960
12766
  }
10961
12767
  const { element, relationship } = rule;
10962
12768
  if (element === void 0 === (relationship === void 0)) {
10963
12769
  issues.push({
10964
12770
  rule: "rules-format/rule-form",
10965
- path: at2,
12771
+ path: at,
10966
12772
  message: `rule "${rule.id}" must name exactly one of "element" (an item rule) or "relationship" (a link rule)`
10967
12773
  });
10968
12774
  return;
@@ -10973,7 +12779,7 @@ function formIssues(file) {
10973
12779
  ];
10974
12780
  for (const [name, clause] of clauses) {
10975
12781
  if (clause === void 0 || clause === "forbidden") continue;
10976
- const clauseAt = `${at2}/${name}`;
12782
+ const clauseAt = `${at}/${name}`;
10977
12783
  if (!isConditionSet(clause)) continue;
10978
12784
  if (name === "then" && Object.keys(clause).length === 0) {
10979
12785
  issues.push({ rule: "rules-format/empty-then", path: clauseAt, message: `rule "${rule.id}" requires nothing; name conditions, or "forbidden"` });
@@ -11035,9 +12841,9 @@ function rulesAgainstProfile(file, profile) {
11035
12841
  const issues = [];
11036
12842
  const elementKinds = new Map((profile.elementKinds ?? []).map((declaration) => [declaration.kind, declaration]));
11037
12843
  const relationshipKinds = new Map((profile.relationshipKinds ?? []).map((declaration) => [declaration.kind, declaration]));
11038
- const checkConditions = (rule, conditions, at2, kinds, declaredEnd) => {
12844
+ const checkConditions = (rule, conditions, at, kinds, declaredEnd) => {
11039
12845
  for (const [name, values] of Object.entries(conditions ?? {})) {
11040
- const attributeAt = `${at2}/${segment3(name)}`;
12846
+ const attributeAt = `${at}/${segment3(name)}`;
11041
12847
  const declarations = kinds.map((kind) => (elementKinds.get(kind)?.attributes ?? []).find((attribute) => attribute.name === name)).filter((attribute) => attribute !== void 0);
11042
12848
  if (declarations.length === 0) {
11043
12849
  issues.push({
@@ -11068,24 +12874,24 @@ function rulesAgainstProfile(file, profile) {
11068
12874
  }
11069
12875
  };
11070
12876
  file.rules.forEach((rule, index3) => {
11071
- const at2 = `/rules/${index3}`;
12877
+ const at = `/rules/${index3}`;
11072
12878
  if (rule.element !== void 0) {
11073
12879
  if (!elementKinds.has(rule.element)) {
11074
12880
  issues.push({
11075
12881
  rule: "rules-format/undeclared-kind",
11076
- path: `${at2}/element`,
12882
+ path: `${at}/element`,
11077
12883
  message: `rule "${rule.id}" applies to element kind "${rule.element}", which profile "${profile.id}" does not declare; it declares ${quoted2([...elementKinds.keys()])}`
11078
12884
  });
11079
12885
  return;
11080
12886
  }
11081
- checkConditions(rule, rule.when, `${at2}/when`, [rule.element]);
11082
- if (rule.then !== "forbidden") checkConditions(rule, rule.then, `${at2}/then`, [rule.element]);
12887
+ checkConditions(rule, rule.when, `${at}/when`, [rule.element]);
12888
+ if (rule.then !== "forbidden") checkConditions(rule, rule.then, `${at}/then`, [rule.element]);
11083
12889
  return;
11084
12890
  }
11085
12891
  if (!relationshipKinds.has(rule.relationship)) {
11086
12892
  issues.push({
11087
12893
  rule: "rules-format/undeclared-kind",
11088
- path: `${at2}/relationship`,
12894
+ path: `${at}/relationship`,
11089
12895
  message: `rule "${rule.id}" applies to relationship kind "${rule.relationship}", which profile "${profile.id}" does not declare; it declares ${quoted2([...relationshipKinds.keys()])}`
11090
12896
  });
11091
12897
  return;
@@ -11101,7 +12907,7 @@ function rulesAgainstProfile(file, profile) {
11101
12907
  for (const [side, position] of [["from", "source"], ["to", "target"]]) {
11102
12908
  const allowed = declaration?.[side];
11103
12909
  const described = allowed === void 0 ? void 0 : `${position} of "${rule.relationship}"`;
11104
- checkConditions(rule, clause[side], `${at2}/${name}/${side}`, allowed ?? anyElement, described);
12910
+ checkConditions(rule, clause[side], `${at}/${name}/${side}`, allowed ?? anyElement, described);
11105
12911
  }
11106
12912
  }
11107
12913
  });
@@ -11207,7 +13013,7 @@ async function readProjectRegistry(projectRoot, registryPath = STRUCTURED_EXCHAN
11207
13013
  const files = [];
11208
13014
  const issues = [];
11209
13015
  const loaded = [];
11210
- for (const [index3, listed] of registry.profiles.entries()) {
13016
+ for (const [index3, listed] of (registry.profiles ?? []).entries()) {
11211
13017
  const read = await readConfinedJson(root, listed, STRUCTURED_EXCHANGE_PROFILE_CEILINGS.profileBytes, "profile-format");
11212
13018
  if (read.ok === "missing") {
11213
13019
  issues.push({ rule: "registry/missing-profile", path: `/profiles/${index3}`, message: `"${listed}" does not exist`, file: registryFile });
@@ -11269,7 +13075,8 @@ async function readProjectRegistry(projectRoot, registryPath = STRUCTURED_EXCHAN
11269
13075
  state: "usable",
11270
13076
  context: { profiles, ...registry.default !== void 0 ? { default: registry.default } : {}, ...rules.size > 0 ? { rules } : {} },
11271
13077
  files,
11272
- filesByProfile
13078
+ filesByProfile,
13079
+ ...registry.appearance !== void 0 ? { appearance: registry.appearance } : {}
11273
13080
  };
11274
13081
  }
11275
13082
 
@@ -12370,7 +14177,7 @@ function rulePatterns(profile, rules, sources) {
12370
14177
  }
12371
14178
 
12372
14179
  // shared/bin/validate-structured-exchange.mjs
12373
- var VERSION = `validate-structured-exchange ${false ? "development" : "0.32.4"}`;
14180
+ var VERSION = `validate-structured-exchange ${false ? "development" : "0.34.0"}`;
12374
14181
  var USAGE = `validate-structured-exchange [file]
12375
14182
  validate-structured-exchange --profile <profile.json> [file]
12376
14183
  validate-structured-exchange --check-profile <profile.json>