@aglyn/aglyn 1.0.0-beta.219 → 1.0.0-beta.221

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/package.json +12 -12
  2. package/src/lib/app-utils/actions.d.ts +1 -59
  3. package/src/lib/app-utils/actions.js +7 -72
  4. package/src/lib/app-utils/actions.js.map +1 -1
  5. package/src/lib/app-utils/consent-groups.d.ts +1 -1
  6. package/src/lib/app-utils/consent-groups.js.map +1 -1
  7. package/src/lib/app-utils/console-routes.d.ts +0 -36
  8. package/src/lib/app-utils/console-routes.js +2 -37
  9. package/src/lib/app-utils/console-routes.js.map +1 -1
  10. package/src/lib/app-utils/contacts.d.ts +1 -1
  11. package/src/lib/app-utils/contacts.js.map +1 -1
  12. package/src/lib/app-utils/container-membership.d.ts +93 -0
  13. package/src/lib/app-utils/container-membership.js +165 -0
  14. package/src/lib/app-utils/container-membership.js.map +1 -0
  15. package/src/lib/app-utils/crm-task-reminders.d.ts +23 -6
  16. package/src/lib/app-utils/crm-task-reminders.js +32 -11
  17. package/src/lib/app-utils/crm-task-reminders.js.map +1 -1
  18. package/src/lib/app-utils/crm.d.ts +24 -4
  19. package/src/lib/app-utils/crm.js +23 -0
  20. package/src/lib/app-utils/crm.js.map +1 -1
  21. package/src/lib/app-utils/csv-upload.d.ts +1 -1
  22. package/src/lib/app-utils/csv-upload.js +1 -1
  23. package/src/lib/app-utils/csv-upload.js.map +1 -1
  24. package/src/lib/app-utils/{dataset-csv.d.ts → csv.d.ts} +10 -43
  25. package/src/lib/app-utils/{dataset-csv.js → csv.js} +10 -81
  26. package/src/lib/app-utils/csv.js.map +1 -0
  27. package/src/lib/app-utils/datasets.d.ts +0 -19
  28. package/src/lib/app-utils/datasets.js +0 -13
  29. package/src/lib/app-utils/datasets.js.map +1 -1
  30. package/src/lib/app-utils/docs-help.generated.d.ts +4 -4
  31. package/src/lib/app-utils/docs-help.generated.js +5 -0
  32. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  33. package/src/lib/app-utils/docs-index.generated.js +71 -36
  34. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  35. package/src/lib/app-utils/dynamic-list-rule.js +3 -3
  36. package/src/lib/app-utils/dynamic-list-rule.js.map +1 -1
  37. package/src/lib/app-utils/email-text.d.ts +80 -0
  38. package/src/lib/app-utils/email-text.js +189 -0
  39. package/src/lib/app-utils/email-text.js.map +1 -0
  40. package/src/lib/app-utils/{list-assignment-policy.d.ts → enrollment-basis.d.ts} +4 -0
  41. package/src/lib/app-utils/{list-assignment-policy.js → enrollment-basis.js} +5 -1
  42. package/src/lib/app-utils/enrollment-basis.js.map +1 -0
  43. package/src/lib/app-utils/expand-repeatables.js +4 -4
  44. package/src/lib/app-utils/expand-repeatables.js.map +1 -1
  45. package/src/lib/app-utils/forms.js +2 -2
  46. package/src/lib/app-utils/forms.js.map +1 -1
  47. package/src/lib/app-utils/health-report.d.ts +2 -3
  48. package/src/lib/app-utils/health-report.js +30 -6
  49. package/src/lib/app-utils/health-report.js.map +1 -1
  50. package/src/lib/app-utils/inbound-mail-domain.d.ts +27 -0
  51. package/src/lib/app-utils/inbound-mail-domain.js +31 -0
  52. package/src/lib/app-utils/inbound-mail-domain.js.map +1 -0
  53. package/src/lib/app-utils/leaving-notice.d.ts +96 -0
  54. package/src/lib/app-utils/leaving-notice.js +157 -0
  55. package/src/lib/app-utils/leaving-notice.js.map +1 -0
  56. package/src/lib/app-utils/lockdown.d.ts +11 -0
  57. package/src/lib/app-utils/lockdown.js +33 -3
  58. package/src/lib/app-utils/lockdown.js.map +1 -1
  59. package/src/lib/app-utils/member-email-aliases.d.ts +25 -0
  60. package/src/lib/app-utils/member-email-aliases.js +29 -0
  61. package/src/lib/app-utils/member-email-aliases.js.map +1 -1
  62. package/src/lib/app-utils/notifications.d.ts +69 -0
  63. package/src/lib/app-utils/notifications.js +119 -0
  64. package/src/lib/app-utils/notifications.js.map +1 -1
  65. package/src/lib/app-utils/operator-alerts.d.ts +15 -1
  66. package/src/lib/app-utils/operator-alerts.js +64 -10
  67. package/src/lib/app-utils/operator-alerts.js.map +1 -1
  68. package/src/lib/app-utils/plan-entitlements.d.ts +164 -178
  69. package/src/lib/app-utils/plan-entitlements.js +200 -224
  70. package/src/lib/app-utils/plan-entitlements.js.map +1 -1
  71. package/src/lib/app-utils/platform-marketing-consent.d.ts +56 -2
  72. package/src/lib/app-utils/platform-marketing-consent.js +6 -5
  73. package/src/lib/app-utils/platform-marketing-consent.js.map +1 -1
  74. package/src/lib/app-utils/plugin-starter-templates.generated.d.ts +12 -0
  75. package/src/lib/app-utils/plugin-starter-templates.generated.js +711 -0
  76. package/src/lib/app-utils/plugin-starter-templates.generated.js.map +1 -0
  77. package/src/lib/app-utils/repeat-query.d.ts +56 -0
  78. package/src/lib/app-utils/{dataset-query.js → repeat-query.js} +16 -16
  79. package/src/lib/app-utils/repeat-query.js.map +1 -0
  80. package/src/lib/app-utils/screen-link-context-value.d.ts +7 -0
  81. package/src/lib/app-utils/screen-link-context-value.js.map +1 -1
  82. package/src/lib/app-utils/screen-link-context.js +14 -2
  83. package/src/lib/app-utils/screen-link-context.js.map +1 -1
  84. package/src/lib/app-utils/screen-route.d.ts +3 -0
  85. package/src/lib/app-utils/screen-route.js +6 -1
  86. package/src/lib/app-utils/screen-route.js.map +1 -1
  87. package/src/lib/app-utils/server.d.ts +7 -9
  88. package/src/lib/app-utils/server.js +18 -26
  89. package/src/lib/app-utils/server.js.map +1 -1
  90. package/src/lib/app-utils/starter-template-nodes.d.ts +137 -0
  91. package/src/lib/app-utils/starter-template-nodes.js +129 -0
  92. package/src/lib/app-utils/starter-template-nodes.js.map +1 -0
  93. package/src/lib/app-utils/starter-templates.d.ts +11 -34
  94. package/src/lib/app-utils/starter-templates.js +19 -230
  95. package/src/lib/app-utils/starter-templates.js.map +1 -1
  96. package/src/lib/app-utils/{email-topics.d.ts → subscription-topics.d.ts} +64 -48
  97. package/src/lib/app-utils/{email-topics.js → subscription-topics.js} +39 -109
  98. package/src/lib/app-utils/subscription-topics.js.map +1 -0
  99. package/src/lib/app-utils/usage-budget.d.ts +73 -188
  100. package/src/lib/app-utils/usage-budget.js +58 -194
  101. package/src/lib/app-utils/usage-budget.js.map +1 -1
  102. package/src/lib/app-utils/utm-attribution.d.ts +1 -1
  103. package/src/lib/app-utils/utm-attribution.js +1 -1
  104. package/src/lib/app-utils/utm-attribution.js.map +1 -1
  105. package/src/lib/app-utils/{campaign-touch.d.ts → utm-touch.d.ts} +21 -13
  106. package/src/lib/app-utils/{campaign-touch.js → utm-touch.js} +30 -22
  107. package/src/lib/app-utils/utm-touch.js.map +1 -0
  108. package/src/lib/app-utils/variables.d.ts +9 -9
  109. package/src/lib/app-utils/variables.js.map +1 -1
  110. package/src/lib/app-utils/visitor-record-ceiling.d.ts +32 -0
  111. package/src/lib/app-utils/visitor-record-ceiling.js +39 -0
  112. package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -1
  113. package/src/lib/foundation/definitions/org-billing.types.d.ts +2 -2
  114. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
  115. package/src/lib/plugin-manager/feature-plugins.d.ts +80 -79
  116. package/src/lib/plugin-manager/feature-plugins.js +22 -35
  117. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  118. package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +18 -1
  119. package/src/lib/plugin-manager/first-party-plugins.generated.js +62 -2
  120. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  121. package/src/lib/plugin-manager/index.d.ts +0 -1
  122. package/src/lib/plugin-manager/index.js +0 -1
  123. package/src/lib/plugin-manager/index.js.map +1 -1
  124. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +9 -4
  125. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
  126. package/src/lib/plugin-manager/plugin-containers.d.ts +68 -0
  127. package/src/lib/plugin-manager/plugin-containers.generated.d.ts +10 -0
  128. package/src/lib/plugin-manager/plugin-containers.generated.js +20 -0
  129. package/src/lib/plugin-manager/plugin-containers.generated.js.map +1 -0
  130. package/src/lib/plugin-manager/plugin-containers.js +27 -0
  131. package/src/lib/plugin-manager/plugin-containers.js.map +1 -0
  132. package/src/lib/plugin-manager/plugin-entitlements.d.ts +8 -0
  133. package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -1
  134. package/src/lib/plugin-manager/plugin-membership-detach.js +1 -1
  135. package/src/lib/plugin-manager/plugin-membership-detach.js.map +1 -1
  136. package/src/lib/plugin-manager/plugin-record-email-state.d.ts +69 -0
  137. package/src/lib/plugin-manager/plugin-record-email-state.js +32 -0
  138. package/src/lib/plugin-manager/plugin-record-email-state.js.map +1 -1
  139. package/src/lib/plugin-manager/plugin-record-lists.d.ts +115 -0
  140. package/src/lib/plugin-manager/plugin-record-lists.js +75 -0
  141. package/src/lib/plugin-manager/plugin-record-lists.js.map +1 -0
  142. package/src/lib/plugin-manager/plugin-record-timeline.d.ts +42 -8
  143. package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -1
  144. package/src/lib/plugin-manager/plugin-server-steps.d.ts +170 -0
  145. package/src/lib/plugin-manager/plugin-server-steps.js +116 -0
  146. package/src/lib/plugin-manager/plugin-server-steps.js.map +1 -0
  147. package/src/lib/plugin-manager/plugin-site-beacons.d.ts +71 -0
  148. package/src/lib/plugin-manager/plugin-site-beacons.js +54 -0
  149. package/src/lib/plugin-manager/plugin-site-beacons.js.map +1 -0
  150. package/src/lib/plugin-manager/plugin-subscription-topics.generated.d.ts +11 -0
  151. package/src/lib/plugin-manager/plugin-subscription-topics.generated.js +41 -0
  152. package/src/lib/plugin-manager/plugin-subscription-topics.generated.js.map +1 -0
  153. package/src/lib/plugin-manager/plugin-usage-axes.d.ts +212 -0
  154. package/src/lib/plugin-manager/plugin-usage-axes.js +82 -3
  155. package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -1
  156. package/src/lib/plugin-manager/plugin-usage-meters.d.ts +108 -0
  157. package/src/lib/plugin-manager/plugin-usage-meters.js +105 -0
  158. package/src/lib/plugin-manager/plugin-usage-meters.js.map +1 -0
  159. package/src/lib/plugin-manager/usage-alert-contributors.d.ts +76 -6
  160. package/src/lib/plugin-manager/usage-alert-contributors.js +23 -10
  161. package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -1
  162. package/src/lib/app-utils/assist-credits.d.ts +0 -482
  163. package/src/lib/app-utils/assist-credits.js +0 -510
  164. package/src/lib/app-utils/assist-credits.js.map +0 -1
  165. package/src/lib/app-utils/campaign-membership.d.ts +0 -121
  166. package/src/lib/app-utils/campaign-membership.js +0 -189
  167. package/src/lib/app-utils/campaign-membership.js.map +0 -1
  168. package/src/lib/app-utils/campaign-touch.js.map +0 -1
  169. package/src/lib/app-utils/console-record-links.d.ts +0 -84
  170. package/src/lib/app-utils/console-record-links.js +0 -76
  171. package/src/lib/app-utils/console-record-links.js.map +0 -1
  172. package/src/lib/app-utils/crm-booking.d.ts +0 -75
  173. package/src/lib/app-utils/crm-booking.js +0 -133
  174. package/src/lib/app-utils/crm-booking.js.map +0 -1
  175. package/src/lib/app-utils/crm-inbound.d.ts +0 -245
  176. package/src/lib/app-utils/crm-inbound.js +0 -407
  177. package/src/lib/app-utils/crm-inbound.js.map +0 -1
  178. package/src/lib/app-utils/crm-reports.d.ts +0 -479
  179. package/src/lib/app-utils/crm-reports.js +0 -680
  180. package/src/lib/app-utils/crm-reports.js.map +0 -1
  181. package/src/lib/app-utils/dataset-csv.js.map +0 -1
  182. package/src/lib/app-utils/dataset-query.d.ts +0 -57
  183. package/src/lib/app-utils/dataset-query.js.map +0 -1
  184. package/src/lib/app-utils/email-topics.js.map +0 -1
  185. package/src/lib/app-utils/list-assignment-policy.js.map +0 -1
  186. package/src/lib/plugin-manager/plugin-metered-lines.d.ts +0 -97
  187. package/src/lib/plugin-manager/plugin-metered-lines.js +0 -123
  188. package/src/lib/plugin-manager/plugin-metered-lines.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/plugin-starter-templates.generated.ts"],"sourcesContent":["/**\n * GENERATED FILE — do not edit. Regenerate with:\n * node tools/scripts/generate-plugin-manifests.mjs\n *\n * The starter sites plugins offer (AGL-3080): what each plugin's\n * `starterTemplates` entry returned when this file was generated, in\n * config order. `STARTER_TEMPLATES` in `starter-templates.ts` is the\n * platform's own starters followed by these.\n * Source of truth: plugins.config.json and the entries it names.\n */\n\nimport type { StarterTemplate } from './starter-template-nodes'\n\nexport const PLUGIN_STARTER_TEMPLATES: readonly StarterTemplate[] = [\n {\n \"id\": \"physical-shop\",\n \"displayName\": \"Shop (physical products)\",\n \"description\": \"Storefront starter: home with featured products, filterable shop, product page, cart, and customer accounts. After applying, set the Product page as the product template in Store settings.\",\n \"category\": \"Commerce\",\n \"screens\": [\n {\n \"key\": \"home\",\n \"displayName\": \"Home\",\n \"slug\": \"/\",\n \"seo\": {\n \"title\": \"Shop\",\n \"description\": \"Browse our products.\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ps_h_heroSection\",\n \"ps_h_gridSection\",\n \"ps_h_newsSection\"\n ]\n },\n \"ps_h_heroSection\": {\n \"$id\": \"ps_h_heroSection\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"xl\"\n },\n \"sx\": {\n \"paddingTop\": 10,\n \"paddingBottom\": 10\n },\n \"nodes\": [\n \"ps_h_hero\"\n ]\n },\n \"ps_h_hero\": {\n \"$id\": \"ps_h_hero\",\n \"componentId\": \"muiStack\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ps_h_heroSection\",\n \"props\": {\n \"spacing\": 2\n },\n \"sx\": {\n \"alignItems\": \"center\"\n },\n \"nodes\": [\n \"ps_h_heroTitle\",\n \"ps_h_heroSub\",\n \"ps_h_heroCta\"\n ]\n },\n \"ps_h_heroTitle\": {\n \"$id\": \"ps_h_heroTitle\",\n \"componentId\": \"muiTypography\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ps_h_hero\",\n \"props\": {\n \"variant\": \"h2\",\n \"children\": \"Gear you can trust\",\n \"align\": \"center\"\n },\n \"nodes\": []\n },\n \"ps_h_heroSub\": {\n \"$id\": \"ps_h_heroSub\",\n \"componentId\": \"muiTypography\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ps_h_hero\",\n \"props\": {\n \"variant\": \"h6\",\n \"children\": \"Quality parts, shipped fast.\",\n \"align\": \"center\"\n },\n \"nodes\": []\n },\n \"ps_h_heroCta\": {\n \"$id\": \"ps_h_heroCta\",\n \"componentId\": \"muiButton\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ps_h_hero\",\n \"props\": {\n \"variant\": \"contained\",\n \"size\": \"large\",\n \"children\": \"Get in touch\"\n },\n \"nodes\": []\n },\n \"ps_h_gridSection\": {\n \"$id\": \"ps_h_gridSection\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"xl\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ps_h_grid\"\n ]\n },\n \"ps_h_grid\": {\n \"$id\": \"ps_h_grid\",\n \"componentId\": \"product-grid\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ps_h_gridSection\",\n \"props\": {\n \"source\": \"all\",\n \"sort\": \"newest\",\n \"columns\": 3,\n \"maxItems\": 6\n },\n \"nodes\": []\n },\n \"ps_h_newsSection\": {\n \"$id\": \"ps_h_newsSection\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"md\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ps_h_news\"\n ]\n },\n \"ps_h_news\": {\n \"$id\": \"ps_h_news\",\n \"componentId\": \"newsletter-signup\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ps_h_newsSection\",\n \"props\": {\n \"heading\": \"Get updates and offers\"\n },\n \"nodes\": []\n }\n }\n },\n {\n \"key\": \"shop\",\n \"displayName\": \"Shop\",\n \"slug\": \"shop\",\n \"seo\": {\n \"title\": \"All products\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ps_s_section\"\n ]\n },\n \"ps_s_section\": {\n \"$id\": \"ps_s_section\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"xl\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ps_s_title\",\n \"ps_s_grid\"\n ]\n },\n \"ps_s_title\": {\n \"$id\": \"ps_s_title\",\n \"componentId\": \"muiTypography\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ps_s_section\",\n \"props\": {\n \"variant\": \"h3\",\n \"children\": \"All products\"\n },\n \"nodes\": []\n },\n \"ps_s_grid\": {\n \"$id\": \"ps_s_grid\",\n \"componentId\": \"product-grid\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ps_s_section\",\n \"props\": {\n \"source\": \"all\",\n \"columns\": 4,\n \"showFilters\": true\n },\n \"nodes\": []\n }\n }\n },\n {\n \"key\": \"product\",\n \"displayName\": \"Product page\",\n \"slug\": \"product\",\n \"seo\": {\n \"title\": \"Product\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ps_p_section\"\n ]\n },\n \"ps_p_section\": {\n \"$id\": \"ps_p_section\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"xl\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ps_p_detail\"\n ]\n },\n \"ps_p_detail\": {\n \"$id\": \"ps_p_detail\",\n \"componentId\": \"product-detail\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ps_p_section\",\n \"props\": {},\n \"nodes\": []\n }\n }\n },\n {\n \"key\": \"cart\",\n \"displayName\": \"Cart\",\n \"slug\": \"cart\",\n \"seo\": {\n \"title\": \"Your cart\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ps_c_section\"\n ]\n },\n \"ps_c_section\": {\n \"$id\": \"ps_c_section\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"lg\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ps_c_title\",\n \"ps_c_cart\"\n ]\n },\n \"ps_c_title\": {\n \"$id\": \"ps_c_title\",\n \"componentId\": \"muiTypography\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ps_c_section\",\n \"props\": {\n \"variant\": \"h3\",\n \"children\": \"Your cart\"\n },\n \"nodes\": []\n },\n \"ps_c_cart\": {\n \"$id\": \"ps_c_cart\",\n \"componentId\": \"cart\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ps_c_section\",\n \"props\": {\n \"variant\": \"inline\",\n \"showCoupon\": true\n },\n \"nodes\": []\n }\n }\n },\n {\n \"key\": \"account\",\n \"displayName\": \"Account\",\n \"slug\": \"account\",\n \"seo\": {\n \"title\": \"Your account\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ps_a_section\"\n ]\n },\n \"ps_a_section\": {\n \"$id\": \"ps_a_section\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"lg\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ps_a_account\"\n ]\n },\n \"ps_a_account\": {\n \"$id\": \"ps_a_account\",\n \"componentId\": \"customer-account\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ps_a_section\",\n \"props\": {\n \"signedOutHeading\": \"Your account\"\n },\n \"nodes\": []\n }\n }\n }\n ]\n },\n {\n \"id\": \"digital-shop\",\n \"displayName\": \"Shop (digital products)\",\n \"description\": \"Digital storefront starter: downloads-focused home, shop, product page, cart, and accounts with a newsletter capture. Set the Product page as the product template in Store settings after applying.\",\n \"category\": \"Commerce\",\n \"screens\": [\n {\n \"key\": \"home\",\n \"displayName\": \"Home\",\n \"slug\": \"/\",\n \"seo\": {\n \"title\": \"Digital shop\",\n \"description\": \"Browse our products.\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ds_h_heroSection\",\n \"ds_h_gridSection\",\n \"ds_h_newsSection\"\n ]\n },\n \"ds_h_heroSection\": {\n \"$id\": \"ds_h_heroSection\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"xl\"\n },\n \"sx\": {\n \"paddingTop\": 10,\n \"paddingBottom\": 10\n },\n \"nodes\": [\n \"ds_h_hero\"\n ]\n },\n \"ds_h_hero\": {\n \"$id\": \"ds_h_hero\",\n \"componentId\": \"muiStack\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ds_h_heroSection\",\n \"props\": {\n \"spacing\": 2\n },\n \"sx\": {\n \"alignItems\": \"center\"\n },\n \"nodes\": [\n \"ds_h_heroTitle\",\n \"ds_h_heroSub\",\n \"ds_h_heroCta\"\n ]\n },\n \"ds_h_heroTitle\": {\n \"$id\": \"ds_h_heroTitle\",\n \"componentId\": \"muiTypography\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ds_h_hero\",\n \"props\": {\n \"variant\": \"h2\",\n \"children\": \"Downloads that level you up\",\n \"align\": \"center\"\n },\n \"nodes\": []\n },\n \"ds_h_heroSub\": {\n \"$id\": \"ds_h_heroSub\",\n \"componentId\": \"muiTypography\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ds_h_hero\",\n \"props\": {\n \"variant\": \"h6\",\n \"children\": \"Instant delivery. Lifetime updates.\",\n \"align\": \"center\"\n },\n \"nodes\": []\n },\n \"ds_h_heroCta\": {\n \"$id\": \"ds_h_heroCta\",\n \"componentId\": \"muiButton\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ds_h_hero\",\n \"props\": {\n \"variant\": \"contained\",\n \"size\": \"large\",\n \"children\": \"Get in touch\"\n },\n \"nodes\": []\n },\n \"ds_h_gridSection\": {\n \"$id\": \"ds_h_gridSection\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"xl\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ds_h_grid\"\n ]\n },\n \"ds_h_grid\": {\n \"$id\": \"ds_h_grid\",\n \"componentId\": \"product-grid\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ds_h_gridSection\",\n \"props\": {\n \"source\": \"all\",\n \"sort\": \"newest\",\n \"columns\": 3,\n \"maxItems\": 6\n },\n \"nodes\": []\n },\n \"ds_h_newsSection\": {\n \"$id\": \"ds_h_newsSection\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"md\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ds_h_news\"\n ]\n },\n \"ds_h_news\": {\n \"$id\": \"ds_h_news\",\n \"componentId\": \"newsletter-signup\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ds_h_newsSection\",\n \"props\": {\n \"heading\": \"Get updates and offers\"\n },\n \"nodes\": []\n }\n }\n },\n {\n \"key\": \"shop\",\n \"displayName\": \"Shop\",\n \"slug\": \"shop\",\n \"seo\": {\n \"title\": \"All products\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ds_s_section\"\n ]\n },\n \"ds_s_section\": {\n \"$id\": \"ds_s_section\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"xl\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ds_s_title\",\n \"ds_s_grid\"\n ]\n },\n \"ds_s_title\": {\n \"$id\": \"ds_s_title\",\n \"componentId\": \"muiTypography\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ds_s_section\",\n \"props\": {\n \"variant\": \"h3\",\n \"children\": \"All products\"\n },\n \"nodes\": []\n },\n \"ds_s_grid\": {\n \"$id\": \"ds_s_grid\",\n \"componentId\": \"product-grid\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ds_s_section\",\n \"props\": {\n \"source\": \"all\",\n \"columns\": 4,\n \"showFilters\": true\n },\n \"nodes\": []\n }\n }\n },\n {\n \"key\": \"product\",\n \"displayName\": \"Product page\",\n \"slug\": \"product\",\n \"seo\": {\n \"title\": \"Product\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ds_p_section\"\n ]\n },\n \"ds_p_section\": {\n \"$id\": \"ds_p_section\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"xl\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ds_p_detail\"\n ]\n },\n \"ds_p_detail\": {\n \"$id\": \"ds_p_detail\",\n \"componentId\": \"product-detail\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ds_p_section\",\n \"props\": {},\n \"nodes\": []\n }\n }\n },\n {\n \"key\": \"cart\",\n \"displayName\": \"Cart\",\n \"slug\": \"cart\",\n \"seo\": {\n \"title\": \"Your cart\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ds_c_section\"\n ]\n },\n \"ds_c_section\": {\n \"$id\": \"ds_c_section\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"lg\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ds_c_title\",\n \"ds_c_cart\"\n ]\n },\n \"ds_c_title\": {\n \"$id\": \"ds_c_title\",\n \"componentId\": \"muiTypography\",\n \"pluginId\": \"mui\",\n \"parentId\": \"ds_c_section\",\n \"props\": {\n \"variant\": \"h3\",\n \"children\": \"Your cart\"\n },\n \"nodes\": []\n },\n \"ds_c_cart\": {\n \"$id\": \"ds_c_cart\",\n \"componentId\": \"cart\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ds_c_section\",\n \"props\": {\n \"variant\": \"inline\",\n \"showCoupon\": true\n },\n \"nodes\": []\n }\n }\n },\n {\n \"key\": \"account\",\n \"displayName\": \"Account\",\n \"slug\": \"account\",\n \"seo\": {\n \"title\": \"Your account\"\n },\n \"nodes\": {\n \"_@_\": {\n \"$id\": \"_@_\",\n \"componentId\": \"div\",\n \"nodes\": [\n \"ds_a_section\"\n ]\n },\n \"ds_a_section\": {\n \"$id\": \"ds_a_section\",\n \"componentId\": \"muiContainer\",\n \"pluginId\": \"mui\",\n \"parentId\": \"_@_\",\n \"props\": {\n \"maxWidth\": \"lg\"\n },\n \"sx\": {\n \"paddingTop\": 6,\n \"paddingBottom\": 6\n },\n \"nodes\": [\n \"ds_a_account\"\n ]\n },\n \"ds_a_account\": {\n \"$id\": \"ds_a_account\",\n \"componentId\": \"customer-account\",\n \"pluginId\": \"commerce\",\n \"parentId\": \"ds_a_section\",\n \"props\": {\n \"signedOutHeading\": \"Your account\"\n },\n \"nodes\": []\n }\n }\n }\n ]\n }\n]\n"],"names":["PLUGIN_STARTER_TEMPLATES"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * A repeat's query (AGL-181): the filter, sort and limit an element's
19
+ * `repeatFilter` / `repeatSort` / `repeatLimit` apply to the rows it repeats
20
+ * over, whatever source supplied them — the canvas preview and the published
21
+ * page's expansion run the same one. Deliberately evaluated IN MEMORY over
22
+ * the already-bounded reads (`REPEAT_MAX_RECORDS`): this sidesteps
23
+ * inequality-filter and composite-index constraints entirely, at the
24
+ * documented cost that rows beyond the fetch bound are never considered. The
25
+ * bound is explicit, never silently unbounded.
26
+ */
27
+ export type RepeatQueryOp = '==' | '!=' | '>' | '>=' | '<' | '<=' | 'contains';
28
+ export declare const REPEAT_QUERY_OPS: RepeatQueryOp[];
29
+ export interface RepeatQueryWhere {
30
+ fieldId: string;
31
+ op: RepeatQueryOp;
32
+ /** Literal, compared with type-aware coercion against stored values. */
33
+ value: string;
34
+ }
35
+ export interface RepeatQuery {
36
+ where?: RepeatQueryWhere[];
37
+ orderBy?: {
38
+ fieldId: string;
39
+ direction?: 'asc' | 'desc';
40
+ };
41
+ limit?: number;
42
+ }
43
+ /**
44
+ * Parses the authoring shorthand `field op value` (e.g. `price <= 20`,
45
+ * `tier == plus`, `tags contains red`). Null for unparseable input so
46
+ * callers fail open (no filter) rather than filtering wrongly.
47
+ */
48
+ export declare function parseRepeatFilter(input: string): RepeatQueryWhere | null;
49
+ /** Parses `field` or `field desc` into an orderBy config. */
50
+ export declare function parseRepeatSort(input: string): RepeatQuery['orderBy'] | null;
51
+ /**
52
+ * Applies where/orderBy/limit to in-memory rows. A clause compares the
53
+ * stored value whatever field it names, so rows with no declared field
54
+ * model filter too; timestamps compare numerically (epoch millis storage).
55
+ */
56
+ export declare function applyRepeatQuery<Row extends Record<string, unknown>>(rows: Row[], query: RepeatQuery): Row[];
@@ -14,15 +14,15 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ /**
17
- * Dataset query layer (AGL-181): filter/sort/limit over typed documents,
18
- * shared by the console editor and the tenant renderer's repeatable
19
- * expansion. Deliberately evaluated IN MEMORY over the already-bounded
20
- * reads (editor page / `REPEAT_MAX_RECORDS`) — the Firestore reality
21
- * check from the issue: this sidesteps inequality-filter and composite-
22
- * index constraints entirely, at the documented cost that documents
23
- * beyond the fetch bound are never considered. The bound is explicit,
24
- * never silently unbounded.
25
- */ export const DATASET_QUERY_OPS = [
17
+ * A repeat's query (AGL-181): the filter, sort and limit an element's
18
+ * `repeatFilter` / `repeatSort` / `repeatLimit` apply to the rows it repeats
19
+ * over, whatever source supplied them — the canvas preview and the published
20
+ * page's expansion run the same one. Deliberately evaluated IN MEMORY over
21
+ * the already-bounded reads (`REPEAT_MAX_RECORDS`): this sidesteps
22
+ * inequality-filter and composite-index constraints entirely, at the
23
+ * documented cost that rows beyond the fetch bound are never considered. The
24
+ * bound is explicit, never silently unbounded.
25
+ */ export const REPEAT_QUERY_OPS = [
26
26
  '==',
27
27
  '!=',
28
28
  '>',
@@ -35,7 +35,7 @@
35
35
  * Parses the authoring shorthand `field op value` (e.g. `price <= 20`,
36
36
  * `tier == plus`, `tags contains red`). Null for unparseable input so
37
37
  * callers fail open (no filter) rather than filtering wrongly.
38
- */ export function parseDatasetFilter(input) {
38
+ */ export function parseRepeatFilter(input) {
39
39
  const match = String(input != null ? input : '').trim().match(/^([A-Za-z][A-Za-z0-9_]*)\s*(==|!=|>=|<=|>|<|contains)\s+(.+)$/);
40
40
  if (!match) return null;
41
41
  return {
@@ -44,7 +44,7 @@
44
44
  value: match[3].trim()
45
45
  };
46
46
  }
47
- /** Parses `field` or `field desc` into an orderBy config. */ export function parseDatasetSort(input) {
47
+ /** Parses `field` or `field desc` into an orderBy config. */ export function parseRepeatSort(input) {
48
48
  var _match_;
49
49
  const match = String(input != null ? input : '').trim().match(/^([A-Za-z][A-Za-z0-9_]*)(?:\s+(asc|desc))?$/i);
50
50
  if (!match) return null;
@@ -92,10 +92,10 @@ function matches(stored, where) {
92
92
  return false;
93
93
  }
94
94
  /**
95
- * Applies where/orderBy/limit to in-memory rows. Where-clauses on fields
96
- * absent from the model still run (stored-value comparison) so v1
97
- * datasets work; timestamps compare numerically (epoch millis storage).
98
- */ export function applyDatasetQuery(model, rows, query) {
95
+ * Applies where/orderBy/limit to in-memory rows. A clause compares the
96
+ * stored value whatever field it names, so rows with no declared field
97
+ * model filter too; timestamps compare numerically (epoch millis storage).
98
+ */ export function applyRepeatQuery(rows, query) {
99
99
  var _query_where;
100
100
  let result = rows;
101
101
  for (const where of (_query_where = query.where) != null ? _query_where : []){
@@ -123,4 +123,4 @@ function matches(stored, where) {
123
123
  return result;
124
124
  }
125
125
 
126
- //# sourceMappingURL=dataset-query.js.map
126
+ //# sourceMappingURL=repeat-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/repeat-query.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A repeat's query (AGL-181): the filter, sort and limit an element's\n * `repeatFilter` / `repeatSort` / `repeatLimit` apply to the rows it repeats\n * over, whatever source supplied them — the canvas preview and the published\n * page's expansion run the same one. Deliberately evaluated IN MEMORY over\n * the already-bounded reads (`REPEAT_MAX_RECORDS`): this sidesteps\n * inequality-filter and composite-index constraints entirely, at the\n * documented cost that rows beyond the fetch bound are never considered. The\n * bound is explicit, never silently unbounded.\n */\n\nexport type RepeatQueryOp =\n | '=='\n | '!='\n | '>'\n | '>='\n | '<'\n | '<='\n | 'contains'\n\nexport const REPEAT_QUERY_OPS: RepeatQueryOp[] = [\n '==',\n '!=',\n '>',\n '>=',\n '<',\n '<=',\n 'contains',\n]\n\nexport interface RepeatQueryWhere {\n fieldId: string\n op: RepeatQueryOp\n /** Literal, compared with type-aware coercion against stored values. */\n value: string\n}\n\nexport interface RepeatQuery {\n where?: RepeatQueryWhere[]\n orderBy?: { fieldId: string; direction?: 'asc' | 'desc' }\n limit?: number\n}\n\n/**\n * Parses the authoring shorthand `field op value` (e.g. `price <= 20`,\n * `tier == plus`, `tags contains red`). Null for unparseable input so\n * callers fail open (no filter) rather than filtering wrongly.\n */\nexport function parseRepeatFilter(input: string): RepeatQueryWhere | null {\n const match = String(input ?? '')\n .trim()\n .match(/^([A-Za-z][A-Za-z0-9_]*)\\s*(==|!=|>=|<=|>|<|contains)\\s+(.+)$/)\n if (!match) return null\n return {\n fieldId: match[1],\n op: match[2] as RepeatQueryOp,\n value: match[3].trim(),\n }\n}\n\n/** Parses `field` or `field desc` into an orderBy config. */\nexport function parseRepeatSort(\n input: string,\n): RepeatQuery['orderBy'] | null {\n const match = String(input ?? '')\n .trim()\n .match(/^([A-Za-z][A-Za-z0-9_]*)(?:\\s+(asc|desc))?$/i)\n if (!match) return null\n return {\n fieldId: match[1],\n direction: match[2]?.toLowerCase() === 'desc' ? 'desc' : 'asc',\n }\n}\n\nconst comparable = (value: unknown): number | string | null => {\n if (typeof value === 'number') return value\n if (typeof value === 'boolean') return value ? 1 : 0\n if (typeof value === 'string') {\n const numeric = Number(value)\n return Number.isFinite(numeric) && value.trim() !== '' ? numeric : value\n }\n return null\n}\n\nfunction matches(stored: unknown, where: RepeatQueryWhere): boolean {\n const { op, value } = where\n if (op === 'contains') {\n if (Array.isArray(stored)) {\n return stored.some(\n (entry) => String(entry).toLowerCase() === value.toLowerCase(),\n )\n }\n return String(stored ?? '')\n .toLowerCase()\n .includes(value.toLowerCase())\n }\n if (op === '==' || op === '!=') {\n const equal =\n typeof stored === 'boolean'\n ? String(stored) === value.toLowerCase()\n : String(stored ?? '') === value\n return op === '==' ? equal : !equal\n }\n const left = comparable(stored)\n const right = comparable(value)\n if (left == null || right == null || typeof left !== typeof right) {\n return false\n }\n switch (op) {\n case '>':\n return left > right\n case '>=':\n return left >= right\n case '<':\n return left < right\n case '<=':\n return left <= right\n }\n return false\n}\n\n/**\n * Applies where/orderBy/limit to in-memory rows. A clause compares the\n * stored value whatever field it names, so rows with no declared field\n * model filter too; timestamps compare numerically (epoch millis storage).\n */\nexport function applyRepeatQuery<Row extends Record<string, unknown>>(\n rows: Row[],\n query: RepeatQuery,\n): Row[] {\n let result = rows\n for (const where of query.where ?? []) {\n result = result.filter((row) => matches(row[where.fieldId], where))\n }\n if (query.orderBy) {\n const { fieldId, direction } = query.orderBy\n const sign = direction === 'desc' ? -1 : 1\n result = [...result].sort((a, b) => {\n const left = comparable(a[fieldId])\n const right = comparable(b[fieldId])\n if (left == null) return 1\n if (right == null) return -1\n if (typeof left === 'number' && typeof right === 'number') {\n return (left - right) * sign\n }\n return String(left).localeCompare(String(right)) * sign\n })\n }\n if (query.limit != null && query.limit > 0) {\n result = result.slice(0, query.limit)\n }\n return result\n}\n"],"names":["REPEAT_QUERY_OPS","parseRepeatFilter","input","match","String","trim","fieldId","op","value","parseRepeatSort","direction","toLowerCase","comparable","numeric","Number","isFinite","matches","stored","where","Array","isArray","some","entry","includes","equal","left","right","applyRepeatQuery","rows","query","result","filter","row","orderBy","sign","sort","a","b","localeCompare","limit","slice"],"mappings":"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"}
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ import type { LeavingNoticeConfig } from './leaving-notice';
17
18
  /**
18
19
  * Host routing map: screen id → routed path in the tenant matcher format
19
20
  * (root is `'/'`, nested paths are slash-joined segments WITHOUT a leading
@@ -45,6 +46,12 @@ export interface ScreenLinkContextValue {
45
46
  localeVariants?: Record<string, string>;
46
47
  /** Locale of the screen being rendered (AGL-164). */
47
48
  currentLocale?: string;
49
+ /**
50
+ * Present only on a published page of a site in its leaving-notice window
51
+ * (AGL-3452): links to other domains resolve to the notice on the site's
52
+ * own host instead. See `leaving-notice.ts`.
53
+ */
54
+ leavingNotice?: LeavingNoticeConfig;
48
55
  }
49
56
  /**
50
57
  * Render-time resolution context for id-based screen links: canvas nodes
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/screen-link-context-value.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The render-time screen-link context, and the shape it carries.\n *\n * Its own module because a published page provides this context and reads\n * nothing else here: the map goes in at the top of the tree and the canvas\n * nodes below resolve their hrefs from it. `screen-link-context.ts` around it\n * is the resolution machinery every editing surface needs — the broken-link\n * verdict and its styling, the unavailable-screen labels, the href resolver,\n * the link-value splitter and the two hooks — and through\n * `screen-link-value.ts` the persisted link grammar as well. A bundler cannot\n * drop that around a single named import.\n *\n * No `'use client'` banner, for the reason `screen-link-context.ts` records:\n * inside this package the directive forks the module graph, and a second\n * copy of the context is a second context — providers on one, consumers on\n * the other, and every link resolving to nothing.\n */\n\nimport { createContext } from 'react'\n\n/**\n * Host routing map: screen id → routed path in the tenant matcher format\n * (root is `'/'`, nested paths are slash-joined segments WITHOUT a leading\n * slash, e.g. `company/about`). This is the `screens` field of the host\n * document — the single source of truth kept current by the publish and\n * hierarchy flows.\n */\nexport type ScreenRouteMap = Record<string, string>\n\nexport interface ScreenLinkContextValue {\n /** Routing map hrefs are resolved against. Absent → nothing resolves. */\n screens?: ScreenRouteMap\n /** Optional display names by screen id, for editor-facing pickers. */\n labels?: Record<string, string>\n /**\n * True inside editing surfaces (besigner canvas, preview): screen links\n * render their content but must not navigate.\n */\n suppressNavigation?: boolean\n /**\n * True ONLY on the static besigner canvas (AGL-830): interactions are\n * inert and command-bus-driven elements (nav menus, drawers) render their\n * editor affordance instead of the live popup. The Preview surface leaves\n * this falsy — it suppresses navigation but runs interactions for real, so\n * a hover-to-open mega menu behaves exactly like the live site. Split out\n * of {@link suppressNavigation}, which now means only \"links don't navigate\".\n */\n editorInert?: boolean\n /** Current screen's translations: locale → screen id (AGL-164). */\n localeVariants?: Record<string, string>\n /** Locale of the screen being rendered (AGL-164). */\n currentLocale?: string\n}\n\n/**\n * Render-time resolution context for id-based screen links: canvas nodes\n * persist a screen id, never a path, so slug renames and re-parenting can't\n * break links. Provided by the tenant page (map from static props, refreshed\n * by ISR) and by the console's besigner/preview surfaces (map from the live\n * host doc subscription, navigation suppressed). Context crosses the canvas\n * shadow DOM because the shadow root renders through a React portal.\n */\nexport const ScreenLinkContext = createContext<ScreenLinkContextValue>({})\nScreenLinkContext.displayName = 'ScreenLinkContext'\n"],"names":["createContext","ScreenLinkContext","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;CAgBC,GAED,SAASA,aAAa,QAAQ,QAAO;AAoCrC;;;;;;;CAOC,GACD,OAAO,MAAMC,oBAAoBD,cAAsC,CAAC,GAAE;AAC1EC,kBAAkBC,WAAW,GAAG"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/screen-link-context-value.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The render-time screen-link context, and the shape it carries.\n *\n * Its own module because a published page provides this context and reads\n * nothing else here: the map goes in at the top of the tree and the canvas\n * nodes below resolve their hrefs from it. `screen-link-context.ts` around it\n * is the resolution machinery every editing surface needs — the broken-link\n * verdict and its styling, the unavailable-screen labels, the href resolver,\n * the link-value splitter and the two hooks — and through\n * `screen-link-value.ts` the persisted link grammar as well. A bundler cannot\n * drop that around a single named import.\n *\n * No `'use client'` banner, for the reason `screen-link-context.ts` records:\n * inside this package the directive forks the module graph, and a second\n * copy of the context is a second context — providers on one, consumers on\n * the other, and every link resolving to nothing.\n */\n\nimport { createContext } from 'react'\nimport type { LeavingNoticeConfig } from './leaving-notice'\n\n/**\n * Host routing map: screen id → routed path in the tenant matcher format\n * (root is `'/'`, nested paths are slash-joined segments WITHOUT a leading\n * slash, e.g. `company/about`). This is the `screens` field of the host\n * document — the single source of truth kept current by the publish and\n * hierarchy flows.\n */\nexport type ScreenRouteMap = Record<string, string>\n\nexport interface ScreenLinkContextValue {\n /** Routing map hrefs are resolved against. Absent → nothing resolves. */\n screens?: ScreenRouteMap\n /** Optional display names by screen id, for editor-facing pickers. */\n labels?: Record<string, string>\n /**\n * True inside editing surfaces (besigner canvas, preview): screen links\n * render their content but must not navigate.\n */\n suppressNavigation?: boolean\n /**\n * True ONLY on the static besigner canvas (AGL-830): interactions are\n * inert and command-bus-driven elements (nav menus, drawers) render their\n * editor affordance instead of the live popup. The Preview surface leaves\n * this falsy — it suppresses navigation but runs interactions for real, so\n * a hover-to-open mega menu behaves exactly like the live site. Split out\n * of {@link suppressNavigation}, which now means only \"links don't navigate\".\n */\n editorInert?: boolean\n /** Current screen's translations: locale → screen id (AGL-164). */\n localeVariants?: Record<string, string>\n /** Locale of the screen being rendered (AGL-164). */\n currentLocale?: string\n /**\n * Present only on a published page of a site in its leaving-notice window\n * (AGL-3452): links to other domains resolve to the notice on the site's\n * own host instead. See `leaving-notice.ts`.\n */\n leavingNotice?: LeavingNoticeConfig\n}\n\n/**\n * Render-time resolution context for id-based screen links: canvas nodes\n * persist a screen id, never a path, so slug renames and re-parenting can't\n * break links. Provided by the tenant page (map from static props, refreshed\n * by ISR) and by the console's besigner/preview surfaces (map from the live\n * host doc subscription, navigation suppressed). Context crosses the canvas\n * shadow DOM because the shadow root renders through a React portal.\n */\nexport const ScreenLinkContext = createContext<ScreenLinkContextValue>({})\nScreenLinkContext.displayName = 'ScreenLinkContext'\n"],"names":["createContext","ScreenLinkContext","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;CAgBC,GAED,SAASA,aAAa,QAAQ,QAAO;AA2CrC;;;;;;;CAOC,GACD,OAAO,MAAMC,oBAAoBD,cAAsC,CAAC,GAAE;AAC1EC,kBAAkBC,WAAW,GAAG"}
@@ -20,6 +20,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
20
20
  // barrel from the tenant page) makes the bundler duplicate parts of the
21
21
  // module graph — a second canvas/emitter instance renders the site blank.
22
22
  import { useContext, useMemo } from "react";
23
+ import { routeThroughLeavingNotice } from "./leaving-notice.js";
23
24
  import { ScreenLinkContext } from "./screen-link-context-value.js";
24
25
  import { EXTERNAL_HREF_PATTERN, SAFE_HREF_PATTERN, formatCollectionLinkValue, linkTargetKind, parseCollectionLinkValue, parseEntryLinkValue, parseFeedLinkValue, resolveScreenHref, screenRoutesAnswerFor, splitLinkValue } from "./screen-link-value.js";
25
26
  export * from "./screen-link-context-value.js";
@@ -260,12 +261,23 @@ export function unresolvedScreenOption(value, screens) {
260
261
  */ export function useLinkTarget(screenId, href) {
261
262
  const target = splitLinkValue(screenId, href);
262
263
  const resolved = useScreenLink(target.screenId);
264
+ const { leavingNotice } = useContext(ScreenLinkContext);
263
265
  const externalHref = target.href && SAFE_HREF_PATTERN.test(target.href) ? target.href : undefined;
264
266
  const finalHref = target.screenId ? resolved.href : externalHref;
267
+ /*
268
+ * A new free site's links to other domains go through the leaving notice on
269
+ * its own host (AGL-3452). Here rather than in each element so the served
270
+ * HTML already carries the notice's address: it holds without JavaScript,
271
+ * and it is what a hover, a copied link and "Open in new tab" all see.
272
+ *
273
+ * The notice is on the site, so the rewritten link no longer leaves it —
274
+ * and keeping the referrer is what lets the notice's Go back return to the
275
+ * page the visitor came from.
276
+ */ const noticeHref = !target.screenId && !resolved.suppressNavigation ? routeThroughLeavingNotice(externalHref, leavingNotice) : undefined;
265
277
  return _extends({}, resolved, {
266
- href: finalHref,
278
+ href: noticeHref != null ? noticeHref : finalHref,
267
279
  externalHref,
268
- leavesSite: Boolean(!target.screenId && externalHref && EXTERNAL_HREF_PATTERN.test(externalHref))
280
+ leavesSite: Boolean(!noticeHref && !target.screenId && externalHref && EXTERNAL_HREF_PATTERN.test(externalHref))
269
281
  });
270
282
  }
271
283
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/screen-link-context.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n// Lives in @aglyn/aglyn (not a UI lib) deliberately, WITHOUT a 'use client'\n// banner: every surface that renders canvas nodes already imports this\n// package, and a client boundary here (or importing the shared-ui-jsx\n// barrel from the tenant page) makes the bundler duplicate parts of the\n// module graph — a second canvas/emitter instance renders the site blank.\nimport { useContext, useMemo } from 'react'\nimport {\n ScreenLinkContext,\n type ScreenRouteMap,\n} from './screen-link-context-value'\nimport {\n EXTERNAL_HREF_PATTERN,\n SAFE_HREF_PATTERN,\n formatCollectionLinkValue,\n linkTargetKind,\n parseCollectionLinkValue,\n parseEntryLinkValue,\n parseFeedLinkValue,\n resolveScreenHref,\n screenRoutesAnswerFor,\n splitLinkValue,\n} from './screen-link-value'\n\nexport * from './screen-link-context-value'\n\nexport interface ResolvedScreenLink {\n /** Site-relative href (`/`, `/company/about`), undefined when unresolvable. */\n href?: string\n suppressNavigation: boolean\n /** True only on the static besigner canvas — interactions are inert. */\n editorInert: boolean\n /**\n * A screen id WAS authored and the routing map does not have it — the\n * target is unpublished or deleted (AGL-1893).\n *\n * Distinct from \"no href\": the canvas and the preview withhold hrefs on\n * purpose, and an element with no link at all never had one to lose.\n * Only this flag means the author asked for a link that cannot exist, and\n * only elements reading THIS may say so — see {@link isScreenLinkBroken}\n * for why an absent or empty map is deliberately not \"broken\".\n */\n broken: boolean\n}\n\n/**\n * Whether an authored screen id points at nothing (AGL-1893).\n *\n * Unpublishing or deleting a screen is a normal authoring action, and until\n * this existed nothing anywhere reported what it had just broken: a Tabs\n * link to a retired screen shipped as a live-looking control that silently\n * did nothing on `aglyn.com/changelog` and `/newsroom` for two days.\n *\n * The two \"we cannot tell\" cases are NOT broken, and the distinction is the\n * whole safety of this predicate:\n *\n * - **no map** — a surface that renders canvas nodes without providing\n * `ScreenLinkContext` (an isolated spec, an embed) knows nothing about\n * the host's screens, and must not conclude that every link is dead;\n * - **an empty map** — the same thing one beat earlier: the console's live\n * host subscription starts empty, and a moment of `{}` must not repaint a\n * whole navigation row as broken and then repaint it back.\n *\n * Both failure directions were weighed. Calling a live link broken hides or\n * disables working navigation on a customer's site; calling a dead link fine\n * leaves exactly the defect this issue is about. Only the first is\n * unrecoverable from the visitor's side, so the doubt resolves that way.\n */\nexport function isScreenLinkBroken(\n screens: ScreenRouteMap | undefined,\n screenId: string | null | undefined,\n): boolean {\n if (!screenId) return false\n // Both \"we cannot tell\" cases, asked of the half of the map that holds this\n // target's kind: a collection listing (AGL-2799) is judged against the\n // listings, a screen against the screens — see `screenRoutesAnswerFor`.\n if (!screenRoutesAnswerFor(screens, screenId)) return false\n return resolveScreenHref(screens, screenId) === undefined\n}\n\n/**\n * The extra option a Screen picker needs when its stored value matches none\n * of the host's screens (AGL-1893).\n *\n * The console builds `SCREEN_SELECT` options from the routing map, so a\n * value the map has lost — the retired `/blog` screen, say — matches\n * nothing and the field renders EMPTY. Which reads, to the person looking\n * at it, as \"no link set\". Meanwhile the element still behaves as linked:\n * the strip is still a nav landmark, that tab is still skipped as\n * \"navigates\", and the panel it would have revealed is still suppressed. So\n * the one surface where this is repairable was also the one actively\n * denying there was anything to repair.\n *\n * Returns the stored value UNCHANGED as the option's value: naming a dead\n * target must not rewrite it, or opening the panel and pressing Save would\n * quietly convert a recoverable reference into something else.\n *\n * Three shapes get named, because a Screen picker can legally hold any of\n * them (AGL-1335 / AGL-1894 / AGL-2799): a screen reference whose screen is\n * gone, a collection listing whose collection is gone, and a plain address\n * typed in before the picker existed. The last is not broken — but it is\n * just as invisible, and an author who cannot see it is the reason those\n * links are not rename-safe yet.\n */\nexport function unavailableScreenLabel(\n screenId: string,\n screensKnown: boolean,\n): string {\n // With no map loaded yet nothing is known to be missing, so the id is\n // shown plainly. Flashing \"unavailable\" over every link for the beat\n // before the console's host subscription lands would teach authors to\n // ignore the warning that matters.\n if (!screensKnown) return screenId\n // A listing has no publish state of its own: its collection is either gone\n // or no longer has the slug its address is built from. A feed hangs off the\n // same listing, so it is lost the same two ways (AGL-3118).\n const collectionId = parseCollectionLinkValue(screenId)\n if (collectionId) {\n return `⚠ Unavailable collection listing (${collectionId}) — deleted or has no slug`\n }\n const feedCollectionId = parseFeedLinkValue(screenId)\n if (feedCollectionId) {\n return `⚠ Unavailable RSS feed (${feedCollectionId}) — collection deleted or has no slug`\n }\n const entry = parseEntryLinkValue(screenId)\n return entry\n ? `⚠ Unavailable entry (${entry.entryId}) — unpublished or deleted`\n : `⚠ Unavailable page (${screenId}) — unpublished or deleted`\n}\n\nexport function unresolvedScreenOption(\n value: unknown,\n screens: ScreenRouteMap | undefined,\n): { value: string; label: string } | undefined {\n const raw = typeof value === 'string' ? value.trim() : ''\n if (!raw) return undefined\n const target = splitLinkValue(raw, undefined)\n if (target.screenId) {\n if (resolveScreenHref(screens, target.screenId) !== undefined) {\n return undefined\n }\n return {\n value: raw,\n label: unavailableScreenLabel(\n target.screenId,\n screenRoutesAnswerFor(screens, target.screenId),\n ),\n }\n }\n return target.href\n ? { value: raw, label: `⚠ Plain address (${target.href}) — not a page` }\n : undefined\n}\n\n/** One target a link picker offers — see {@link screenLinkTargetOptions}. */\nexport interface ScreenLinkTargetOption {\n /**\n * The routing-map key: a bare screen id, a collection listing's\n * `collection:<id>`, or its feed's `feed:<id>`. Stored as it is in a screen\n * slot, and through `formatScreenLinkValue` in a `Link`-typed prop.\n */\n value: string\n label: string\n kind: 'screen' | 'collection-listing' | 'feed'\n}\n\n/**\n * What a picker calls one routing-map target, or `undefined` for a key the\n * map does not hold — the label {@link screenLinkTargetOptions} gives it.\n *\n * A listing and a feed are labeled with their collection's name and address,\n * like a screen, and marked as what they are IN the label rather than only by\n * position: a closed picker shows the chosen label and nothing else, and\n * \"Blog (/blog)\" there reads exactly like a screen named Blog. A feed borrows\n * its collection's name, which is the only name it has (AGL-3119).\n *\n * An entry is never named here. A map holds only the entries a page\n * references, under no label, so an entry is named by the search seam\n * (`LinkTargetSearchContext`), which reads the entry itself.\n */\nexport function screenLinkTargetLabel(\n key: string,\n screens: ScreenRouteMap | undefined,\n labels: Record<string, string> | undefined,\n): string | undefined {\n const href = resolveScreenHref(screens, key)\n if (href === undefined) return undefined\n const collectionId = parseCollectionLinkValue(key)\n if (collectionId) {\n return `${labels?.[key] ?? collectionId} (${href}) — collection listing`\n }\n const feedCollectionId = parseFeedLinkValue(key)\n if (feedCollectionId) {\n const name =\n labels?.[key] ??\n labels?.[formatCollectionLinkValue(feedCollectionId)] ??\n feedCollectionId\n return `${name} (${href}) — RSS feed`\n }\n if (parseEntryLinkValue(key)) return undefined\n return `${labels?.[key] ?? key} (${href})`\n}\n\n/**\n * Everything a Screen picker offers, in the order it offers it (AGL-2799):\n * the host's screens, then its content collections' listing pages, then\n * their RSS feeds (AGL-3119).\n *\n * ONE builder for both pickers — the attributes panel's `SCREEN_SELECT` and\n * the `Link`-typed prop picker. Each used to map the routing map into options\n * by itself, so a target added to one list was missing from the other; and\n * neither offered a collection's listing at all, which is how the drawer on\n * aglyn.com came to link its blog as a typed `/blog`.\n *\n * Entries are left out even when the map holds some: they are searched, not\n * listed, because a site's entries are far too many to read into a dropdown\n * — see `LinkTargetSearchContext`.\n *\n * `order` keeps each picker's established screen order — by path in the\n * attributes panel, by name in the prop picker — and the listings and feeds\n * follow the screens in the same order.\n */\nexport function screenLinkTargetOptions(\n screens: ScreenRouteMap | undefined,\n labels: Record<string, string> | undefined,\n order: 'path' | 'label' = 'path',\n): ScreenLinkTargetOption[] {\n type Ranked = ScreenLinkTargetOption & { path: string }\n const pages: Ranked[] = []\n const listings: Ranked[] = []\n const feeds: Ranked[] = []\n for (const [key, path] of Object.entries(screens ?? {})) {\n const kind = linkTargetKind(key)\n if (kind === 'entry') continue\n const label = screenLinkTargetLabel(key, screens, labels) ?? key\n if (kind === 'collection') {\n listings.push({ value: key, label, kind: 'collection-listing', path })\n } else if (kind === 'feed') {\n feeds.push({ value: key, label, kind: 'feed', path })\n } else {\n pages.push({ value: key, label, kind: 'screen', path })\n }\n }\n const compare = (a: Ranked, b: Ranked) =>\n order === 'path'\n ? a.path.localeCompare(b.path)\n : a.label.localeCompare(b.label)\n return [\n ...pages.sort(compare),\n ...listings.sort(compare),\n ...feeds.sort(compare),\n ].map(({ value, label, kind }) => ({ value, label, kind }))\n}\n\n/**\n * Marks the element in the DOM. Present on the live site too, deliberately:\n * a smoke pass can then find every dead control on a page with one selector\n * instead of reading five different components' conditions.\n */\nexport const BROKEN_SCREEN_LINK_ATTR = 'data-aglyn-broken-link'\n\n/** What the AUTHOR is told, on the one surface that can fix it. */\nexport const BROKEN_SCREEN_LINK_MESSAGE =\n 'Broken link: this points at a page or collection listing that is ' +\n 'unpublished or deleted, so it will not work on the published site. Pick ' +\n 'a target again in the attributes panel, or clear the link.'\n\n/**\n * The editor-only outline. A warning ring rather than an error one: the\n * screen may be deliberately unpublished and about to come back, and the\n * page is not broken, one control on it is.\n */\nexport const BROKEN_SCREEN_LINK_SX = {\n outline: '2px dashed',\n outlineColor: 'warning.main',\n outlineOffset: '2px',\n borderRadius: 1,\n}\n\n/**\n * Props that make a dead screen link visible to its author (AGL-1893).\n *\n * The tooltip and the ring are `editorInert` only — that is the static\n * besigner canvas, the one surface where the person who can fix this is\n * looking at it. Preview deliberately does NOT get them: preview exists to\n * show what visitors will see, and an authoring annotation painted into it\n * is a lie of a different kind.\n *\n * The data attribute ships everywhere, canvas and live site alike.\n */\nexport function brokenScreenLinkProps(\n broken: boolean,\n editorInert: boolean,\n): Record<string, unknown> {\n if (!broken) return {}\n return {\n [BROKEN_SCREEN_LINK_ATTR]: '',\n ...(editorInert ? { title: BROKEN_SCREEN_LINK_MESSAGE } : null),\n }\n}\n\n/**\n * Link VALUE parsing AND resolution live in `./screen-link-value` —\n * server-safe, because the where-used scan and the Markdown representation of\n * a page both run on the server, and the `createContext` this module reaches\n * keeps it out of the `@aglyn/aglyn/server` barrel (AGL-703, AGL-2740).\n * Re-exported here so every existing importer, and the spec beside this file,\n * keeps reaching them at the address they have always used.\n */\nexport {\n COLLECTION_LINK_VALUE_PREFIX,\n ENTRY_LINK_VALUE_PREFIX,\n EXTERNAL_HREF_PATTERN,\n FEED_LINK_VALUE_PREFIX,\n SAFE_HREF_PATTERN,\n SCREEN_LINK_VALUE_PREFIX,\n formatCollectionLinkValue,\n formatEntryLinkValue,\n formatFeedLinkValue,\n formatScreenLinkValue,\n linkTargetKind,\n nodesReferenceScreen,\n parseCollectionLinkValue,\n parseEntryLinkValue,\n parseFeedLinkValue,\n parseScreenLinkValue,\n resolveScreenHref,\n screenRoutesAnswerFor,\n splitLinkValue,\n type LinkTargetKind,\n} from './screen-link-value'\n\n/** What a linking element renders — see {@link useLinkTarget}. */\nexport interface ResolvedLinkTarget extends ResolvedScreenLink {\n /** The literal href in play once safety-checked, when no screen resolved. */\n externalHref?: string\n /** True when `href` leaves the site — what \"open in a new tab\" may act on. */\n leavesSite: boolean\n}\n\n/**\n * The single href a linking element should render, from the `screenId` +\n * `href` pair every one of them declares (AGL-1335).\n *\n * Button, Screen Link, Link Box and Image each had the same four lines —\n * resolve the id, safety-check the URL, prefer the id — which is three\n * copies too many now that a `Link`-typed component prop can feed either\n * slot with either shape. Hook-shaped because {@link useScreenLink} is;\n * it is always called, so the hook order never depends on the values.\n */\nexport function useLinkTarget(\n screenId: string | null | undefined,\n href: string | null | undefined,\n): ResolvedLinkTarget {\n const target = splitLinkValue(screenId, href)\n const resolved = useScreenLink(target.screenId)\n const externalHref =\n target.href && SAFE_HREF_PATTERN.test(target.href) ? target.href : undefined\n const finalHref = target.screenId ? resolved.href : externalHref\n return {\n ...resolved,\n href: finalHref,\n externalHref,\n leavesSite: Boolean(\n !target.screenId && externalHref && EXTERNAL_HREF_PATTERN.test(externalHref),\n ),\n }\n}\n\n/**\n * Resolves a screen id to its current href. Memoized on the routing-map\n * identity: a slug rename, parent-slug change, or re-parent produces a new\n * map value, so cached hrefs reset exactly when the map changes. Returns no\n * href for unknown/unpublished ids — callers degrade to plain content\n * instead of rendering a dead link.\n */\nexport function useScreenLink(\n screenId: string | null | undefined,\n): ResolvedScreenLink {\n const { screens, suppressNavigation, editorInert } = useContext(ScreenLinkContext)\n const href = useMemo(() => {\n if (!screenId) return undefined\n const resolved = resolveScreenHref(screens, screenId)\n if (resolved === undefined) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[ScreenLink] screen \"${screenId}\" has no routing-map entry — ` +\n 'it may be unpublished or deleted; rendering without an href.',\n )\n }\n return undefined\n }\n return resolved\n }, [screenId, screens])\n return {\n href,\n suppressNavigation: Boolean(suppressNavigation),\n editorInert: Boolean(editorInert),\n // NOT `!href`: a suppressed surface has no href for links that resolve\n // perfectly well, and the whole point of this flag is to separate the\n // two (AGL-1893).\n broken: isScreenLinkBroken(screens, screenId),\n }\n}\n"],"names":["useContext","useMemo","ScreenLinkContext","EXTERNAL_HREF_PATTERN","SAFE_HREF_PATTERN","formatCollectionLinkValue","linkTargetKind","parseCollectionLinkValue","parseEntryLinkValue","parseFeedLinkValue","resolveScreenHref","screenRoutesAnswerFor","splitLinkValue","isScreenLinkBroken","screens","screenId","undefined","unavailableScreenLabel","screensKnown","collectionId","feedCollectionId","entry","entryId","unresolvedScreenOption","value","raw","trim","target","label","href","screenLinkTargetLabel","key","labels","name","screenLinkTargetOptions","order","pages","listings","feeds","path","Object","entries","kind","push","compare","a","b","localeCompare","sort","map","BROKEN_SCREEN_LINK_ATTR","BROKEN_SCREEN_LINK_MESSAGE","BROKEN_SCREEN_LINK_SX","outline","outlineColor","outlineOffset","borderRadius","brokenScreenLinkProps","broken","editorInert","title","COLLECTION_LINK_VALUE_PREFIX","ENTRY_LINK_VALUE_PREFIX","FEED_LINK_VALUE_PREFIX","SCREEN_LINK_VALUE_PREFIX","formatEntryLinkValue","formatFeedLinkValue","formatScreenLinkValue","nodesReferenceScreen","parseScreenLinkValue","useLinkTarget","resolved","useScreenLink","externalHref","test","finalHref","leavesSite","Boolean","suppressNavigation","process","env","NODE_ENV","console","warn"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/screen-link-context.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n// Lives in @aglyn/aglyn (not a UI lib) deliberately, WITHOUT a 'use client'\n// banner: every surface that renders canvas nodes already imports this\n// package, and a client boundary here (or importing the shared-ui-jsx\n// barrel from the tenant page) makes the bundler duplicate parts of the\n// module graph — a second canvas/emitter instance renders the site blank.\nimport { useContext, useMemo } from 'react'\nimport { routeThroughLeavingNotice } from './leaving-notice'\nimport {\n ScreenLinkContext,\n type ScreenRouteMap,\n} from './screen-link-context-value'\nimport {\n EXTERNAL_HREF_PATTERN,\n SAFE_HREF_PATTERN,\n formatCollectionLinkValue,\n linkTargetKind,\n parseCollectionLinkValue,\n parseEntryLinkValue,\n parseFeedLinkValue,\n resolveScreenHref,\n screenRoutesAnswerFor,\n splitLinkValue,\n} from './screen-link-value'\n\nexport * from './screen-link-context-value'\n\nexport interface ResolvedScreenLink {\n /** Site-relative href (`/`, `/company/about`), undefined when unresolvable. */\n href?: string\n suppressNavigation: boolean\n /** True only on the static besigner canvas — interactions are inert. */\n editorInert: boolean\n /**\n * A screen id WAS authored and the routing map does not have it — the\n * target is unpublished or deleted (AGL-1893).\n *\n * Distinct from \"no href\": the canvas and the preview withhold hrefs on\n * purpose, and an element with no link at all never had one to lose.\n * Only this flag means the author asked for a link that cannot exist, and\n * only elements reading THIS may say so — see {@link isScreenLinkBroken}\n * for why an absent or empty map is deliberately not \"broken\".\n */\n broken: boolean\n}\n\n/**\n * Whether an authored screen id points at nothing (AGL-1893).\n *\n * Unpublishing or deleting a screen is a normal authoring action, and until\n * this existed nothing anywhere reported what it had just broken: a Tabs\n * link to a retired screen shipped as a live-looking control that silently\n * did nothing on `aglyn.com/changelog` and `/newsroom` for two days.\n *\n * The two \"we cannot tell\" cases are NOT broken, and the distinction is the\n * whole safety of this predicate:\n *\n * - **no map** — a surface that renders canvas nodes without providing\n * `ScreenLinkContext` (an isolated spec, an embed) knows nothing about\n * the host's screens, and must not conclude that every link is dead;\n * - **an empty map** — the same thing one beat earlier: the console's live\n * host subscription starts empty, and a moment of `{}` must not repaint a\n * whole navigation row as broken and then repaint it back.\n *\n * Both failure directions were weighed. Calling a live link broken hides or\n * disables working navigation on a customer's site; calling a dead link fine\n * leaves exactly the defect this issue is about. Only the first is\n * unrecoverable from the visitor's side, so the doubt resolves that way.\n */\nexport function isScreenLinkBroken(\n screens: ScreenRouteMap | undefined,\n screenId: string | null | undefined,\n): boolean {\n if (!screenId) return false\n // Both \"we cannot tell\" cases, asked of the half of the map that holds this\n // target's kind: a collection listing (AGL-2799) is judged against the\n // listings, a screen against the screens — see `screenRoutesAnswerFor`.\n if (!screenRoutesAnswerFor(screens, screenId)) return false\n return resolveScreenHref(screens, screenId) === undefined\n}\n\n/**\n * The extra option a Screen picker needs when its stored value matches none\n * of the host's screens (AGL-1893).\n *\n * The console builds `SCREEN_SELECT` options from the routing map, so a\n * value the map has lost — the retired `/blog` screen, say — matches\n * nothing and the field renders EMPTY. Which reads, to the person looking\n * at it, as \"no link set\". Meanwhile the element still behaves as linked:\n * the strip is still a nav landmark, that tab is still skipped as\n * \"navigates\", and the panel it would have revealed is still suppressed. So\n * the one surface where this is repairable was also the one actively\n * denying there was anything to repair.\n *\n * Returns the stored value UNCHANGED as the option's value: naming a dead\n * target must not rewrite it, or opening the panel and pressing Save would\n * quietly convert a recoverable reference into something else.\n *\n * Three shapes get named, because a Screen picker can legally hold any of\n * them (AGL-1335 / AGL-1894 / AGL-2799): a screen reference whose screen is\n * gone, a collection listing whose collection is gone, and a plain address\n * typed in before the picker existed. The last is not broken — but it is\n * just as invisible, and an author who cannot see it is the reason those\n * links are not rename-safe yet.\n */\nexport function unavailableScreenLabel(\n screenId: string,\n screensKnown: boolean,\n): string {\n // With no map loaded yet nothing is known to be missing, so the id is\n // shown plainly. Flashing \"unavailable\" over every link for the beat\n // before the console's host subscription lands would teach authors to\n // ignore the warning that matters.\n if (!screensKnown) return screenId\n // A listing has no publish state of its own: its collection is either gone\n // or no longer has the slug its address is built from. A feed hangs off the\n // same listing, so it is lost the same two ways (AGL-3118).\n const collectionId = parseCollectionLinkValue(screenId)\n if (collectionId) {\n return `⚠ Unavailable collection listing (${collectionId}) — deleted or has no slug`\n }\n const feedCollectionId = parseFeedLinkValue(screenId)\n if (feedCollectionId) {\n return `⚠ Unavailable RSS feed (${feedCollectionId}) — collection deleted or has no slug`\n }\n const entry = parseEntryLinkValue(screenId)\n return entry\n ? `⚠ Unavailable entry (${entry.entryId}) — unpublished or deleted`\n : `⚠ Unavailable page (${screenId}) — unpublished or deleted`\n}\n\nexport function unresolvedScreenOption(\n value: unknown,\n screens: ScreenRouteMap | undefined,\n): { value: string; label: string } | undefined {\n const raw = typeof value === 'string' ? value.trim() : ''\n if (!raw) return undefined\n const target = splitLinkValue(raw, undefined)\n if (target.screenId) {\n if (resolveScreenHref(screens, target.screenId) !== undefined) {\n return undefined\n }\n return {\n value: raw,\n label: unavailableScreenLabel(\n target.screenId,\n screenRoutesAnswerFor(screens, target.screenId),\n ),\n }\n }\n return target.href\n ? { value: raw, label: `⚠ Plain address (${target.href}) — not a page` }\n : undefined\n}\n\n/** One target a link picker offers — see {@link screenLinkTargetOptions}. */\nexport interface ScreenLinkTargetOption {\n /**\n * The routing-map key: a bare screen id, a collection listing's\n * `collection:<id>`, or its feed's `feed:<id>`. Stored as it is in a screen\n * slot, and through `formatScreenLinkValue` in a `Link`-typed prop.\n */\n value: string\n label: string\n kind: 'screen' | 'collection-listing' | 'feed'\n}\n\n/**\n * What a picker calls one routing-map target, or `undefined` for a key the\n * map does not hold — the label {@link screenLinkTargetOptions} gives it.\n *\n * A listing and a feed are labeled with their collection's name and address,\n * like a screen, and marked as what they are IN the label rather than only by\n * position: a closed picker shows the chosen label and nothing else, and\n * \"Blog (/blog)\" there reads exactly like a screen named Blog. A feed borrows\n * its collection's name, which is the only name it has (AGL-3119).\n *\n * An entry is never named here. A map holds only the entries a page\n * references, under no label, so an entry is named by the search seam\n * (`LinkTargetSearchContext`), which reads the entry itself.\n */\nexport function screenLinkTargetLabel(\n key: string,\n screens: ScreenRouteMap | undefined,\n labels: Record<string, string> | undefined,\n): string | undefined {\n const href = resolveScreenHref(screens, key)\n if (href === undefined) return undefined\n const collectionId = parseCollectionLinkValue(key)\n if (collectionId) {\n return `${labels?.[key] ?? collectionId} (${href}) — collection listing`\n }\n const feedCollectionId = parseFeedLinkValue(key)\n if (feedCollectionId) {\n const name =\n labels?.[key] ??\n labels?.[formatCollectionLinkValue(feedCollectionId)] ??\n feedCollectionId\n return `${name} (${href}) — RSS feed`\n }\n if (parseEntryLinkValue(key)) return undefined\n return `${labels?.[key] ?? key} (${href})`\n}\n\n/**\n * Everything a Screen picker offers, in the order it offers it (AGL-2799):\n * the host's screens, then its content collections' listing pages, then\n * their RSS feeds (AGL-3119).\n *\n * ONE builder for both pickers — the attributes panel's `SCREEN_SELECT` and\n * the `Link`-typed prop picker. Each used to map the routing map into options\n * by itself, so a target added to one list was missing from the other; and\n * neither offered a collection's listing at all, which is how the drawer on\n * aglyn.com came to link its blog as a typed `/blog`.\n *\n * Entries are left out even when the map holds some: they are searched, not\n * listed, because a site's entries are far too many to read into a dropdown\n * — see `LinkTargetSearchContext`.\n *\n * `order` keeps each picker's established screen order — by path in the\n * attributes panel, by name in the prop picker — and the listings and feeds\n * follow the screens in the same order.\n */\nexport function screenLinkTargetOptions(\n screens: ScreenRouteMap | undefined,\n labels: Record<string, string> | undefined,\n order: 'path' | 'label' = 'path',\n): ScreenLinkTargetOption[] {\n type Ranked = ScreenLinkTargetOption & { path: string }\n const pages: Ranked[] = []\n const listings: Ranked[] = []\n const feeds: Ranked[] = []\n for (const [key, path] of Object.entries(screens ?? {})) {\n const kind = linkTargetKind(key)\n if (kind === 'entry') continue\n const label = screenLinkTargetLabel(key, screens, labels) ?? key\n if (kind === 'collection') {\n listings.push({ value: key, label, kind: 'collection-listing', path })\n } else if (kind === 'feed') {\n feeds.push({ value: key, label, kind: 'feed', path })\n } else {\n pages.push({ value: key, label, kind: 'screen', path })\n }\n }\n const compare = (a: Ranked, b: Ranked) =>\n order === 'path'\n ? a.path.localeCompare(b.path)\n : a.label.localeCompare(b.label)\n return [\n ...pages.sort(compare),\n ...listings.sort(compare),\n ...feeds.sort(compare),\n ].map(({ value, label, kind }) => ({ value, label, kind }))\n}\n\n/**\n * Marks the element in the DOM. Present on the live site too, deliberately:\n * a smoke pass can then find every dead control on a page with one selector\n * instead of reading five different components' conditions.\n */\nexport const BROKEN_SCREEN_LINK_ATTR = 'data-aglyn-broken-link'\n\n/** What the AUTHOR is told, on the one surface that can fix it. */\nexport const BROKEN_SCREEN_LINK_MESSAGE =\n 'Broken link: this points at a page or collection listing that is ' +\n 'unpublished or deleted, so it will not work on the published site. Pick ' +\n 'a target again in the attributes panel, or clear the link.'\n\n/**\n * The editor-only outline. A warning ring rather than an error one: the\n * screen may be deliberately unpublished and about to come back, and the\n * page is not broken, one control on it is.\n */\nexport const BROKEN_SCREEN_LINK_SX = {\n outline: '2px dashed',\n outlineColor: 'warning.main',\n outlineOffset: '2px',\n borderRadius: 1,\n}\n\n/**\n * Props that make a dead screen link visible to its author (AGL-1893).\n *\n * The tooltip and the ring are `editorInert` only — that is the static\n * besigner canvas, the one surface where the person who can fix this is\n * looking at it. Preview deliberately does NOT get them: preview exists to\n * show what visitors will see, and an authoring annotation painted into it\n * is a lie of a different kind.\n *\n * The data attribute ships everywhere, canvas and live site alike.\n */\nexport function brokenScreenLinkProps(\n broken: boolean,\n editorInert: boolean,\n): Record<string, unknown> {\n if (!broken) return {}\n return {\n [BROKEN_SCREEN_LINK_ATTR]: '',\n ...(editorInert ? { title: BROKEN_SCREEN_LINK_MESSAGE } : null),\n }\n}\n\n/**\n * Link VALUE parsing AND resolution live in `./screen-link-value` —\n * server-safe, because the where-used scan and the Markdown representation of\n * a page both run on the server, and the `createContext` this module reaches\n * keeps it out of the `@aglyn/aglyn/server` barrel (AGL-703, AGL-2740).\n * Re-exported here so every existing importer, and the spec beside this file,\n * keeps reaching them at the address they have always used.\n */\nexport {\n COLLECTION_LINK_VALUE_PREFIX,\n ENTRY_LINK_VALUE_PREFIX,\n EXTERNAL_HREF_PATTERN,\n FEED_LINK_VALUE_PREFIX,\n SAFE_HREF_PATTERN,\n SCREEN_LINK_VALUE_PREFIX,\n formatCollectionLinkValue,\n formatEntryLinkValue,\n formatFeedLinkValue,\n formatScreenLinkValue,\n linkTargetKind,\n nodesReferenceScreen,\n parseCollectionLinkValue,\n parseEntryLinkValue,\n parseFeedLinkValue,\n parseScreenLinkValue,\n resolveScreenHref,\n screenRoutesAnswerFor,\n splitLinkValue,\n type LinkTargetKind,\n} from './screen-link-value'\n\n/** What a linking element renders — see {@link useLinkTarget}. */\nexport interface ResolvedLinkTarget extends ResolvedScreenLink {\n /** The literal href in play once safety-checked, when no screen resolved. */\n externalHref?: string\n /** True when `href` leaves the site — what \"open in a new tab\" may act on. */\n leavesSite: boolean\n}\n\n/**\n * The single href a linking element should render, from the `screenId` +\n * `href` pair every one of them declares (AGL-1335).\n *\n * Button, Screen Link, Link Box and Image each had the same four lines —\n * resolve the id, safety-check the URL, prefer the id — which is three\n * copies too many now that a `Link`-typed component prop can feed either\n * slot with either shape. Hook-shaped because {@link useScreenLink} is;\n * it is always called, so the hook order never depends on the values.\n */\nexport function useLinkTarget(\n screenId: string | null | undefined,\n href: string | null | undefined,\n): ResolvedLinkTarget {\n const target = splitLinkValue(screenId, href)\n const resolved = useScreenLink(target.screenId)\n const { leavingNotice } = useContext(ScreenLinkContext)\n const externalHref =\n target.href && SAFE_HREF_PATTERN.test(target.href) ? target.href : undefined\n const finalHref = target.screenId ? resolved.href : externalHref\n /*\n * A new free site's links to other domains go through the leaving notice on\n * its own host (AGL-3452). Here rather than in each element so the served\n * HTML already carries the notice's address: it holds without JavaScript,\n * and it is what a hover, a copied link and \"Open in new tab\" all see.\n *\n * The notice is on the site, so the rewritten link no longer leaves it —\n * and keeping the referrer is what lets the notice's Go back return to the\n * page the visitor came from.\n */\n const noticeHref =\n !target.screenId && !resolved.suppressNavigation\n ? routeThroughLeavingNotice(externalHref, leavingNotice)\n : undefined\n return {\n ...resolved,\n href: noticeHref ?? finalHref,\n externalHref,\n leavesSite: Boolean(\n !noticeHref &&\n !target.screenId &&\n externalHref &&\n EXTERNAL_HREF_PATTERN.test(externalHref),\n ),\n }\n}\n\n/**\n * Resolves a screen id to its current href. Memoized on the routing-map\n * identity: a slug rename, parent-slug change, or re-parent produces a new\n * map value, so cached hrefs reset exactly when the map changes. Returns no\n * href for unknown/unpublished ids — callers degrade to plain content\n * instead of rendering a dead link.\n */\nexport function useScreenLink(\n screenId: string | null | undefined,\n): ResolvedScreenLink {\n const { screens, suppressNavigation, editorInert } = useContext(ScreenLinkContext)\n const href = useMemo(() => {\n if (!screenId) return undefined\n const resolved = resolveScreenHref(screens, screenId)\n if (resolved === undefined) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[ScreenLink] screen \"${screenId}\" has no routing-map entry — ` +\n 'it may be unpublished or deleted; rendering without an href.',\n )\n }\n return undefined\n }\n return resolved\n }, [screenId, screens])\n return {\n href,\n suppressNavigation: Boolean(suppressNavigation),\n editorInert: Boolean(editorInert),\n // NOT `!href`: a suppressed surface has no href for links that resolve\n // perfectly well, and the whole point of this flag is to separate the\n // two (AGL-1893).\n broken: isScreenLinkBroken(screens, screenId),\n }\n}\n"],"names":["useContext","useMemo","routeThroughLeavingNotice","ScreenLinkContext","EXTERNAL_HREF_PATTERN","SAFE_HREF_PATTERN","formatCollectionLinkValue","linkTargetKind","parseCollectionLinkValue","parseEntryLinkValue","parseFeedLinkValue","resolveScreenHref","screenRoutesAnswerFor","splitLinkValue","isScreenLinkBroken","screens","screenId","undefined","unavailableScreenLabel","screensKnown","collectionId","feedCollectionId","entry","entryId","unresolvedScreenOption","value","raw","trim","target","label","href","screenLinkTargetLabel","key","labels","name","screenLinkTargetOptions","order","pages","listings","feeds","path","Object","entries","kind","push","compare","a","b","localeCompare","sort","map","BROKEN_SCREEN_LINK_ATTR","BROKEN_SCREEN_LINK_MESSAGE","BROKEN_SCREEN_LINK_SX","outline","outlineColor","outlineOffset","borderRadius","brokenScreenLinkProps","broken","editorInert","title","COLLECTION_LINK_VALUE_PREFIX","ENTRY_LINK_VALUE_PREFIX","FEED_LINK_VALUE_PREFIX","SCREEN_LINK_VALUE_PREFIX","formatEntryLinkValue","formatFeedLinkValue","formatScreenLinkValue","nodesReferenceScreen","parseScreenLinkValue","useLinkTarget","resolved","useScreenLink","leavingNotice","externalHref","test","finalHref","noticeHref","suppressNavigation","leavesSite","Boolean","process","env","NODE_ENV","console","warn"],"mappings":"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"}
@@ -71,6 +71,9 @@ export declare function normalizeScreenSlug(input: string | null | undefined): s
71
71
  * catch-all is required, not optional — and it is reserved anyway for the
72
72
  * reason given below about child paths: a child only exists when its
73
73
  * parent is published, and this rule refuses the parent.
74
+ * - `_aglyn` — the platform's own pages on every site, the leaving notice
75
+ * at `/_aglyn/leaving` first (AGL-3452). The middleware rewrites that path
76
+ * to an api route before the catch-all could see it.
74
77
  *
75
78
  * NOT reserved, and deliberately so, because measuring found them fine:
76
79
  * `401`, `403`, `503` (`/401` returns 200 through the catch-all — two of the
@@ -17,6 +17,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
17
17
  */ import { HOST_ERROR_SCREEN_SLOTS } from "../foundation/index.js";
18
18
  import { collectionListUrl } from "./collection-entries.js";
19
19
  import { ENTRY_PREVIEW_ROUTE_SEGMENT } from "./entry-preview-link.js";
20
+ import { LEAVING_NOTICE_ROUTE_SEGMENT } from "./leaving-notice.js";
20
21
  import { PLATFORM_BRAND_NAME } from "./platform-brand.js";
21
22
  import { formatCollectionLinkValue, formatEntryLinkValue, formatFeedLinkValue, parseEntryLinkValue } from "./screen-link-value.js";
22
23
  /**
@@ -79,6 +80,9 @@ import { formatCollectionLinkValue, formatEntryLinkValue, formatFeedLinkValue, p
79
80
  * catch-all is required, not optional — and it is reserved anyway for the
80
81
  * reason given below about child paths: a child only exists when its
81
82
  * parent is published, and this rule refuses the parent.
83
+ * - `_aglyn` — the platform's own pages on every site, the leaving notice
84
+ * at `/_aglyn/leaving` first (AGL-3452). The middleware rewrites that path
85
+ * to an api route before the catch-all could see it.
82
86
  *
83
87
  * NOT reserved, and deliberately so, because measuring found them fine:
84
88
  * `401`, `403`, `503` (`/401` returns 200 through the catch-all — two of the
@@ -96,7 +100,8 @@ import { formatCollectionLinkValue, formatEntryLinkValue, formatFeedLinkValue, p
96
100
  ENTRY_PREVIEW_ROUTE_SEGMENT,
97
101
  'api',
98
102
  '_next',
99
- '_static'
103
+ '_static',
104
+ LEAVING_NOTICE_ROUTE_SEGMENT
100
105
  ];
101
106
  /**
102
107
  * The reserved segment a routing-map path would collide with, or `undefined`