@cardstack/boxel-cli 0.2.0 → 0.3.0-unstable.13

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 (879) hide show
  1. package/bundled-types/base/-private.d.ts +1 -0
  2. package/bundled-types/base/address.d.ts +17 -0
  3. package/bundled-types/base/ai-app-generator.d.ts +14 -0
  4. package/bundled-types/base/amount-with-currency.d.ts +10 -0
  5. package/bundled-types/base/avif-image-def.d.ts +12 -0
  6. package/bundled-types/base/avif-meta-extractor.d.ts +4 -0
  7. package/bundled-types/base/base64-image.d.ts +25 -0
  8. package/bundled-types/base/big-integer.d.ts +12 -0
  9. package/bundled-types/base/boolean.d.ts +14 -0
  10. package/bundled-types/base/brand-functional-palette.d.ts +16 -0
  11. package/bundled-types/base/brand-guide.d.ts +26 -0
  12. package/bundled-types/base/brand-logo.d.ts +29 -0
  13. package/bundled-types/base/card-api.d.ts +513 -0
  14. package/bundled-types/base/card-serialization.d.ts +47 -0
  15. package/bundled-types/base/cards-grid.d.ts +10 -0
  16. package/bundled-types/base/code-ref.d.ts +14 -0
  17. package/bundled-types/base/codemirror-editor.d.ts +135 -0
  18. package/bundled-types/base/color-field/components/advanced-color-picker.d.ts +66 -0
  19. package/bundled-types/base/color-field/components/color-picker-field.d.ts +22 -0
  20. package/bundled-types/base/color-field/components/color-wheel-picker.d.ts +44 -0
  21. package/bundled-types/base/color-field/components/contrast-checker-addon.d.ts +14 -0
  22. package/bundled-types/base/color-field/components/recent-colors-addon.d.ts +19 -0
  23. package/bundled-types/base/color-field/components/slider-picker.d.ts +40 -0
  24. package/bundled-types/base/color-field/components/swatches-picker.d.ts +5 -0
  25. package/bundled-types/base/color-field/modifiers/setup-element-modifier.d.ts +10 -0
  26. package/bundled-types/base/color-field/util/color-field-signature.d.ts +9 -0
  27. package/bundled-types/base/color-field/util/color-utils.d.ts +96 -0
  28. package/bundled-types/base/color-field/util/css-color-parsers.d.ts +11 -0
  29. package/bundled-types/base/color.d.ts +10 -0
  30. package/bundled-types/base/command.d.ts +593 -0
  31. package/bundled-types/base/commands/search-card-result.d.ts +36 -0
  32. package/bundled-types/base/components/age.d.ts +22 -0
  33. package/bundled-types/base/components/business-days.d.ts +16 -0
  34. package/bundled-types/base/components/card-list.d.ts +25 -0
  35. package/bundled-types/base/components/cards-grid-layout.d.ts +58 -0
  36. package/bundled-types/base/components/countdown.d.ts +31 -0
  37. package/bundled-types/base/components/expiration-warning.d.ts +24 -0
  38. package/bundled-types/base/components/time-ago.d.ts +24 -0
  39. package/bundled-types/base/components/time-slots.d.ts +20 -0
  40. package/bundled-types/base/components/timeline.d.ts +22 -0
  41. package/bundled-types/base/contains-many-component.d.ts +7 -0
  42. package/bundled-types/base/coordinate.d.ts +7 -0
  43. package/bundled-types/base/country.d.ts +23 -0
  44. package/bundled-types/base/css-value.d.ts +6 -0
  45. package/bundled-types/base/csv-file-def.d.ts +29 -0
  46. package/bundled-types/base/currency.d.ts +16 -0
  47. package/bundled-types/base/date/day.d.ts +9 -0
  48. package/bundled-types/base/date/month-day.d.ts +10 -0
  49. package/bundled-types/base/date/month-year.d.ts +9 -0
  50. package/bundled-types/base/date/month.d.ts +9 -0
  51. package/bundled-types/base/date/quarter.d.ts +10 -0
  52. package/bundled-types/base/date/week.d.ts +9 -0
  53. package/bundled-types/base/date/year.d.ts +9 -0
  54. package/bundled-types/base/date-range-field.d.ts +12 -0
  55. package/bundled-types/base/date.d.ts +12 -0
  56. package/bundled-types/base/datetime-stamp.d.ts +7 -0
  57. package/bundled-types/base/datetime.d.ts +12 -0
  58. package/bundled-types/base/default-templates/atom.d.ts +10 -0
  59. package/bundled-types/base/default-templates/card-info.d.ts +32 -0
  60. package/bundled-types/base/default-templates/embedded.d.ts +11 -0
  61. package/bundled-types/base/default-templates/field-edit.d.ts +10 -0
  62. package/bundled-types/base/default-templates/file-def-edit.d.ts +8 -0
  63. package/bundled-types/base/default-templates/fitted.d.ts +12 -0
  64. package/bundled-types/base/default-templates/head.d.ts +13 -0
  65. package/bundled-types/base/default-templates/image-def-atom.d.ts +8 -0
  66. package/bundled-types/base/default-templates/image-def-embedded.d.ts +8 -0
  67. package/bundled-types/base/default-templates/image-def-fitted.d.ts +8 -0
  68. package/bundled-types/base/default-templates/image-def-isolated.d.ts +8 -0
  69. package/bundled-types/base/default-templates/isolated-and-edit.d.ts +15 -0
  70. package/bundled-types/base/default-templates/markdown-fallback.d.ts +19 -0
  71. package/bundled-types/base/default-templates/markdown.d.ts +60 -0
  72. package/bundled-types/base/default-templates/missing-template.d.ts +13 -0
  73. package/bundled-types/base/default-templates/theme-dashboard.d.ts +126 -0
  74. package/bundled-types/base/detailed-style-reference.d.ts +48 -0
  75. package/bundled-types/base/email.d.ts +10 -0
  76. package/bundled-types/base/enum.d.ts +30 -0
  77. package/bundled-types/base/ethereum-address.d.ts +13 -0
  78. package/bundled-types/base/field-component.d.ts +65 -0
  79. package/bundled-types/base/field-support.d.ts +55 -0
  80. package/bundled-types/base/file-api.d.ts +2 -0
  81. package/bundled-types/base/file-menu-items.d.ts +4 -0
  82. package/bundled-types/base/gif-image-def.d.ts +12 -0
  83. package/bundled-types/base/gif-meta-extractor.d.ts +4 -0
  84. package/bundled-types/base/gts-file-def.d.ts +7 -0
  85. package/bundled-types/base/helpers/country.d.ts +3198 -0
  86. package/bundled-types/base/helpers/sanitized-html.d.ts +2 -0
  87. package/bundled-types/base/helpers/set-background-image.d.ts +2 -0
  88. package/bundled-types/base/image-file-def.d.ts +1 -0
  89. package/bundled-types/base/image.d.ts +8 -0
  90. package/bundled-types/base/index.d.ts +1 -0
  91. package/bundled-types/base/join-the-community.d.ts +14 -0
  92. package/bundled-types/base/jpg-image-def.d.ts +12 -0
  93. package/bundled-types/base/jpg-meta-extractor.d.ts +4 -0
  94. package/bundled-types/base/json-file-def.d.ts +23 -0
  95. package/bundled-types/base/links-to-editor.d.ts +22 -0
  96. package/bundled-types/base/links-to-many-component.d.ts +7 -0
  97. package/bundled-types/base/llm-model.d.ts +6 -0
  98. package/bundled-types/base/markdown-file-def.d.ts +25 -0
  99. package/bundled-types/base/markdown-helpers.d.ts +26 -0
  100. package/bundled-types/base/markdown.d.ts +1 -0
  101. package/bundled-types/base/matrix-event.d.ts +400 -0
  102. package/bundled-types/base/menu-items.d.ts +21 -0
  103. package/bundled-types/base/number/components/badge-counter.d.ts +32 -0
  104. package/bundled-types/base/number/components/badge-metric.d.ts +34 -0
  105. package/bundled-types/base/number/components/badge-notification.d.ts +38 -0
  106. package/bundled-types/base/number/components/gauge.d.ts +45 -0
  107. package/bundled-types/base/number/components/number-input.d.ts +27 -0
  108. package/bundled-types/base/number/components/progress-bar.d.ts +44 -0
  109. package/bundled-types/base/number/components/progress-circle.d.ts +41 -0
  110. package/bundled-types/base/number/components/score.d.ts +41 -0
  111. package/bundled-types/base/number/components/stat.d.ts +38 -0
  112. package/bundled-types/base/number/util/index.d.ts +29 -0
  113. package/bundled-types/base/number.d.ts +52 -0
  114. package/bundled-types/base/percentage.d.ts +9 -0
  115. package/bundled-types/base/phone-number.d.ts +25 -0
  116. package/bundled-types/base/png-image-def.d.ts +12 -0
  117. package/bundled-types/base/png-meta-extractor.d.ts +4 -0
  118. package/bundled-types/base/positioned-card.d.ts +7 -0
  119. package/bundled-types/base/query-field-support.d.ts +6 -0
  120. package/bundled-types/base/realm-config.d.ts +16 -0
  121. package/bundled-types/base/realm.d.ts +7 -0
  122. package/bundled-types/base/resources/command-data.d.ts +43 -0
  123. package/bundled-types/base/response-field.d.ts +6 -0
  124. package/bundled-types/base/rich-markdown.d.ts +30 -0
  125. package/bundled-types/base/shared-state.d.ts +1 -0
  126. package/bundled-types/base/skill-plus.d.ts +79 -0
  127. package/bundled-types/base/skill-reference.d.ts +12 -0
  128. package/bundled-types/base/skill-set.d.ts +18 -0
  129. package/bundled-types/base/skill.d.ts +21 -0
  130. package/bundled-types/base/spec.d.ts +108 -0
  131. package/bundled-types/base/string.d.ts +2 -0
  132. package/bundled-types/base/structured-theme-variables.d.ts +85 -0
  133. package/bundled-types/base/structured-theme.d.ts +32 -0
  134. package/bundled-types/base/style-reference.d.ts +14 -0
  135. package/bundled-types/base/svg-image-def.d.ts +12 -0
  136. package/bundled-types/base/svg-meta-extractor.d.ts +12 -0
  137. package/bundled-types/base/system-card.d.ts +17 -0
  138. package/bundled-types/base/tag.d.ts +15 -0
  139. package/bundled-types/base/text-area.d.ts +2 -0
  140. package/bundled-types/base/text-file-def.d.ts +23 -0
  141. package/bundled-types/base/text-input-validator.d.ts +13 -0
  142. package/bundled-types/base/theme.d.ts +2 -0
  143. package/bundled-types/base/time/duration.d.ts +14 -0
  144. package/bundled-types/base/time/relative-time.d.ts +10 -0
  145. package/bundled-types/base/time/time-range.d.ts +11 -0
  146. package/bundled-types/base/time.d.ts +9 -0
  147. package/bundled-types/base/ts-file-def.d.ts +26 -0
  148. package/bundled-types/base/ts-highlight.d.ts +1 -0
  149. package/bundled-types/base/types/@cardstack/boxel-host/index.d.ts +5 -0
  150. package/bundled-types/base/types/ember-css-url/index.d.ts +15 -0
  151. package/bundled-types/base/typography.d.ts +14 -0
  152. package/bundled-types/base/url.d.ts +13 -0
  153. package/bundled-types/base/watched-array.d.ts +7 -0
  154. package/bundled-types/base/webp-image-def.d.ts +12 -0
  155. package/bundled-types/base/webp-meta-extractor.d.ts +4 -0
  156. package/bundled-types/base/website.d.ts +8 -0
  157. package/bundled-types/base/welcome-to-boxel.d.ts +22 -0
  158. package/bundled-types/boxel-ui/components/accordion/index.gts +50 -0
  159. package/bundled-types/boxel-ui/components/accordion/item/index.gts +156 -0
  160. package/bundled-types/boxel-ui/components/accordion/usage.gts +157 -0
  161. package/bundled-types/boxel-ui/components/add-button/index.gts +46 -0
  162. package/bundled-types/boxel-ui/components/add-button/usage.gts +54 -0
  163. package/bundled-types/boxel-ui/components/alert/index.gts +151 -0
  164. package/bundled-types/boxel-ui/components/alert/usage.gts +66 -0
  165. package/bundled-types/boxel-ui/components/avatar/index.gts +79 -0
  166. package/bundled-types/boxel-ui/components/avatar/usage.gts +96 -0
  167. package/bundled-types/boxel-ui/components/basic-fitted/index.gts +340 -0
  168. package/bundled-types/boxel-ui/components/basic-fitted/usage.gts +223 -0
  169. package/bundled-types/boxel-ui/components/button/index.gts +416 -0
  170. package/bundled-types/boxel-ui/components/button/usage.gts +334 -0
  171. package/bundled-types/boxel-ui/components/card-container/index.gts +251 -0
  172. package/bundled-types/boxel-ui/components/card-container/usage.gts +53 -0
  173. package/bundled-types/boxel-ui/components/card-header/index.gts +473 -0
  174. package/bundled-types/boxel-ui/components/card-header/usage.gts +295 -0
  175. package/bundled-types/boxel-ui/components/circle-spinner/index.gts +42 -0
  176. package/bundled-types/boxel-ui/components/circle-spinner/usage.gts +48 -0
  177. package/bundled-types/boxel-ui/components/color-palette/index.gts +134 -0
  178. package/bundled-types/boxel-ui/components/color-palette/usage.gts +69 -0
  179. package/bundled-types/boxel-ui/components/color-picker/index.gts +125 -0
  180. package/bundled-types/boxel-ui/components/color-picker/usage.gts +56 -0
  181. package/bundled-types/boxel-ui/components/container/index.gts +67 -0
  182. package/bundled-types/boxel-ui/components/container/usage.gts +59 -0
  183. package/bundled-types/boxel-ui/components/context-button/index.gts +184 -0
  184. package/bundled-types/boxel-ui/components/context-button/usage.gts +125 -0
  185. package/bundled-types/boxel-ui/components/copy-button/index.gts +79 -0
  186. package/bundled-types/boxel-ui/components/copy-button/usage.gts +25 -0
  187. package/bundled-types/boxel-ui/components/date-range-picker/index.gts +211 -0
  188. package/bundled-types/boxel-ui/components/date-range-picker/setup.gts +11 -0
  189. package/bundled-types/boxel-ui/components/date-range-picker/usage.gts +52 -0
  190. package/bundled-types/boxel-ui/components/drag-and-drop/index.gts +256 -0
  191. package/bundled-types/boxel-ui/components/drag-and-drop/usage.gts +142 -0
  192. package/bundled-types/boxel-ui/components/dropdown/index.gts +478 -0
  193. package/bundled-types/boxel-ui/components/dropdown/trigger/index.gts +70 -0
  194. package/bundled-types/boxel-ui/components/dropdown/trigger/usage.gts +54 -0
  195. package/bundled-types/boxel-ui/components/dropdown/usage.gts +119 -0
  196. package/bundled-types/boxel-ui/components/email-input/index.gts +111 -0
  197. package/bundled-types/boxel-ui/components/email-input/usage.gts +75 -0
  198. package/bundled-types/boxel-ui/components/entity-icon-display/index.gts +144 -0
  199. package/bundled-types/boxel-ui/components/entity-icon-display/usage.gts +54 -0
  200. package/bundled-types/boxel-ui/components/entity-thumbnail-display/index.gts +148 -0
  201. package/bundled-types/boxel-ui/components/entity-thumbnail-display/usage.gts +76 -0
  202. package/bundled-types/boxel-ui/components/field-container/index.gts +150 -0
  203. package/bundled-types/boxel-ui/components/field-container/usage.gts +188 -0
  204. package/bundled-types/boxel-ui/components/filter-list/index.gts +255 -0
  205. package/bundled-types/boxel-ui/components/filter-list/usage.gts +153 -0
  206. package/bundled-types/boxel-ui/components/fitted-card-container/index.gts +66 -0
  207. package/bundled-types/boxel-ui/components/fitted-card-container/usage.gts +81 -0
  208. package/bundled-types/boxel-ui/components/grid-container/grid-item-container/index.gts +37 -0
  209. package/bundled-types/boxel-ui/components/grid-container/index.gts +116 -0
  210. package/bundled-types/boxel-ui/components/grid-container/usage.gts +105 -0
  211. package/bundled-types/boxel-ui/components/header/index.gts +101 -0
  212. package/bundled-types/boxel-ui/components/header/usage.gts +116 -0
  213. package/bundled-types/boxel-ui/components/icon-button/index.gts +148 -0
  214. package/bundled-types/boxel-ui/components/icon-button/usage.gts +249 -0
  215. package/bundled-types/boxel-ui/components/input/index.gts +560 -0
  216. package/bundled-types/boxel-ui/components/input/usage.gts +449 -0
  217. package/bundled-types/boxel-ui/components/input-group/accessories/index.gts +191 -0
  218. package/bundled-types/boxel-ui/components/input-group/controls/index.gts +54 -0
  219. package/bundled-types/boxel-ui/components/input-group/index.gts +347 -0
  220. package/bundled-types/boxel-ui/components/input-group/usage.gts +437 -0
  221. package/bundled-types/boxel-ui/components/kanban/card.gts +89 -0
  222. package/bundled-types/boxel-ui/components/kanban/column-header.gts +151 -0
  223. package/bundled-types/boxel-ui/components/kanban/drag.gts +1007 -0
  224. package/bundled-types/boxel-ui/components/kanban/engine.ts +247 -0
  225. package/bundled-types/boxel-ui/components/kanban/ghost.gts +64 -0
  226. package/bundled-types/boxel-ui/components/kanban/index.gts +16 -0
  227. package/bundled-types/boxel-ui/components/kanban/modifiers.gts +42 -0
  228. package/bundled-types/boxel-ui/components/kanban/plane-inner.gts +525 -0
  229. package/bundled-types/boxel-ui/components/kanban/plane.gts +163 -0
  230. package/bundled-types/boxel-ui/components/kanban/usage.gts +392 -0
  231. package/bundled-types/boxel-ui/components/label/index.gts +64 -0
  232. package/bundled-types/boxel-ui/components/loading-indicator/index.gts +78 -0
  233. package/bundled-types/boxel-ui/components/loading-indicator/usage.gts +72 -0
  234. package/bundled-types/boxel-ui/components/menu/index.gts +267 -0
  235. package/bundled-types/boxel-ui/components/menu/usage.gts +100 -0
  236. package/bundled-types/boxel-ui/components/message/index.gts +146 -0
  237. package/bundled-types/boxel-ui/components/message/usage.gts +263 -0
  238. package/bundled-types/boxel-ui/components/modal/index.gts +159 -0
  239. package/bundled-types/boxel-ui/components/modal/usage.gts +206 -0
  240. package/bundled-types/boxel-ui/components/multi-select/after-options.gts +59 -0
  241. package/bundled-types/boxel-ui/components/multi-select/index.gts +230 -0
  242. package/bundled-types/boxel-ui/components/multi-select/selected-item.gts +91 -0
  243. package/bundled-types/boxel-ui/components/multi-select/trigger.gts +190 -0
  244. package/bundled-types/boxel-ui/components/multi-select/usage.gts +420 -0
  245. package/bundled-types/boxel-ui/components/phone-input/index.gts +149 -0
  246. package/bundled-types/boxel-ui/components/phone-input/usage.gts +73 -0
  247. package/bundled-types/boxel-ui/components/picker/before-options-with-search.gts +340 -0
  248. package/bundled-types/boxel-ui/components/picker/index.gts +427 -0
  249. package/bundled-types/boxel-ui/components/picker/option-row.gts +270 -0
  250. package/bundled-types/boxel-ui/components/picker/selected-item.gts +171 -0
  251. package/bundled-types/boxel-ui/components/picker/trigger-labeled.gts +216 -0
  252. package/bundled-types/boxel-ui/components/picker/usage.gts +179 -0
  253. package/bundled-types/boxel-ui/components/pill/index.gts +231 -0
  254. package/bundled-types/boxel-ui/components/pill/usage.gts +230 -0
  255. package/bundled-types/boxel-ui/components/progress-bar/index.gts +124 -0
  256. package/bundled-types/boxel-ui/components/progress-bar/usage.gts +131 -0
  257. package/bundled-types/boxel-ui/components/progress-radial/index.gts +95 -0
  258. package/bundled-types/boxel-ui/components/progress-radial/usage.gts +79 -0
  259. package/bundled-types/boxel-ui/components/radio-input/index.gts +143 -0
  260. package/bundled-types/boxel-ui/components/radio-input/item.gts +210 -0
  261. package/bundled-types/boxel-ui/components/radio-input/usage.gts +303 -0
  262. package/bundled-types/boxel-ui/components/realm-icon/index.gts +129 -0
  263. package/bundled-types/boxel-ui/components/realm-icon/usage.gts +62 -0
  264. package/bundled-types/boxel-ui/components/resizable-panel-group/handle.gts +180 -0
  265. package/bundled-types/boxel-ui/components/resizable-panel-group/index.gts +341 -0
  266. package/bundled-types/boxel-ui/components/resizable-panel-group/panel.gts +86 -0
  267. package/bundled-types/boxel-ui/components/resizable-panel-group/usage.gts +350 -0
  268. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/adjust-layout-by-delta.ts +231 -0
  269. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/assert.ts +10 -0
  270. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/calculate-delta-percentage.ts +41 -0
  271. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/calculate-unsafe-default-layout.ts +53 -0
  272. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/compare-layouts.ts +12 -0
  273. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/const.ts +6 -0
  274. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/determine-pivot-indices.ts +15 -0
  275. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/dom/get-panel-element.ts +10 -0
  276. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/dom/get-panel-elements-for-group.ts +8 -0
  277. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/dom/get-panel-group-element.ts +21 -0
  278. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/dom/get-resize-handle-element-index.ts +14 -0
  279. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/dom/get-resize-handle-element.ts +12 -0
  280. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/dom/get-resize-handle-elements-for-group.ts +10 -0
  281. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/fuzzy-layouts-equal.ts +22 -0
  282. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/fuzzy-numbers.ts +21 -0
  283. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/get-resize-event-coordinates.ts +8 -0
  284. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/get-resize-event-cursor-position.ts +10 -0
  285. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/panel-resize-handle-registry.ts +446 -0
  286. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/resize-panel.ts +35 -0
  287. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/types.ts +23 -0
  288. package/bundled-types/boxel-ui/components/resizable-panel-group/utils/validate-panel-group-layout.ts +86 -0
  289. package/bundled-types/boxel-ui/components/select/index.gts +882 -0
  290. package/bundled-types/boxel-ui/components/select/trigger.gts +132 -0
  291. package/bundled-types/boxel-ui/components/select/usage.gts +353 -0
  292. package/bundled-types/boxel-ui/components/skeleton-placeholder/index.gts +89 -0
  293. package/bundled-types/boxel-ui/components/skeleton-placeholder/usage.gts +102 -0
  294. package/bundled-types/boxel-ui/components/sort-dropdown/index.gts +91 -0
  295. package/bundled-types/boxel-ui/components/sort-dropdown/usage.gts +77 -0
  296. package/bundled-types/boxel-ui/components/swatch/index.gts +81 -0
  297. package/bundled-types/boxel-ui/components/swatch/usage.gts +57 -0
  298. package/bundled-types/boxel-ui/components/switch/index.gts +95 -0
  299. package/bundled-types/boxel-ui/components/switch/usage.gts +79 -0
  300. package/bundled-types/boxel-ui/components/tabbed-header/index.gts +141 -0
  301. package/bundled-types/boxel-ui/components/tabbed-header/usage.gts +103 -0
  302. package/bundled-types/boxel-ui/components/tag/index.gts +50 -0
  303. package/bundled-types/boxel-ui/components/tag/usage.gts +89 -0
  304. package/bundled-types/boxel-ui/components/tag-list/index.gts +85 -0
  305. package/bundled-types/boxel-ui/components/tag-list/usage.gts +129 -0
  306. package/bundled-types/boxel-ui/components/tooltip/index.gts +306 -0
  307. package/bundled-types/boxel-ui/components/tooltip/usage.gts +168 -0
  308. package/bundled-types/boxel-ui/components/view-selector/index.gts +166 -0
  309. package/bundled-types/boxel-ui/components/view-selector/usage.gts +149 -0
  310. package/bundled-types/boxel-ui/components.ts +169 -0
  311. package/bundled-types/boxel-ui/helpers/add-class-to-svg.ts +8 -0
  312. package/bundled-types/boxel-ui/helpers/clipboard.ts +21 -0
  313. package/bundled-types/boxel-ui/helpers/cn.ts +12 -0
  314. package/bundled-types/boxel-ui/helpers/color-tools.ts +331 -0
  315. package/bundled-types/boxel-ui/helpers/compact.ts +3 -0
  316. package/bundled-types/boxel-ui/helpers/contrast-color.ts +32 -0
  317. package/bundled-types/boxel-ui/helpers/css-var.ts +41 -0
  318. package/bundled-types/boxel-ui/helpers/currency-format.ts +25 -0
  319. package/bundled-types/boxel-ui/helpers/dayjs-format.ts +27 -0
  320. package/bundled-types/boxel-ui/helpers/deterministic-color-from-string.ts +45 -0
  321. package/bundled-types/boxel-ui/helpers/element.ts +16 -0
  322. package/bundled-types/boxel-ui/helpers/extract-css-variables.ts +126 -0
  323. package/bundled-types/boxel-ui/helpers/format-age.ts +72 -0
  324. package/bundled-types/boxel-ui/helpers/format-countdown.ts +110 -0
  325. package/bundled-types/boxel-ui/helpers/format-currency.ts +95 -0
  326. package/bundled-types/boxel-ui/helpers/format-date-time.ts +567 -0
  327. package/bundled-types/boxel-ui/helpers/format-duration.ts +143 -0
  328. package/bundled-types/boxel-ui/helpers/format-file-size.ts +49 -0
  329. package/bundled-types/boxel-ui/helpers/format-list.ts +73 -0
  330. package/bundled-types/boxel-ui/helpers/format-names.ts +103 -0
  331. package/bundled-types/boxel-ui/helpers/format-number.ts +140 -0
  332. package/bundled-types/boxel-ui/helpers/format-ordinal.ts +115 -0
  333. package/bundled-types/boxel-ui/helpers/format-period.ts +188 -0
  334. package/bundled-types/boxel-ui/helpers/format-relative-time.ts +789 -0
  335. package/bundled-types/boxel-ui/helpers/generate-css-variables.ts +47 -0
  336. package/bundled-types/boxel-ui/helpers/markdown-escape.ts +36 -0
  337. package/bundled-types/boxel-ui/helpers/math-helpers.ts +18 -0
  338. package/bundled-types/boxel-ui/helpers/menu-divider.ts +15 -0
  339. package/bundled-types/boxel-ui/helpers/menu-item.ts +76 -0
  340. package/bundled-types/boxel-ui/helpers/optional.ts +7 -0
  341. package/bundled-types/boxel-ui/helpers/pick.ts +11 -0
  342. package/bundled-types/boxel-ui/helpers/sanitize-html.ts +45 -0
  343. package/bundled-types/boxel-ui/helpers/string.ts +15 -0
  344. package/bundled-types/boxel-ui/helpers/theme-css.ts +148 -0
  345. package/bundled-types/boxel-ui/helpers/truth-helpers.ts +48 -0
  346. package/bundled-types/boxel-ui/helpers/validate-email-format.ts +138 -0
  347. package/bundled-types/boxel-ui/helpers/validate-phone-format.ts +201 -0
  348. package/bundled-types/boxel-ui/helpers.ts +134 -0
  349. package/bundled-types/boxel-ui/icons/ai-bw.gts +22 -0
  350. package/bundled-types/boxel-ui/icons/arrow-left.gts +25 -0
  351. package/bundled-types/boxel-ui/icons/arrow-right.gts +28 -0
  352. package/bundled-types/boxel-ui/icons/arrow-top-left.gts +22 -0
  353. package/bundled-types/boxel-ui/icons/arrow-up.gts +25 -0
  354. package/bundled-types/boxel-ui/icons/atom.gts +26 -0
  355. package/bundled-types/boxel-ui/icons/boxel-icon-with-text.gts +29 -0
  356. package/bundled-types/boxel-ui/icons/boxel-icon.gts +34 -0
  357. package/bundled-types/boxel-ui/icons/card-definition.gts +26 -0
  358. package/bundled-types/boxel-ui/icons/card-instance.gts +26 -0
  359. package/bundled-types/boxel-ui/icons/card.gts +24 -0
  360. package/bundled-types/boxel-ui/icons/cardbot-lg.gts +24 -0
  361. package/bundled-types/boxel-ui/icons/caret-down.gts +23 -0
  362. package/bundled-types/boxel-ui/icons/caret-up.gts +23 -0
  363. package/bundled-types/boxel-ui/icons/check-mark.gts +23 -0
  364. package/bundled-types/boxel-ui/icons/chevron-right.gts +23 -0
  365. package/bundled-types/boxel-ui/icons/copy.gts +28 -0
  366. package/bundled-types/boxel-ui/icons/diagonal-arrow-left-up.gts +21 -0
  367. package/bundled-types/boxel-ui/icons/download.gts +19 -0
  368. package/bundled-types/boxel-ui/icons/dropdown-arrow-down.gts +20 -0
  369. package/bundled-types/boxel-ui/icons/dropdown-arrow-filled.gts +19 -0
  370. package/bundled-types/boxel-ui/icons/dropdown-arrow-up.gts +20 -0
  371. package/bundled-types/boxel-ui/icons/edit.gts +14 -0
  372. package/bundled-types/boxel-ui/icons/embedded.gts +34 -0
  373. package/bundled-types/boxel-ui/icons/exclamation-circle.gts +26 -0
  374. package/bundled-types/boxel-ui/icons/exclamation.gts +20 -0
  375. package/bundled-types/boxel-ui/icons/eye.gts +19 -0
  376. package/bundled-types/boxel-ui/icons/failure-bordered.gts +26 -0
  377. package/bundled-types/boxel-ui/icons/field.gts +26 -0
  378. package/bundled-types/boxel-ui/icons/file-alert.gts +28 -0
  379. package/bundled-types/boxel-ui/icons/file.gts +27 -0
  380. package/bundled-types/boxel-ui/icons/fitted.gts +42 -0
  381. package/bundled-types/boxel-ui/icons/folder.gts +26 -0
  382. package/bundled-types/boxel-ui/icons/form.gts +38 -0
  383. package/bundled-types/boxel-ui/icons/four-lines.gts +21 -0
  384. package/bundled-types/boxel-ui/icons/grid-3x3.gts +26 -0
  385. package/bundled-types/boxel-ui/icons/group.gts +22 -0
  386. package/bundled-types/boxel-ui/icons/head.gts +28 -0
  387. package/bundled-types/boxel-ui/icons/highlight-icon.gts +46 -0
  388. package/bundled-types/boxel-ui/icons/icon-circle-selected.gts +25 -0
  389. package/bundled-types/boxel-ui/icons/icon-circle.gts +25 -0
  390. package/bundled-types/boxel-ui/icons/icon-code.gts +22 -0
  391. package/bundled-types/boxel-ui/icons/icon-funnel.gts +23 -0
  392. package/bundled-types/boxel-ui/icons/icon-globe.gts +19 -0
  393. package/bundled-types/boxel-ui/icons/icon-grid.gts +68 -0
  394. package/bundled-types/boxel-ui/icons/icon-hexagon.gts +22 -0
  395. package/bundled-types/boxel-ui/icons/icon-inherit.gts +19 -0
  396. package/bundled-types/boxel-ui/icons/icon-link.gts +20 -0
  397. package/bundled-types/boxel-ui/icons/icon-list.gts +40 -0
  398. package/bundled-types/boxel-ui/icons/icon-minus-circle.gts +26 -0
  399. package/bundled-types/boxel-ui/icons/icon-pencil-crossed-out.gts +33 -0
  400. package/bundled-types/boxel-ui/icons/icon-pencil-not-crossed-out.gts +21 -0
  401. package/bundled-types/boxel-ui/icons/icon-pencil.gts +23 -0
  402. package/bundled-types/boxel-ui/icons/icon-plus-circle.gts +31 -0
  403. package/bundled-types/boxel-ui/icons/icon-plus-thin.gts +19 -0
  404. package/bundled-types/boxel-ui/icons/icon-plus.gts +20 -0
  405. package/bundled-types/boxel-ui/icons/icon-search-thick.gts +27 -0
  406. package/bundled-types/boxel-ui/icons/icon-search.gts +25 -0
  407. package/bundled-types/boxel-ui/icons/icon-table.gts +44 -0
  408. package/bundled-types/boxel-ui/icons/icon-trash.gts +24 -0
  409. package/bundled-types/boxel-ui/icons/icon-turn-down-right.gts +18 -0
  410. package/bundled-types/boxel-ui/icons/icon-x.gts +21 -0
  411. package/bundled-types/boxel-ui/icons/image-placeholder.gts +30 -0
  412. package/bundled-types/boxel-ui/icons/isolated.gts +20 -0
  413. package/bundled-types/boxel-ui/icons/loading-indicator.gts +26 -0
  414. package/bundled-types/boxel-ui/icons/lock.gts +23 -0
  415. package/bundled-types/boxel-ui/icons/markdown.gts +19 -0
  416. package/bundled-types/boxel-ui/icons/profile.gts +23 -0
  417. package/bundled-types/boxel-ui/icons/publish-site-icon.gts +25 -0
  418. package/bundled-types/boxel-ui/icons/rows-4.gts +26 -0
  419. package/bundled-types/boxel-ui/icons/select-all.gts +25 -0
  420. package/bundled-types/boxel-ui/icons/send.gts +21 -0
  421. package/bundled-types/boxel-ui/icons/sparkle.gts +21 -0
  422. package/bundled-types/boxel-ui/icons/star-filled.gts +25 -0
  423. package/bundled-types/boxel-ui/icons/star-half-fill.gts +27 -0
  424. package/bundled-types/boxel-ui/icons/star.gts +25 -0
  425. package/bundled-types/boxel-ui/icons/success-bordered.gts +30 -0
  426. package/bundled-types/boxel-ui/icons/three-dots-horizontal.gts +21 -0
  427. package/bundled-types/boxel-ui/icons/triangle-left.gts +23 -0
  428. package/bundled-types/boxel-ui/icons/triangle-right.gts +23 -0
  429. package/bundled-types/boxel-ui/icons/types.ts +10 -0
  430. package/bundled-types/boxel-ui/icons/upload.gts +20 -0
  431. package/bundled-types/boxel-ui/icons/warning.gts +19 -0
  432. package/bundled-types/boxel-ui/icons.gts +249 -0
  433. package/bundled-types/boxel-ui/modifiers/auto-focus.ts +7 -0
  434. package/bundled-types/boxel-ui/modifiers/set-css-var.ts +24 -0
  435. package/bundled-types/boxel-ui/modifiers.ts +14 -0
  436. package/bundled-types/boxel-ui/types/@cardstack/boxel-ui/index.d.ts +3 -0
  437. package/bundled-types/boxel-ui/types/ember-css-url/index.d.ts +15 -0
  438. package/bundled-types/boxel-ui/types/ember-draggable-modifiers/index.d.ts +109 -0
  439. package/bundled-types/boxel-ui/types/ember-draggable-modifiers/utils.d.ts +7 -0
  440. package/bundled-types/boxel-ui/types/ember-focus-trap/index.d.ts +20 -0
  441. package/bundled-types/boxel-ui/types/ember-power-calendar/components/index.d.ts +9 -0
  442. package/bundled-types/boxel-ui/types/ember-resize-modifier/index.d.ts +14 -0
  443. package/bundled-types/boxel-ui/types/ember-set-body-class/index.d.ts +12 -0
  444. package/bundled-types/boxel-ui/types/ember-sortable/index.d.ts +52 -0
  445. package/bundled-types/boxel-ui/types/global.d.ts +7 -0
  446. package/bundled-types/boxel-ui/usage.ts +112 -0
  447. package/bundled-types/boxel-ui/utils/date-utils.ts +192 -0
  448. package/bundled-types/boxel-ui/utils/fitted-formats.ts +161 -0
  449. package/bundled-types/host-app/app.ts +22 -0
  450. package/bundled-types/host-app/commands/add-field-to-card-definition.ts +74 -0
  451. package/bundled-types/host-app/commands/ai-assistant.ts +203 -0
  452. package/bundled-types/host-app/commands/apply-markdown-edit.ts +203 -0
  453. package/bundled-types/host-app/commands/apply-search-replace-block.ts +225 -0
  454. package/bundled-types/host-app/commands/ask-ai.ts +66 -0
  455. package/bundled-types/host-app/commands/authed-fetch.ts +53 -0
  456. package/bundled-types/host-app/commands/bot-requests/create-listing-pr-request.ts +77 -0
  457. package/bundled-types/host-app/commands/bot-requests/send-bot-trigger-event.ts +53 -0
  458. package/bundled-types/host-app/commands/can-read-realm.ts +34 -0
  459. package/bundled-types/host-app/commands/cancel-indexing-job.ts +29 -0
  460. package/bundled-types/host-app/commands/check-correctness.ts +309 -0
  461. package/bundled-types/host-app/commands/copy-and-edit.ts +311 -0
  462. package/bundled-types/host-app/commands/copy-card-as-markdown.ts +41 -0
  463. package/bundled-types/host-app/commands/copy-card-to-stack.ts +70 -0
  464. package/bundled-types/host-app/commands/copy-card.ts +67 -0
  465. package/bundled-types/host-app/commands/copy-file-to-realm.ts +82 -0
  466. package/bundled-types/host-app/commands/copy-source.ts +46 -0
  467. package/bundled-types/host-app/commands/create-ai-assistant-room.ts +144 -0
  468. package/bundled-types/host-app/commands/create-and-open-submission-workflow-card.ts +30 -0
  469. package/bundled-types/host-app/commands/create-specs.ts +422 -0
  470. package/bundled-types/host-app/commands/create-submission-workflow.ts +172 -0
  471. package/bundled-types/host-app/commands/evaluate-module.ts +119 -0
  472. package/bundled-types/host-app/commands/execute-atomic-operations.ts +44 -0
  473. package/bundled-types/host-app/commands/fetch-card-json.ts +33 -0
  474. package/bundled-types/host-app/commands/full-reindex-realm.ts +30 -0
  475. package/bundled-types/host-app/commands/generate-example-cards.ts +298 -0
  476. package/bundled-types/host-app/commands/generate-readme-spec.ts +116 -0
  477. package/bundled-types/host-app/commands/generate-theme-example.ts +147 -0
  478. package/bundled-types/host-app/commands/generate-thumbnail.ts +247 -0
  479. package/bundled-types/host-app/commands/get-all-realm-metas.ts +38 -0
  480. package/bundled-types/host-app/commands/get-available-realm-identifiers.ts +29 -0
  481. package/bundled-types/host-app/commands/get-card-type-schema.ts +59 -0
  482. package/bundled-types/host-app/commands/get-card.ts +34 -0
  483. package/bundled-types/host-app/commands/get-catalog-realm-identifiers.ts +29 -0
  484. package/bundled-types/host-app/commands/get-default-writable-realm.ts +28 -0
  485. package/bundled-types/host-app/commands/get-events-from-room.ts +70 -0
  486. package/bundled-types/host-app/commands/get-realm-of-resource-identifier.ts +37 -0
  487. package/bundled-types/host-app/commands/get-user-system-card.ts +38 -0
  488. package/bundled-types/host-app/commands/index.ts +590 -0
  489. package/bundled-types/host-app/commands/instantiate-card.ts +185 -0
  490. package/bundled-types/host-app/commands/invalidate-realm-identifiers.ts +33 -0
  491. package/bundled-types/host-app/commands/invite-user-to-room.ts +34 -0
  492. package/bundled-types/host-app/commands/lint-and-fix.ts +60 -0
  493. package/bundled-types/host-app/commands/listing-action-build.ts +79 -0
  494. package/bundled-types/host-app/commands/listing-action-init.ts +106 -0
  495. package/bundled-types/host-app/commands/listing-create.ts +632 -0
  496. package/bundled-types/host-app/commands/listing-generate-example.ts +83 -0
  497. package/bundled-types/host-app/commands/listing-install.ts +227 -0
  498. package/bundled-types/host-app/commands/listing-remix.ts +150 -0
  499. package/bundled-types/host-app/commands/listing-update-specs.ts +116 -0
  500. package/bundled-types/host-app/commands/listing-use.ts +97 -0
  501. package/bundled-types/host-app/commands/one-shot-llm-request.ts +209 -0
  502. package/bundled-types/host-app/commands/open-ai-assistant-room.ts +38 -0
  503. package/bundled-types/host-app/commands/open-create-listing-modal.ts +52 -0
  504. package/bundled-types/host-app/commands/open-in-interact-mode.ts +36 -0
  505. package/bundled-types/host-app/commands/open-workspace.ts +38 -0
  506. package/bundled-types/host-app/commands/patch-card-instance.ts +108 -0
  507. package/bundled-types/host-app/commands/patch-code.ts +243 -0
  508. package/bundled-types/host-app/commands/patch-fields.ts +304 -0
  509. package/bundled-types/host-app/commands/patch-theme.ts +52 -0
  510. package/bundled-types/host-app/commands/persist-module-inspector-view.ts +41 -0
  511. package/bundled-types/host-app/commands/populate-with-sample-data.ts +84 -0
  512. package/bundled-types/host-app/commands/preview-format.ts +52 -0
  513. package/bundled-types/host-app/commands/read-binary-file.ts +62 -0
  514. package/bundled-types/host-app/commands/read-card-for-ai-assistant.ts +50 -0
  515. package/bundled-types/host-app/commands/read-file-for-ai-assistant.ts +46 -0
  516. package/bundled-types/host-app/commands/read-source.ts +46 -0
  517. package/bundled-types/host-app/commands/read-text-file.ts +44 -0
  518. package/bundled-types/host-app/commands/register-bot.ts +42 -0
  519. package/bundled-types/host-app/commands/reindex-realm.ts +30 -0
  520. package/bundled-types/host-app/commands/retry-submission-workflow.ts +119 -0
  521. package/bundled-types/host-app/commands/sanitize-module-list.ts +83 -0
  522. package/bundled-types/host-app/commands/save-card.ts +44 -0
  523. package/bundled-types/host-app/commands/screenshot-card.ts +148 -0
  524. package/bundled-types/host-app/commands/search-and-choose.ts +180 -0
  525. package/bundled-types/host-app/commands/search-cards.ts +102 -0
  526. package/bundled-types/host-app/commands/search-google-images.ts +100 -0
  527. package/bundled-types/host-app/commands/send-ai-assistant-message.ts +122 -0
  528. package/bundled-types/host-app/commands/send-request-via-proxy.ts +70 -0
  529. package/bundled-types/host-app/commands/serialize-card.ts +45 -0
  530. package/bundled-types/host-app/commands/set-active-llm.ts +39 -0
  531. package/bundled-types/host-app/commands/set-user-system-card.ts +29 -0
  532. package/bundled-types/host-app/commands/show-card.ts +102 -0
  533. package/bundled-types/host-app/commands/show-file.ts +39 -0
  534. package/bundled-types/host-app/commands/store-add.ts +37 -0
  535. package/bundled-types/host-app/commands/summarize-session.ts +82 -0
  536. package/bundled-types/host-app/commands/switch-submode.ts +112 -0
  537. package/bundled-types/host-app/commands/sync-openrouter-models.ts +373 -0
  538. package/bundled-types/host-app/commands/transform-cards.ts +59 -0
  539. package/bundled-types/host-app/commands/unregister-bot.ts +29 -0
  540. package/bundled-types/host-app/commands/update-code-path-with-selection.ts +45 -0
  541. package/bundled-types/host-app/commands/update-playground-selection.ts +37 -0
  542. package/bundled-types/host-app/commands/update-room-skills.ts +231 -0
  543. package/bundled-types/host-app/commands/utils.ts +178 -0
  544. package/bundled-types/host-app/commands/validate-realm.ts +40 -0
  545. package/bundled-types/host-app/commands/write-binary-file.ts +142 -0
  546. package/bundled-types/host-app/commands/write-text-file.ts +104 -0
  547. package/bundled-types/host-app/components/.gitkeep +0 -0
  548. package/bundled-types/host-app/components/ai-assistant/action-bar.gts +188 -0
  549. package/bundled-types/host-app/components/ai-assistant/ai-assist-button-active-bg.webp +0 -0
  550. package/bundled-types/host-app/components/ai-assistant/ai-assist-button-active-bg@2x.webp +0 -0
  551. package/bundled-types/host-app/components/ai-assistant/ai-assist-button-active-bg@3x.webp +0 -0
  552. package/bundled-types/host-app/components/ai-assistant/ai-assist-icon-animated.webp +0 -0
  553. package/bundled-types/host-app/components/ai-assistant/ai-assist-icon-bw.png +0 -0
  554. package/bundled-types/host-app/components/ai-assistant/ai-assist-icon-bw@2x.png +0 -0
  555. package/bundled-types/host-app/components/ai-assistant/ai-assist-icon-bw@3x.png +0 -0
  556. package/bundled-types/host-app/components/ai-assistant/ai-assist-icon.webp +0 -0
  557. package/bundled-types/host-app/components/ai-assistant/ai-assist-icon@2x.webp +0 -0
  558. package/bundled-types/host-app/components/ai-assistant/ai-assist-icon@3x.webp +0 -0
  559. package/bundled-types/host-app/components/ai-assistant/apply-button/index.gts +226 -0
  560. package/bundled-types/host-app/components/ai-assistant/apply-button/usage.gts +81 -0
  561. package/bundled-types/host-app/components/ai-assistant/attached-file-dropdown-menu.gts +179 -0
  562. package/bundled-types/host-app/components/ai-assistant/attachment-picker/attach-button.gts +176 -0
  563. package/bundled-types/host-app/components/ai-assistant/attachment-picker/attached-items.gts +271 -0
  564. package/bundled-types/host-app/components/ai-assistant/attachment-picker/index.gts +144 -0
  565. package/bundled-types/host-app/components/ai-assistant/attachment-picker/usage.gts +127 -0
  566. package/bundled-types/host-app/components/ai-assistant/button.gts +60 -0
  567. package/bundled-types/host-app/components/ai-assistant/chat-input/index.gts +203 -0
  568. package/bundled-types/host-app/components/ai-assistant/chat-input/usage.gts +68 -0
  569. package/bundled-types/host-app/components/ai-assistant/code-block/actions.gts +58 -0
  570. package/bundled-types/host-app/components/ai-assistant/code-block/apply-code-patch-button.gts +66 -0
  571. package/bundled-types/host-app/components/ai-assistant/code-block/command-header.gts +115 -0
  572. package/bundled-types/host-app/components/ai-assistant/code-block/diff-editor-header.gts +209 -0
  573. package/bundled-types/host-app/components/ai-assistant/code-block/index.gts +228 -0
  574. package/bundled-types/host-app/components/ai-assistant/code-block/patch-footer.gts +32 -0
  575. package/bundled-types/host-app/components/ai-assistant/code-block/view-code-button.gts +73 -0
  576. package/bundled-types/host-app/components/ai-assistant/focus-pill/index.gts +60 -0
  577. package/bundled-types/host-app/components/ai-assistant/focus-pill/usage.gts +45 -0
  578. package/bundled-types/host-app/components/ai-assistant/llm-mode-toggle.gts +113 -0
  579. package/bundled-types/host-app/components/ai-assistant/llm-select.gts +158 -0
  580. package/bundled-types/host-app/components/ai-assistant/message/aibot-message.gts +374 -0
  581. package/bundled-types/host-app/components/ai-assistant/message/attachments.gts +153 -0
  582. package/bundled-types/host-app/components/ai-assistant/message/index.gts +678 -0
  583. package/bundled-types/host-app/components/ai-assistant/message/meta.gts +66 -0
  584. package/bundled-types/host-app/components/ai-assistant/message/text-content.gts +32 -0
  585. package/bundled-types/host-app/components/ai-assistant/message/usage.gts +182 -0
  586. package/bundled-types/host-app/components/ai-assistant/message/user-message.gts +52 -0
  587. package/bundled-types/host-app/components/ai-assistant/new-session-button.gts +126 -0
  588. package/bundled-types/host-app/components/ai-assistant/new-session-settings.gts +180 -0
  589. package/bundled-types/host-app/components/ai-assistant/new-session.gts +83 -0
  590. package/bundled-types/host-app/components/ai-assistant/panel-popover.gts +102 -0
  591. package/bundled-types/host-app/components/ai-assistant/panel.gts +453 -0
  592. package/bundled-types/host-app/components/ai-assistant/past-session-item.gts +323 -0
  593. package/bundled-types/host-app/components/ai-assistant/past-sessions.gts +115 -0
  594. package/bundled-types/host-app/components/ai-assistant/rename-session.gts +144 -0
  595. package/bundled-types/host-app/components/ai-assistant/restore-file-modal.gts +105 -0
  596. package/bundled-types/host-app/components/ai-assistant/skill-menu/index.gts +190 -0
  597. package/bundled-types/host-app/components/ai-assistant/skill-menu/skill-toggle.gts +168 -0
  598. package/bundled-types/host-app/components/ai-assistant/skill-menu/usage.gts +48 -0
  599. package/bundled-types/host-app/components/ai-assistant/toast.gts +293 -0
  600. package/bundled-types/host-app/components/card-catalog/modal.gts +579 -0
  601. package/bundled-types/host-app/components/card-error.gts +41 -0
  602. package/bundled-types/host-app/components/card-instance-picker/index.gts +54 -0
  603. package/bundled-types/host-app/components/card-pill.gts +189 -0
  604. package/bundled-types/host-app/components/card-prerender.gts +880 -0
  605. package/bundled-types/host-app/components/card-renderer.gts +84 -0
  606. package/bundled-types/host-app/components/card-search/constants.ts +76 -0
  607. package/bundled-types/host-app/components/card-search/item-button.gts +243 -0
  608. package/bundled-types/host-app/components/card-search/panel.gts +150 -0
  609. package/bundled-types/host-app/components/card-search/search-bar.gts +218 -0
  610. package/bundled-types/host-app/components/card-search/search-content.gts +604 -0
  611. package/bundled-types/host-app/components/card-search/search-result-header.gts +165 -0
  612. package/bundled-types/host-app/components/card-search/search-result-section.gts +545 -0
  613. package/bundled-types/host-app/components/card-search/section-header.gts +117 -0
  614. package/bundled-types/host-app/components/editor/directory.gts +308 -0
  615. package/bundled-types/host-app/components/editor/file-tree.gts +69 -0
  616. package/bundled-types/host-app/components/editor/import-module.gts +27 -0
  617. package/bundled-types/host-app/components/editor/indexed-file-tree.gts +572 -0
  618. package/bundled-types/host-app/components/editor/recent-files.gts +141 -0
  619. package/bundled-types/host-app/components/file-pill.gts +206 -0
  620. package/bundled-types/host-app/components/head-format-preview.gts +781 -0
  621. package/bundled-types/host-app/components/host-mode/breadcrumb-item.gts +150 -0
  622. package/bundled-types/host-app/components/host-mode/breadcrumbs.gts +131 -0
  623. package/bundled-types/host-app/components/host-mode/card.gts +199 -0
  624. package/bundled-types/host-app/components/host-mode/content.gts +195 -0
  625. package/bundled-types/host-app/components/host-mode/stack-item.gts +310 -0
  626. package/bundled-types/host-app/components/host-mode/stack.gts +91 -0
  627. package/bundled-types/host-app/components/matrix/auth-container.gts +66 -0
  628. package/bundled-types/host-app/components/matrix/auth.gts +73 -0
  629. package/bundled-types/host-app/components/matrix/forgot-password.gts +487 -0
  630. package/bundled-types/host-app/components/matrix/login.gts +241 -0
  631. package/bundled-types/host-app/components/matrix/register-user.gts +843 -0
  632. package/bundled-types/host-app/components/matrix/room-message-command.gts +330 -0
  633. package/bundled-types/host-app/components/matrix/room-message.gts +261 -0
  634. package/bundled-types/host-app/components/matrix/room.gts +1872 -0
  635. package/bundled-types/host-app/components/matrix/user-profile.gts +99 -0
  636. package/bundled-types/host-app/components/modal-container.gts +171 -0
  637. package/bundled-types/host-app/components/operator-mode/binary-file-info.gts +77 -0
  638. package/bundled-types/host-app/components/operator-mode/card-adoption-chain.gts +99 -0
  639. package/bundled-types/host-app/components/operator-mode/card-error-detail.gts +87 -0
  640. package/bundled-types/host-app/components/operator-mode/card-error.gts +177 -0
  641. package/bundled-types/host-app/components/operator-mode/card-schema-editor.gts +616 -0
  642. package/bundled-types/host-app/components/operator-mode/card-url-bar.gts +197 -0
  643. package/bundled-types/host-app/components/operator-mode/choose-file-modal.gts +562 -0
  644. package/bundled-types/host-app/components/operator-mode/choose-subscription-plan-modal.gts +517 -0
  645. package/bundled-types/host-app/components/operator-mode/code-editor.gts +736 -0
  646. package/bundled-types/host-app/components/operator-mode/code-submode/editor-indicator.gts +128 -0
  647. package/bundled-types/host-app/components/operator-mode/code-submode/format-chooser.gts +589 -0
  648. package/bundled-types/host-app/components/operator-mode/code-submode/inner-container.gts +162 -0
  649. package/bundled-types/host-app/components/operator-mode/code-submode/left-panel-toggle.gts +267 -0
  650. package/bundled-types/host-app/components/operator-mode/code-submode/module-inspector.gts +836 -0
  651. package/bundled-types/host-app/components/operator-mode/code-submode/playground/field-chooser-modal.gts +97 -0
  652. package/bundled-types/host-app/components/operator-mode/code-submode/playground/instance-chooser-dropdown.gts +346 -0
  653. package/bundled-types/host-app/components/operator-mode/code-submode/playground/playground-background.png +0 -0
  654. package/bundled-types/host-app/components/operator-mode/code-submode/playground/playground-panel.gts +1274 -0
  655. package/bundled-types/host-app/components/operator-mode/code-submode/playground/playground-preview.gts +155 -0
  656. package/bundled-types/host-app/components/operator-mode/code-submode/playground/playground.gts +98 -0
  657. package/bundled-types/host-app/components/operator-mode/code-submode/playground/spec-search.gts +70 -0
  658. package/bundled-types/host-app/components/operator-mode/code-submode/schema-editor.gts +205 -0
  659. package/bundled-types/host-app/components/operator-mode/code-submode/spec-preview-badge.gts +72 -0
  660. package/bundled-types/host-app/components/operator-mode/code-submode/spec-preview.gts +432 -0
  661. package/bundled-types/host-app/components/operator-mode/code-submode/toggle-button.gts +72 -0
  662. package/bundled-types/host-app/components/operator-mode/code-submode.gts +1030 -0
  663. package/bundled-types/host-app/components/operator-mode/container.gts +270 -0
  664. package/bundled-types/host-app/components/operator-mode/context-menu-button.gts +95 -0
  665. package/bundled-types/host-app/components/operator-mode/copy-button.gts +277 -0
  666. package/bundled-types/host-app/components/operator-mode/create-file-modal.gts +1087 -0
  667. package/bundled-types/host-app/components/operator-mode/create-listing-modal.gts +513 -0
  668. package/bundled-types/host-app/components/operator-mode/definition-container/base.gts +222 -0
  669. package/bundled-types/host-app/components/operator-mode/definition-container/clickable.gts +59 -0
  670. package/bundled-types/host-app/components/operator-mode/definition-container/divider.gts +48 -0
  671. package/bundled-types/host-app/components/operator-mode/definition-container/index.gts +108 -0
  672. package/bundled-types/host-app/components/operator-mode/delete-modal.gts +123 -0
  673. package/bundled-types/host-app/components/operator-mode/detail-panel-selector.gts +320 -0
  674. package/bundled-types/host-app/components/operator-mode/detail-panel.gts +834 -0
  675. package/bundled-types/host-app/components/operator-mode/edit-field-modal.gts +450 -0
  676. package/bundled-types/host-app/components/operator-mode/error-display.gts +590 -0
  677. package/bundled-types/host-app/components/operator-mode/host-submode/open-site-popover.gts +127 -0
  678. package/bundled-types/host-app/components/operator-mode/host-submode/publishing-realm-popover.gts +159 -0
  679. package/bundled-types/host-app/components/operator-mode/host-submode.gts +373 -0
  680. package/bundled-types/host-app/components/operator-mode/interact-submode/neighbor-stack-trigger.gts +116 -0
  681. package/bundled-types/host-app/components/operator-mode/interact-submode.gts +1021 -0
  682. package/bundled-types/host-app/components/operator-mode/new-file-button.gts +187 -0
  683. package/bundled-types/host-app/components/operator-mode/operator-mode-overlays.gts +511 -0
  684. package/bundled-types/host-app/components/operator-mode/overlays.gts +342 -0
  685. package/bundled-types/host-app/components/operator-mode/preview-panel/fitted-format-gallery.gts +127 -0
  686. package/bundled-types/host-app/components/operator-mode/preview-panel/index.gts +443 -0
  687. package/bundled-types/host-app/components/operator-mode/preview-panel/markdown-preview.gts +175 -0
  688. package/bundled-types/host-app/components/operator-mode/preview-panel/metadata-panel.gts +214 -0
  689. package/bundled-types/host-app/components/operator-mode/preview-panel/rendered-markdown.gts +718 -0
  690. package/bundled-types/host-app/components/operator-mode/private-dependency-violation.gts +70 -0
  691. package/bundled-types/host-app/components/operator-mode/profile/profile-email.gts +686 -0
  692. package/bundled-types/host-app/components/operator-mode/profile/profile-settings-modal.gts +454 -0
  693. package/bundled-types/host-app/components/operator-mode/profile/profile-subscription.gts +170 -0
  694. package/bundled-types/host-app/components/operator-mode/profile-info-popover.gts +263 -0
  695. package/bundled-types/host-app/components/operator-mode/publish-realm-modal.gts +1529 -0
  696. package/bundled-types/host-app/components/operator-mode/remove-field-modal.gts +135 -0
  697. package/bundled-types/host-app/components/operator-mode/send-error-to-ai-assistant.gts +195 -0
  698. package/bundled-types/host-app/components/operator-mode/stack-item.gts +1341 -0
  699. package/bundled-types/host-app/components/operator-mode/stack.gts +191 -0
  700. package/bundled-types/host-app/components/operator-mode/submode-layout.gts +781 -0
  701. package/bundled-types/host-app/components/operator-mode/syntax-error-display.gts +72 -0
  702. package/bundled-types/host-app/components/operator-mode/workspace-chooser/add-workspace.gts +282 -0
  703. package/bundled-types/host-app/components/operator-mode/workspace-chooser/index.gts +299 -0
  704. package/bundled-types/host-app/components/operator-mode/workspace-chooser/item-container.gts +50 -0
  705. package/bundled-types/host-app/components/operator-mode/workspace-chooser/workspace-loading-indicator.gts +50 -0
  706. package/bundled-types/host-app/components/operator-mode/workspace-chooser/workspace.gts +1079 -0
  707. package/bundled-types/host-app/components/pill-menu/index.gts +281 -0
  708. package/bundled-types/host-app/components/pill-menu/usage.gts +64 -0
  709. package/bundled-types/host-app/components/prerendered-card-search.gts +274 -0
  710. package/bundled-types/host-app/components/realm-dropdown.gts +209 -0
  711. package/bundled-types/host-app/components/realm-picker/index.gts +142 -0
  712. package/bundled-types/host-app/components/search-sheet/index.gts +426 -0
  713. package/bundled-types/host-app/components/search-sheet/usage.gts +99 -0
  714. package/bundled-types/host-app/components/submode-switcher.gts +260 -0
  715. package/bundled-types/host-app/components/type-picker/index.gts +111 -0
  716. package/bundled-types/host-app/components/with-known-realms-loaded.gts +47 -0
  717. package/bundled-types/host-app/components/with-loaded-realm.gts +39 -0
  718. package/bundled-types/host-app/components/with-subscription-data.gts +247 -0
  719. package/bundled-types/host-app/config/environment.ts +66 -0
  720. package/bundled-types/host-app/deprecation-workflow.js +30 -0
  721. package/bundled-types/host-app/lib/browser-queue.ts +198 -0
  722. package/bundled-types/host-app/lib/codemirror-context.ts +1081 -0
  723. package/bundled-types/host-app/lib/command-definitions.ts +41 -0
  724. package/bundled-types/host-app/lib/download-realm.ts +9 -0
  725. package/bundled-types/host-app/lib/example-card-helpers.ts +99 -0
  726. package/bundled-types/host-app/lib/externals.ts +247 -0
  727. package/bundled-types/host-app/lib/field-path-parser.ts +520 -0
  728. package/bundled-types/host-app/lib/file-def-manager.ts +712 -0
  729. package/bundled-types/host-app/lib/file-upload-state.ts +6 -0
  730. package/bundled-types/host-app/lib/formatted-message/utils.ts +272 -0
  731. package/bundled-types/host-app/lib/gc-card-store.ts +1084 -0
  732. package/bundled-types/host-app/lib/host-base-command.ts +29 -0
  733. package/bundled-types/host-app/lib/html-component.ts +144 -0
  734. package/bundled-types/host-app/lib/html-to-markdown.ts +96 -0
  735. package/bundled-types/host-app/lib/isolated-render.gts +90 -0
  736. package/bundled-types/host-app/lib/known-file-meta-urls.ts +17 -0
  737. package/bundled-types/host-app/lib/limited-set.ts +68 -0
  738. package/bundled-types/host-app/lib/matrix-classes/member.ts +28 -0
  739. package/bundled-types/host-app/lib/matrix-classes/message-builder.ts +444 -0
  740. package/bundled-types/host-app/lib/matrix-classes/message-code-patch-result.ts +44 -0
  741. package/bundled-types/host-app/lib/matrix-classes/message-command.ts +109 -0
  742. package/bundled-types/host-app/lib/matrix-classes/message.ts +239 -0
  743. package/bundled-types/host-app/lib/matrix-classes/room.ts +174 -0
  744. package/bundled-types/host-app/lib/matrix-utils.ts +114 -0
  745. package/bundled-types/host-app/lib/mutex.ts +62 -0
  746. package/bundled-types/host-app/lib/prerender-fetch-headers.ts +46 -0
  747. package/bundled-types/host-app/lib/prerender-util.ts +31 -0
  748. package/bundled-types/host-app/lib/public-path.ts +1 -0
  749. package/bundled-types/host-app/lib/random-name.ts +26 -0
  750. package/bundled-types/host-app/lib/realm-utils.ts +45 -0
  751. package/bundled-types/host-app/lib/search-cache-key.ts +33 -0
  752. package/bundled-types/host-app/lib/search-in-flight-key.ts +28 -0
  753. package/bundled-types/host-app/lib/search-replace-block-parsing.ts +101 -0
  754. package/bundled-types/host-app/lib/setup-globals.ts +15 -0
  755. package/bundled-types/host-app/lib/sqlite-adapter.ts +510 -0
  756. package/bundled-types/host-app/lib/stack-item.ts +164 -0
  757. package/bundled-types/host-app/lib/utils.ts +84 -0
  758. package/bundled-types/host-app/lib/window-error-handler.ts +132 -0
  759. package/bundled-types/host-app/router.ts +31 -0
  760. package/bundled-types/host-app/services/ai-assistant-panel-service.ts +850 -0
  761. package/bundled-types/host-app/services/billing-service.ts +208 -0
  762. package/bundled-types/host-app/services/card-service.ts +373 -0
  763. package/bundled-types/host-app/services/card-type-service.ts +226 -0
  764. package/bundled-types/host-app/services/code-semantics-service.ts +297 -0
  765. package/bundled-types/host-app/services/command-service.ts +975 -0
  766. package/bundled-types/host-app/services/environment-service.ts +26 -0
  767. package/bundled-types/host-app/services/error-display.ts +42 -0
  768. package/bundled-types/host-app/services/file-upload.ts +212 -0
  769. package/bundled-types/host-app/services/host-mode-service.ts +381 -0
  770. package/bundled-types/host-app/services/host-mode-state-service.ts +160 -0
  771. package/bundled-types/host-app/services/loader-service.ts +148 -0
  772. package/bundled-types/host-app/services/local-indexer.ts +68 -0
  773. package/bundled-types/host-app/services/local-persistence-service.ts +296 -0
  774. package/bundled-types/host-app/services/logger-service.ts +13 -0
  775. package/bundled-types/host-app/services/matrix-sdk-loader.ts +346 -0
  776. package/bundled-types/host-app/services/matrix-service.ts +2287 -0
  777. package/bundled-types/host-app/services/message-service.ts +84 -0
  778. package/bundled-types/host-app/services/module-contents-service.ts +333 -0
  779. package/bundled-types/host-app/services/monaco-service.ts +333 -0
  780. package/bundled-types/host-app/services/network.ts +101 -0
  781. package/bundled-types/host-app/services/operator-mode-state-service.ts +1591 -0
  782. package/bundled-types/host-app/services/playground-panel-service.ts +236 -0
  783. package/bundled-types/host-app/services/queue.ts +30 -0
  784. package/bundled-types/host-app/services/realm-info-service.ts +37 -0
  785. package/bundled-types/host-app/services/realm-server.ts +1209 -0
  786. package/bundled-types/host-app/services/realm.ts +1413 -0
  787. package/bundled-types/host-app/services/recent-cards-service.ts +174 -0
  788. package/bundled-types/host-app/services/recent-files-service.ts +224 -0
  789. package/bundled-types/host-app/services/render-error-state.ts +55 -0
  790. package/bundled-types/host-app/services/render-service.ts +362 -0
  791. package/bundled-types/host-app/services/render-store.ts +27 -0
  792. package/bundled-types/host-app/services/reset.ts +19 -0
  793. package/bundled-types/host-app/services/scroll-position-service.ts +81 -0
  794. package/bundled-types/host-app/services/spec-panel-service.ts +107 -0
  795. package/bundled-types/host-app/services/store.ts +2540 -0
  796. package/bundled-types/host-app/utils/assert-never.ts +3 -0
  797. package/bundled-types/host-app/utils/auth-error-guard.ts +183 -0
  798. package/bundled-types/host-app/utils/auth-service-worker-registration.ts +123 -0
  799. package/bundled-types/host-app/utils/card-search/query-builder.ts +169 -0
  800. package/bundled-types/host-app/utils/card-search/section-pagination.ts +57 -0
  801. package/bundled-types/host-app/utils/card-search/sections.ts +240 -0
  802. package/bundled-types/host-app/utils/card-search/type-filter.ts +124 -0
  803. package/bundled-types/host-app/utils/card-search/types.ts +11 -0
  804. package/bundled-types/host-app/utils/card-search/url.ts +23 -0
  805. package/bundled-types/host-app/utils/clipboard.ts +23 -0
  806. package/bundled-types/host-app/utils/editor/boxel-formatter.ts +65 -0
  807. package/bundled-types/host-app/utils/editor/editor-language.ts +88 -0
  808. package/bundled-types/host-app/utils/editor/monaco-test-waiter.ts +302 -0
  809. package/bundled-types/host-app/utils/file-def-attributes-extractor.ts +438 -0
  810. package/bundled-types/host-app/utils/file-name.ts +38 -0
  811. package/bundled-types/host-app/utils/id-from-card-or-url.ts +11 -0
  812. package/bundled-types/host-app/utils/lint-formatting.ts +57 -0
  813. package/bundled-types/host-app/utils/local-storage-keys.ts +28 -0
  814. package/bundled-types/host-app/utils/normalized-dir-path.ts +3 -0
  815. package/bundled-types/host-app/utils/prettify-messages.ts +21 -0
  816. package/bundled-types/host-app/utils/prettify-prompts.ts +21 -0
  817. package/bundled-types/host-app/utils/realm-index-boilerplate.ts +27 -0
  818. package/bundled-types/host-app/utils/register-boxel-transition.ts +17 -0
  819. package/bundled-types/host-app/utils/render-desync-detector.ts +362 -0
  820. package/bundled-types/host-app/utils/render-error.ts +455 -0
  821. package/bundled-types/host-app/utils/render-timer-stub.ts +249 -0
  822. package/bundled-types/host-app/utils/run-while-active.ts +14 -0
  823. package/bundled-types/host-app/utils/schema-editor.ts +25 -0
  824. package/bundled-types/host-app/utils/text-suggestion.ts +82 -0
  825. package/bundled-types/host-app/utils/titleize.ts +10 -0
  826. package/bundled-types/host-app/utils/uuid.ts +13 -0
  827. package/bundled-types/host-tests/helpers/adapter.ts +464 -0
  828. package/bundled-types/host-tests/helpers/base-realm.ts +464 -0
  829. package/bundled-types/host-tests/helpers/cards/view-card-demo.ts +119 -0
  830. package/bundled-types/host-tests/helpers/field-test-helpers.gts +54 -0
  831. package/bundled-types/host-tests/helpers/image-fixture.ts +3 -0
  832. package/bundled-types/host-tests/helpers/index.gts +2099 -0
  833. package/bundled-types/host-tests/helpers/indexer.ts +293 -0
  834. package/bundled-types/host-tests/helpers/interact-submode-setup.gts +551 -0
  835. package/bundled-types/host-tests/helpers/mock-matrix/_client.ts +1042 -0
  836. package/bundled-types/host-tests/helpers/mock-matrix/_sdk.ts +71 -0
  837. package/bundled-types/host-tests/helpers/mock-matrix/_server-state.ts +343 -0
  838. package/bundled-types/host-tests/helpers/mock-matrix/_sliding-sync.ts +140 -0
  839. package/bundled-types/host-tests/helpers/mock-matrix/_utils.ts +146 -0
  840. package/bundled-types/host-tests/helpers/mock-matrix.ts +200 -0
  841. package/bundled-types/host-tests/helpers/operator-mode-parameters-match.ts +71 -0
  842. package/bundled-types/host-tests/helpers/operator-mode-state.ts +18 -0
  843. package/bundled-types/host-tests/helpers/percy-snapshot.ts +228 -0
  844. package/bundled-types/host-tests/helpers/playground.ts +130 -0
  845. package/bundled-types/host-tests/helpers/realm-server-mock/index.ts +100 -0
  846. package/bundled-types/host-tests/helpers/realm-server-mock/routes.ts +617 -0
  847. package/bundled-types/host-tests/helpers/realm-server-mock/types.ts +24 -0
  848. package/bundled-types/host-tests/helpers/recent-files-cards.ts +86 -0
  849. package/bundled-types/host-tests/helpers/render-component.ts +40 -0
  850. package/bundled-types/host-tests/helpers/setup-qunit.js +164 -0
  851. package/bundled-types/host-tests/helpers/setup.ts +349 -0
  852. package/bundled-types/host-tests/helpers/shard-warmup.ts +72 -0
  853. package/bundled-types/host-tests/helpers/stream.ts +60 -0
  854. package/bundled-types/host-tests/helpers/test-auth.ts +27 -0
  855. package/bundled-types/host-tests/helpers/test-realm-registry.ts +23 -0
  856. package/bundled-types/host-tests/helpers/test-realm-service-worker.ts +92 -0
  857. package/bundled-types/host-tests/helpers/uncaught-exceptions.ts +16 -0
  858. package/bundled-types/host-tests/helpers/visit-operator-mode.ts +39 -0
  859. package/bundled-types/host-types/@cardstack/host/index.d.ts +3 -0
  860. package/bundled-types/host-types/@joplin/turndown-plugin-gfm/index.d.ts +15 -0
  861. package/bundled-types/host-types/@sqlite.org/sqlite-wasm/index.d.ts +122 -0
  862. package/bundled-types/host-types/ember-click-outside/modifiers/on-click-outside.ts +16 -0
  863. package/bundled-types/host-types/ember-concurrency/helpers/perform.d.ts +3 -0
  864. package/bundled-types/host-types/ember-css-url/index.d.ts +15 -0
  865. package/bundled-types/host-types/ember-data/types/registries/model.d.ts +6 -0
  866. package/bundled-types/host-types/ember-elsewhere/from-elsewhere.d.ts +12 -0
  867. package/bundled-types/host-types/ember-elsewhere/to-elsewhere.d.ts +16 -0
  868. package/bundled-types/host-types/ember-focus-trap/index.d.ts +4 -0
  869. package/bundled-types/host-types/ember-keyboard/modifiers/on-key.d.ts +15 -0
  870. package/bundled-types/host-types/index.d.ts +6 -0
  871. package/bundled-types/local-types/eslint-js.d.ts +5 -0
  872. package/bundled-types/local-types/index.d.ts +115 -0
  873. package/bundled-types/local-types/marked-gfm-heading-id.d.ts +12 -0
  874. package/bundled-types/local-types/matrix-js-sdk/index.d.ts +11 -0
  875. package/bundled-types/local-types/package.json +13 -0
  876. package/bundled-types/shims/boxel-cli-shims.d.ts +10 -0
  877. package/dist/index.js +100 -100
  878. package/package.json +13 -7
  879. package/src/commands/parse.ts +277 -34
@@ -0,0 +1,2099 @@
1
+ import type Owner from '@ember/owner';
2
+ import Service from '@ember/service';
3
+ import {
4
+ type TestContext,
5
+ getContext,
6
+ visit,
7
+ settled,
8
+ } from '@ember/test-helpers';
9
+ import { click, findAll, waitUntil, waitFor } from '@ember/test-helpers';
10
+ import GlimmerComponent from '@glimmer/component';
11
+ import { tracked } from '@glimmer/tracking';
12
+
13
+ import { getService } from '@universal-ember/test-support';
14
+
15
+ import QUnit from 'qunit';
16
+ import { validate as uuidValidate } from 'uuid';
17
+
18
+ import {
19
+ baseRealm,
20
+ CachingDefinitionLookup,
21
+ cardDefComputedFields,
22
+ ensureTrailingSlash,
23
+ getCreatedTime,
24
+ IndexWriter,
25
+ insertPermissions,
26
+ Loader,
27
+ MatrixClient,
28
+ Realm,
29
+ simpleHash,
30
+ testHostModeRealmURL,
31
+ testRealmInfo,
32
+ testRealmURL,
33
+ testRRI,
34
+ Worker,
35
+ DEFAULT_CARD_SIZE_LIMIT_BYTES,
36
+ DEFAULT_FILE_SIZE_LIMIT_BYTES,
37
+ type DefinitionLookup,
38
+ type LooseSingleCardDocument,
39
+ type Prerenderer,
40
+ type RealmAction,
41
+ type RealmAdapter,
42
+ ri,
43
+ rri,
44
+ type RealmInfo,
45
+ type RealmPermissions,
46
+ type RenderError,
47
+ type RealmIdentifier,
48
+ type RealmResourceIdentifier,
49
+ } from '@cardstack/runtime-common';
50
+
51
+ import UpdateRoomSkillsCommand from '@cardstack/host/commands/update-room-skills';
52
+ import CardPrerender from '@cardstack/host/components/card-prerender';
53
+ import ENV from '@cardstack/host/config/environment';
54
+ import {
55
+ render as renderIntoElement,
56
+ teardown as teardownIsolatedRender,
57
+ } from '@cardstack/host/lib/isolated-render';
58
+ import SQLiteAdapter from '@cardstack/host/lib/sqlite-adapter';
59
+ import type QueueService from '@cardstack/host/services/queue';
60
+ import type { CardSaveSubscriber } from '@cardstack/host/services/store';
61
+
62
+ import {
63
+ coerceRenderError,
64
+ normalizeRenderError,
65
+ } from '@cardstack/host/utils/render-error';
66
+
67
+ import type {
68
+ CardStore,
69
+ CardDef,
70
+ FieldDef,
71
+ } from 'https://cardstack.com/base/card-api';
72
+
73
+ import { TestRealmAdapter } from './adapter';
74
+ import { testRealmServerMatrixUsername, setupMockMatrix } from './mock-matrix';
75
+ import percySnapshot from './percy-snapshot';
76
+ import { setupAuthEndpoints } from './realm-server-mock';
77
+ import { setupRenderingTest } from './setup';
78
+ import { createJWT, testRealmSecretSeed } from './test-auth';
79
+ import { getTestRealmRegistry } from './test-realm-registry';
80
+ import visitOperatorMode from './visit-operator-mode';
81
+
82
+ import type { MockUtils } from './mock-matrix/_utils';
83
+
84
+ import type { SimpleElement } from '@simple-dom/interface';
85
+
86
+ export {
87
+ visitOperatorMode,
88
+ testHostModeRealmURL,
89
+ testRealmURL,
90
+ testRealmInfo,
91
+ testRRI,
92
+ percySnapshot,
93
+ };
94
+ export { createJWT, testRealmSecretSeed } from './test-auth';
95
+ export {
96
+ registerRealmAuthSessionRoomEnsurer,
97
+ resetCatalogRealmURL,
98
+ setupAuthEndpoints,
99
+ setCatalogRealmURL,
100
+ } from './realm-server-mock';
101
+ export { setupOperatorModeStateCleanup } from './operator-mode-state';
102
+ export * from '@cardstack/runtime-common/helpers';
103
+ export * from './indexer';
104
+
105
+ export const testModuleRealm = ri('https://localhost:4202/test/');
106
+
107
+ /**
108
+ * Build a `RealmResourceIdentifier` for a module in `testModuleRealm`.
109
+ * Shorter than `` rri(`${testModuleRealm}${path}`) ``.
110
+ */
111
+ export function testModuleRRI(path: string): RealmResourceIdentifier {
112
+ return rri(`${testModuleRealm}${path}`);
113
+ }
114
+
115
+ export {
116
+ catalogRealm,
117
+ skillsRealm,
118
+ skillCardURL,
119
+ devSkillId,
120
+ envSkillId,
121
+ } from '@cardstack/host/lib/utils';
122
+
123
+ const { sqlSchema } = ENV;
124
+
125
+ export const cardDefFieldCount = cardDefComputedFields?.length + 1; // standard computeds + `cardInfo`
126
+
127
+ type CardAPI = typeof import('https://cardstack.com/base/card-api');
128
+ type ModuleHooks = {
129
+ after: (callback: () => void | Promise<void>) => void;
130
+ };
131
+
132
+ const baseTestMatrix = {
133
+ url: new URL(`http://localhost:8008`),
134
+ username: 'test_realm',
135
+ password: 'password',
136
+ };
137
+
138
+ export { provide as provideConsumeContext } from 'ember-provide-consume-context/test-support';
139
+
140
+ export function cleanWhiteSpace(text: string) {
141
+ // this also normalizes non-breaking space characters which seem
142
+ // to be appearing in date/time serialization in some envs
143
+
144
+ return text
145
+ .replace(/<!---->/g, '')
146
+ .replace(/[\s]+/g, ' ')
147
+ .trim();
148
+ }
149
+
150
+ export function getMonacoContent(
151
+ editor: 'main' | 'firstAvailable' = 'main',
152
+ ): string {
153
+ if (editor === 'main') {
154
+ let monacoService = getService('monaco-service');
155
+ return monacoService.getMonacoContent()!;
156
+ } else {
157
+ return (window as any).monaco.editor.getModels()[0].getValue();
158
+ }
159
+ }
160
+
161
+ export function setMonacoContent(content: string): string {
162
+ return (window as any).monaco.editor.getModels()[0].setValue(content);
163
+ }
164
+
165
+ export function cleanupMonacoEditorModels() {
166
+ // If there's no monaco, nothing to clean up
167
+ if (!(window as any).monaco) return;
168
+ let diffEditors = (window as any).monaco.editor.getDiffEditors();
169
+ for (let editor of diffEditors) {
170
+ editor.dispose();
171
+ }
172
+
173
+ let models = (window as any).monaco.editor.getModels();
174
+ for (let model of models) {
175
+ model.dispose();
176
+ }
177
+ }
178
+
179
+ export async function getDbAdapter() {
180
+ let dbAdapter = (globalThis as any).__sqliteAdapter as
181
+ | SQLiteAdapter
182
+ | undefined;
183
+ if (!dbAdapter) {
184
+ dbAdapter = new SQLiteAdapter(sqlSchema);
185
+ (globalThis as any).__sqliteAdapter = dbAdapter;
186
+ }
187
+ return dbAdapter;
188
+ }
189
+
190
+ const realmCacheTeardownRegistrations = new WeakMap<ModuleHooks, Set<string>>();
191
+
192
+ export function setupRealmCacheTeardown(
193
+ hooks: ModuleHooks,
194
+ moduleCacheKey?: string,
195
+ ): void {
196
+ let resolvedModuleCacheKey = moduleCacheKey ?? getCurrentModuleCacheKey();
197
+ let snapshotPrefix = snapshotPrefixForModule(resolvedModuleCacheKey);
198
+ let registrations = realmCacheTeardownRegistrations.get(hooks);
199
+ if (!registrations) {
200
+ registrations = new Set<string>();
201
+ realmCacheTeardownRegistrations.set(hooks, registrations);
202
+ }
203
+ if (registrations.has(snapshotPrefix)) {
204
+ return;
205
+ }
206
+ registrations.add(snapshotPrefix);
207
+ hooks.after(async () => {
208
+ let dbAdapter = await getDbAdapter();
209
+ await dbAdapter.deleteSnapshotsByPrefix(snapshotPrefix);
210
+ });
211
+ }
212
+
213
+ export async function withCachedRealmSetup<T>(
214
+ setupOrAdditionalKey: (() => Promise<T>) | string,
215
+ maybeSetup?: () => Promise<T>,
216
+ ): Promise<T> {
217
+ let moduleCacheKey = getCurrentModuleCacheKey();
218
+ let additionalKey: string | undefined;
219
+ let setup: () => Promise<T>;
220
+
221
+ if (typeof setupOrAdditionalKey === 'function') {
222
+ setup = setupOrAdditionalKey;
223
+ } else {
224
+ if (!maybeSetup) {
225
+ throw new Error(
226
+ 'withCachedRealmSetup(additionalKey, setup) requires a setup callback',
227
+ );
228
+ }
229
+ additionalKey = setupOrAdditionalKey;
230
+ setup = maybeSetup;
231
+ }
232
+
233
+ let snapshotName = snapshotNameForCacheKey(moduleCacheKey, additionalKey);
234
+ let dbAdapter = await getDbAdapter();
235
+ if (dbAdapter.hasSnapshot(snapshotName)) {
236
+ await dbAdapter.importSnapshot(snapshotName);
237
+ return setup();
238
+ }
239
+ let result = await setup();
240
+ await dbAdapter.exportSnapshot(snapshotName);
241
+ return result;
242
+ }
243
+
244
+ function getCurrentModuleCacheKey(): string {
245
+ let config = QUnit.config as QUnit['config'] & {
246
+ currentModule?: { name?: string };
247
+ };
248
+ let moduleName =
249
+ QUnit.config.current?.module?.name ?? config.currentModule?.name;
250
+ if (moduleName?.trim()) {
251
+ return moduleName;
252
+ }
253
+ throw new Error(
254
+ 'withCachedRealmSetup() was called without an explicit cacheKey, but no active QUnit module name was available',
255
+ );
256
+ }
257
+
258
+ function snapshotNameForCacheKey(
259
+ moduleCacheKey: string,
260
+ additionalKey?: string,
261
+ ): string {
262
+ let trimmedModuleCacheKey = moduleCacheKey.trim();
263
+ if (!trimmedModuleCacheKey) {
264
+ throw new Error('snapshotNameForCacheKey() requires a non-empty cache key');
265
+ }
266
+
267
+ let trimmedAdditionalKey = additionalKey?.trim();
268
+ let effectiveCacheKey = trimmedAdditionalKey
269
+ ? `${trimmedModuleCacheKey}::${trimmedAdditionalKey}`
270
+ : trimmedModuleCacheKey;
271
+
272
+ let slug = (trimmedAdditionalKey ?? trimmedModuleCacheKey)
273
+ .toLowerCase()
274
+ .replace(/[^a-z0-9]+/g, '_')
275
+ .replace(/^_+|_+$/g, '')
276
+ .slice(0, 40);
277
+
278
+ if (!slug) {
279
+ slug = 'module';
280
+ }
281
+
282
+ return `${snapshotPrefixForModule(trimmedModuleCacheKey)}${simpleHash(
283
+ effectiveCacheKey,
284
+ )}_${slug}`;
285
+ }
286
+
287
+ function snapshotPrefixForModule(moduleCacheKey: string): string {
288
+ let trimmedModuleCacheKey = moduleCacheKey.trim();
289
+ if (!trimmedModuleCacheKey) {
290
+ throw new Error('snapshotPrefixForModule() requires a non-empty cache key');
291
+ }
292
+ return `snapshot_${simpleHash(trimmedModuleCacheKey)}_`;
293
+ }
294
+
295
+ export async function withSlowSave(
296
+ delayMs: number,
297
+ cb: () => Promise<void>,
298
+ ): Promise<void> {
299
+ let store = getService('store');
300
+ (store as any)._originalPersist = (store as any).persistAndUpdate;
301
+ (store as any).persistAndUpdate = async (
302
+ card: CardDef,
303
+ defaultRealmHref?: string,
304
+ ) => {
305
+ await delay(delayMs);
306
+ await (store as any)._originalPersist(card, defaultRealmHref);
307
+ };
308
+ try {
309
+ return cb();
310
+ } finally {
311
+ (store as any).persistAndUpdate = (store as any)._originalPersist;
312
+ }
313
+ }
314
+
315
+ export async function waitForSyntaxHighlighting(
316
+ textContent: string,
317
+ color: string,
318
+ ) {
319
+ let codeTokens;
320
+ let finalHighlightedToken: Element | undefined;
321
+
322
+ await waitUntil(
323
+ () => {
324
+ codeTokens = findAll('.view-line span span');
325
+ finalHighlightedToken = codeTokens.find(
326
+ (t) => t.innerHTML === textContent,
327
+ );
328
+ return finalHighlightedToken;
329
+ },
330
+ {
331
+ timeout: 10000, // need to wait for monaco to load
332
+ timeoutMessage: `timed out waiting for \`${textContent}\` token`,
333
+ },
334
+ );
335
+
336
+ await waitUntil(
337
+ () =>
338
+ window
339
+ .getComputedStyle(finalHighlightedToken!)
340
+ .getPropertyValue('color') === color,
341
+ {
342
+ timeout: 2000,
343
+ timeoutMessage: 'timed out waiting for syntax highlighting',
344
+ },
345
+ );
346
+ }
347
+ export async function showSearchResult(realmName: string, id: string) {
348
+ await waitFor(
349
+ `[data-test-realm="${realmName}"] [data-test-card-catalog-item]`,
350
+ );
351
+ while (
352
+ document.querySelector(
353
+ `[data-test-realm="${realmName}"] [data-test-show-more-cards]`,
354
+ ) &&
355
+ !document.querySelector(
356
+ `[data-test-realm="${realmName}"] [data-test-card-catalog-item="${id}"]`,
357
+ )
358
+ ) {
359
+ await click(`[data-test-realm="${realmName}"] [data-test-show-more-cards]`);
360
+ }
361
+ }
362
+ export interface Dir {
363
+ [name: string]: string | Dir;
364
+ }
365
+
366
+ export interface CardDocFiles {
367
+ [filename: string]: LooseSingleCardDocument;
368
+ }
369
+
370
+ export interface TestContextWithSave extends TestContext {
371
+ onSave: (subscriber: CardSaveSubscriber) => void;
372
+ unregisterOnSave: () => void;
373
+ }
374
+
375
+ export async function capturePrerenderResult(
376
+ capture: 'textContent' | 'innerHTML' | 'outerHTML',
377
+ expectedStatus: 'ready' | 'error' = 'ready',
378
+ ): Promise<{ status: 'ready' | 'error'; value: string }> {
379
+ await waitUntil(() => {
380
+ let container = document.querySelector(
381
+ '[data-prerender]',
382
+ ) as HTMLElement | null;
383
+ let errorElement = document.querySelector(
384
+ '[data-prerender-error]',
385
+ ) as HTMLElement | null;
386
+ let errorText = (errorElement?.textContent ?? errorElement?.innerHTML ?? '')
387
+ .trim()
388
+ .trim();
389
+ if (expectedStatus === 'error') {
390
+ if (container) {
391
+ let status = container.dataset.prerenderStatus ?? '';
392
+ if (status === 'error' || status === 'unusable') {
393
+ return true;
394
+ }
395
+ }
396
+ return errorText.length > 0;
397
+ }
398
+ if (errorText.length > 0) {
399
+ return true;
400
+ }
401
+ if (!container) {
402
+ return false;
403
+ }
404
+ return (container.dataset.prerenderStatus ?? '') === expectedStatus;
405
+ });
406
+ let container = document.querySelector(
407
+ '[data-prerender]',
408
+ ) as HTMLElement | null;
409
+ let errorElement = document.querySelector(
410
+ '[data-prerender-error]',
411
+ ) as HTMLElement | null;
412
+ let errorText = (
413
+ errorElement?.textContent ??
414
+ errorElement?.innerHTML ??
415
+ ''
416
+ ).trim();
417
+ if (errorText.length > 0) {
418
+ return {
419
+ status: 'error',
420
+ value: normalizeCapturedErrorText(errorText),
421
+ };
422
+ }
423
+ if (!container) {
424
+ throw new Error(
425
+ 'capturePrerenderResult: missing [data-prerender] container after wait',
426
+ );
427
+ }
428
+ let status = container.dataset.prerenderStatus as
429
+ | 'ready'
430
+ | 'error'
431
+ | 'unusable'
432
+ | undefined;
433
+ if (status === 'error' || status === 'unusable') {
434
+ return {
435
+ status: 'error',
436
+ value: normalizeCapturedErrorText(
437
+ container.innerHTML!.replace(/}[^}]*$/, '}'),
438
+ ),
439
+ };
440
+ }
441
+ return { status: 'ready', value: container.children[0][capture]! };
442
+ }
443
+
444
+ export interface WaitForLoadedImageOptions {
445
+ timeout?: number;
446
+ timeoutMessage?: string;
447
+ }
448
+
449
+ export async function waitForLoadedImage(
450
+ selector: string,
451
+ options: WaitForLoadedImageOptions = {},
452
+ ): Promise<HTMLImageElement> {
453
+ let {
454
+ timeout = 5000,
455
+ timeoutMessage = 'Image failed to load - naturalWidth remained 0. This likely indicates an authentication issue preventing the browser from fetching the image.',
456
+ } = options;
457
+
458
+ try {
459
+ await waitUntil(
460
+ () => {
461
+ let currentImg = findLatestMatchingImage(selector);
462
+ return Boolean(currentImg && currentImg.complete);
463
+ },
464
+ {
465
+ timeout,
466
+ timeoutMessage,
467
+ },
468
+ );
469
+ } catch (originalError) {
470
+ let currentImg = findLatestMatchingImage(selector);
471
+ if (currentImg) {
472
+ throw new Error(
473
+ await buildImageLoadErrorMessage(
474
+ selector,
475
+ currentImg,
476
+ timeoutMessage,
477
+ originalError,
478
+ ),
479
+ );
480
+ }
481
+ throw originalError;
482
+ }
483
+
484
+ let loadedImg = findLatestMatchingImage(selector);
485
+ if (!loadedImg) {
486
+ throw new Error(
487
+ `waitForLoadedImage: missing image element matching selector ${selector} after wait`,
488
+ );
489
+ }
490
+ if (loadedImg.naturalWidth === 0) {
491
+ throw new Error(
492
+ await buildImageLoadErrorMessage(selector, loadedImg, timeoutMessage),
493
+ );
494
+ }
495
+ return loadedImg;
496
+ }
497
+
498
+ function findLatestMatchingImage(selector: string): HTMLImageElement | null {
499
+ let candidates = Array.from(document.querySelectorAll(selector)).filter(
500
+ (element): element is HTMLImageElement =>
501
+ element instanceof HTMLImageElement,
502
+ );
503
+ for (let i = candidates.length - 1; i >= 0; i--) {
504
+ let candidate = candidates[i];
505
+ if (candidate.isConnected) {
506
+ return candidate;
507
+ }
508
+ }
509
+ return null;
510
+ }
511
+
512
+ async function buildImageLoadErrorMessage(
513
+ selector: string,
514
+ img: HTMLImageElement,
515
+ baseMessage: string,
516
+ originalError?: unknown,
517
+ ): Promise<string> {
518
+ let srcAttr = img.getAttribute('src') ?? '';
519
+ let currentSrc = img.currentSrc ?? '';
520
+ let targetURL = currentSrc || img.src || srcAttr;
521
+ let probe = await probeImageURL(targetURL);
522
+ let imgDecodeProbe = await probeImgDecode(img);
523
+ let extra =
524
+ originalError instanceof Error && originalError.message
525
+ ? `waitUntil=${originalError.message}`
526
+ : originalError
527
+ ? `waitUntil=${String(originalError)}`
528
+ : 'waitUntil=none';
529
+
530
+ return [
531
+ baseMessage,
532
+ `selector=${selector}`,
533
+ `srcAttr=${srcAttr || '<empty>'}`,
534
+ `currentSrc=${currentSrc || '<empty>'}`,
535
+ `complete=${String(img.complete)}`,
536
+ `naturalWidth=${String(img.naturalWidth)}`,
537
+ `naturalHeight=${String(img.naturalHeight)}`,
538
+ imgDecodeProbe,
539
+ probe,
540
+ extra,
541
+ ].join(' | ');
542
+ }
543
+
544
+ async function probeImageURL(url: string): Promise<string> {
545
+ if (!url) {
546
+ return 'fetchProbe=skipped (missing URL)';
547
+ }
548
+ try {
549
+ let response = await fetch(url, { cache: 'no-store' });
550
+ let contentType = response.headers.get('content-type') ?? '<missing>';
551
+ let contentLength = response.headers.get('content-length') ?? '<missing>';
552
+ let swClientId =
553
+ response.headers.get('x-test-realm-sw-client-id') ?? '<missing>';
554
+ let swClientURL =
555
+ response.headers.get('x-test-realm-sw-client-url') ?? '<missing>';
556
+ let swClientFocused =
557
+ response.headers.get('x-test-realm-sw-client-focused') ?? '<missing>';
558
+ let swClientVisibility =
559
+ response.headers.get('x-test-realm-sw-client-visibility') ?? '<missing>';
560
+ let responseBuffer = await response.clone().arrayBuffer();
561
+ let bytes = new Uint8Array(responseBuffer);
562
+ let checksum = checksum32(bytes);
563
+ let magic = bytesToHexPrefix(bytes, 16);
564
+ let inferredKind = inferImageKind(bytes, contentType);
565
+ let bitmapProbe = await probeCreateImageBitmap(responseBuffer, contentType);
566
+ let virtualNetworkProbe = await probeVirtualNetworkImageURL(url, checksum);
567
+
568
+ return [
569
+ `fetchProbe=${response.status} ${response.statusText || ''}`.trim(),
570
+ `contentType=${contentType}`,
571
+ `contentLength=${contentLength}`,
572
+ `swClientId=${swClientId}`,
573
+ `swClientFocused=${swClientFocused}`,
574
+ `swClientVisibility=${swClientVisibility}`,
575
+ `swClientURL=${swClientURL}`,
576
+ `bodyBytes=${String(bytes.byteLength)}`,
577
+ `checksum=${checksum}`,
578
+ `magic=${magic}`,
579
+ `inferredKind=${inferredKind}`,
580
+ bitmapProbe,
581
+ virtualNetworkProbe,
582
+ ].join(' | ');
583
+ } catch (error) {
584
+ let reason = normalizeErrorMessage(error);
585
+ return `fetchProbe=error (${reason})`;
586
+ }
587
+ }
588
+
589
+ async function probeVirtualNetworkImageURL(
590
+ url: string,
591
+ browserFetchChecksum: string,
592
+ ): Promise<string> {
593
+ try {
594
+ let network = getService('network') as {
595
+ virtualNetwork: { fetch: typeof fetch };
596
+ };
597
+ let response = await network.virtualNetwork.fetch(url);
598
+ let contentType = response.headers.get('content-type') ?? '<missing>';
599
+ let buffer = await response.arrayBuffer();
600
+ let bytes = new Uint8Array(buffer);
601
+ let checksum = checksum32(bytes);
602
+ let magic = bytesToHexPrefix(bytes, 16);
603
+ let inferredKind = inferImageKind(bytes, contentType);
604
+ let matchesBrowserFetch = checksum === browserFetchChecksum ? 'yes' : 'no';
605
+
606
+ return [
607
+ `virtualProbe=${response.status} ${response.statusText || ''}`.trim(),
608
+ `virtualContentType=${contentType}`,
609
+ `virtualBodyBytes=${String(bytes.byteLength)}`,
610
+ `virtualChecksum=${checksum}`,
611
+ `virtualMagic=${magic}`,
612
+ `virtualInferredKind=${inferredKind}`,
613
+ `virtualMatchesFetch=${matchesBrowserFetch}`,
614
+ ].join(' | ');
615
+ } catch (error) {
616
+ return `virtualProbe=error (${normalizeErrorMessage(error)})`;
617
+ }
618
+ }
619
+
620
+ async function probeImgDecode(img: HTMLImageElement): Promise<string> {
621
+ if (typeof img.decode !== 'function') {
622
+ return 'imgDecode=unsupported';
623
+ }
624
+ try {
625
+ await img.decode();
626
+ return 'imgDecode=ok';
627
+ } catch (error) {
628
+ return `imgDecode=error (${normalizeErrorMessage(error)})`;
629
+ }
630
+ }
631
+
632
+ async function probeCreateImageBitmap(
633
+ bytes: ArrayBuffer,
634
+ contentType: string,
635
+ ): Promise<string> {
636
+ if (typeof createImageBitmap !== 'function') {
637
+ return 'bitmapDecode=unsupported';
638
+ }
639
+ try {
640
+ let blob = new Blob([bytes], {
641
+ type:
642
+ contentType === '<missing>' ? 'application/octet-stream' : contentType,
643
+ });
644
+ let bitmap = await createImageBitmap(blob);
645
+ let dimensions = `${bitmap.width}x${bitmap.height}`;
646
+ bitmap.close();
647
+ return `bitmapDecode=ok(${dimensions})`;
648
+ } catch (error) {
649
+ return `bitmapDecode=error (${normalizeErrorMessage(error)})`;
650
+ }
651
+ }
652
+
653
+ function bytesToHexPrefix(bytes: Uint8Array, count: number): string {
654
+ let prefix = bytes.slice(0, count);
655
+ if (!prefix.length) {
656
+ return '<empty>';
657
+ }
658
+ return Array.from(prefix)
659
+ .map((value) => value.toString(16).padStart(2, '0'))
660
+ .join('');
661
+ }
662
+
663
+ function inferImageKind(bytes: Uint8Array, contentType: string): string {
664
+ let loweredContentType = contentType.toLowerCase();
665
+ if (loweredContentType.includes('svg')) {
666
+ return 'svg';
667
+ }
668
+ if (isJpegBytes(bytes)) {
669
+ return 'jpeg';
670
+ }
671
+ if (isPngBytes(bytes)) {
672
+ return 'png';
673
+ }
674
+ if (isGifBytes(bytes)) {
675
+ return 'gif';
676
+ }
677
+ if (isWebpBytes(bytes)) {
678
+ return 'webp';
679
+ }
680
+ if (isAvifBytes(bytes)) {
681
+ return 'avif';
682
+ }
683
+ if (looksLikeSvgText(bytes)) {
684
+ return 'svg-text';
685
+ }
686
+ return 'unknown';
687
+ }
688
+
689
+ function isJpegBytes(bytes: Uint8Array): boolean {
690
+ return (
691
+ bytes.length >= 3 &&
692
+ bytes[0] === 0xff &&
693
+ bytes[1] === 0xd8 &&
694
+ bytes[2] === 0xff
695
+ );
696
+ }
697
+
698
+ function isPngBytes(bytes: Uint8Array): boolean {
699
+ let signature = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
700
+ return signature.every((byte, index) => bytes[index] === byte);
701
+ }
702
+
703
+ function isGifBytes(bytes: Uint8Array): boolean {
704
+ return (
705
+ bytes.length >= 6 &&
706
+ bytes[0] === 0x47 &&
707
+ bytes[1] === 0x49 &&
708
+ bytes[2] === 0x46 &&
709
+ bytes[3] === 0x38 &&
710
+ (bytes[4] === 0x39 || bytes[4] === 0x37) &&
711
+ bytes[5] === 0x61
712
+ );
713
+ }
714
+
715
+ function isWebpBytes(bytes: Uint8Array): boolean {
716
+ return (
717
+ bytes.length >= 12 &&
718
+ bytes[0] === 0x52 &&
719
+ bytes[1] === 0x49 &&
720
+ bytes[2] === 0x46 &&
721
+ bytes[3] === 0x46 &&
722
+ bytes[8] === 0x57 &&
723
+ bytes[9] === 0x45 &&
724
+ bytes[10] === 0x42 &&
725
+ bytes[11] === 0x50
726
+ );
727
+ }
728
+
729
+ function isAvifBytes(bytes: Uint8Array): boolean {
730
+ if (bytes.length < 12) {
731
+ return false;
732
+ }
733
+ let hasFtyp =
734
+ bytes[4] === 0x66 &&
735
+ bytes[5] === 0x74 &&
736
+ bytes[6] === 0x79 &&
737
+ bytes[7] === 0x70;
738
+ if (!hasFtyp) {
739
+ return false;
740
+ }
741
+ let brand = String.fromCharCode(
742
+ bytes[8] ?? 0,
743
+ bytes[9] ?? 0,
744
+ bytes[10] ?? 0,
745
+ bytes[11] ?? 0,
746
+ ).toLowerCase();
747
+ return brand.startsWith('avi');
748
+ }
749
+
750
+ function looksLikeSvgText(bytes: Uint8Array): boolean {
751
+ let sample = new TextDecoder().decode(bytes.slice(0, 256)).toLowerCase();
752
+ return sample.includes('<svg');
753
+ }
754
+
755
+ function normalizeErrorMessage(error: unknown): string {
756
+ return error instanceof Error && error.message
757
+ ? error.message
758
+ : String(error ?? 'unknown');
759
+ }
760
+
761
+ function checksum32(bytes: Uint8Array): string {
762
+ let hash = 0x811c9dc5;
763
+ for (let i = 0; i < bytes.length; i++) {
764
+ hash ^= bytes[i]!;
765
+ hash = Math.imul(hash, 0x01000193) >>> 0;
766
+ }
767
+ return hash.toString(16).padStart(8, '0');
768
+ }
769
+
770
+ function normalizeCapturedErrorText(errorText: string): string {
771
+ let normalized = formatCapturedRenderError(errorText);
772
+ return normalized ?? errorText;
773
+ }
774
+
775
+ function formatCapturedRenderError(errorText: string): string | undefined {
776
+ let renderError = coerceRenderError(errorText);
777
+ if (!renderError) {
778
+ return undefined;
779
+ }
780
+ let normalized = flattenNestedRenderError(normalizeRenderError(renderError));
781
+ return JSON.stringify(normalized, null, 2);
782
+ }
783
+
784
+ function flattenNestedRenderError(renderError: RenderError): RenderError {
785
+ let cloned = JSON.parse(JSON.stringify(renderError)) as RenderError;
786
+ let nested =
787
+ extractNestedErrorPayload(cloned.error.message) ??
788
+ extractNestedErrorPayload(cloned.error.title);
789
+ if (!nested) {
790
+ return cloned;
791
+ }
792
+ cloned.error = {
793
+ ...cloned.error,
794
+ ...nested,
795
+ };
796
+ return cloned;
797
+ }
798
+
799
+ function extractNestedErrorPayload(
800
+ value: unknown,
801
+ ): Record<string, unknown> | undefined {
802
+ if (typeof value !== 'string') {
803
+ return undefined;
804
+ }
805
+ let trimmed = value.trim();
806
+ if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
807
+ return undefined;
808
+ }
809
+ try {
810
+ let nested = JSON.parse(trimmed);
811
+ if (nested && typeof nested === 'object' && 'message' in nested) {
812
+ return nested as Record<string, unknown>;
813
+ }
814
+ } catch (_err) {
815
+ return undefined;
816
+ }
817
+ return undefined;
818
+ }
819
+
820
+ export function captureModuleResult(): {
821
+ status: 'ready' | 'error';
822
+ model: any;
823
+ raw: string;
824
+ } {
825
+ let container = document.querySelector(
826
+ '[data-prerender-module]',
827
+ ) as HTMLElement | null;
828
+ if (!container) {
829
+ throw new Error(
830
+ 'captureModuleResult: missing [data-prerender-module] container after wait',
831
+ );
832
+ }
833
+ let status = (container.dataset.prerenderModuleStatus ?? 'ready') as
834
+ | 'ready'
835
+ | 'error';
836
+ let pre = container.querySelector('pre');
837
+ if (!pre) {
838
+ throw new Error(
839
+ 'captureModuleResult: missing <pre> element inside [data-prerender-module]',
840
+ );
841
+ }
842
+ let raw = pre.textContent ?? '';
843
+ let model = raw ? JSON.parse(raw) : null;
844
+ return { status, model, raw };
845
+ }
846
+
847
+ type RenderingContextWithPrerender = TestContext & {
848
+ owner: Owner;
849
+ __cardPrerenderElement?: HTMLElement;
850
+ __hasMountedCardPrerender?: boolean;
851
+ };
852
+
853
+ export async function makeRenderer() {
854
+ let context = getContext() as RenderingContextWithPrerender;
855
+ let owner = context.owner;
856
+ if (!owner) {
857
+ throw new Error('makeRenderer: missing test owner');
858
+ }
859
+
860
+ let element = context.__cardPrerenderElement;
861
+ if (!element) {
862
+ element = document.createElement('div');
863
+ element.dataset.testCardPrerenderRoot = 'true';
864
+ document.body.appendChild(element);
865
+ context.__cardPrerenderElement = element;
866
+ }
867
+
868
+ if (context.__hasMountedCardPrerender) {
869
+ // Rendering tests can set up multiple realms in one test context. Reusing a
870
+ // single CardPrerender instance for the whole test avoids tearing down an
871
+ // in-flight prerender between realm setups, which would cancel the same
872
+ // background work we are intentionally emulating from the server.
873
+ return;
874
+ }
875
+
876
+ renderIntoElement(
877
+ class CardPrerenderHost extends GlimmerComponent {
878
+ <template><CardPrerender /></template>
879
+ },
880
+ element as unknown as SimpleElement,
881
+ owner,
882
+ );
883
+ context.__hasMountedCardPrerender = true;
884
+ }
885
+
886
+ class MockLocalIndexer extends Service {
887
+ @tracked renderError: string | undefined;
888
+ @tracked prerenderStatus: 'ready' | 'loading' | 'unusable' | undefined;
889
+ url = new URL(testRealmURL);
890
+ #adapter: RealmAdapter | undefined;
891
+ #indexWriter: IndexWriter | undefined;
892
+ #prerenderer: Prerenderer | undefined;
893
+ setup(prerenderer: Prerenderer) {
894
+ if (this.#prerenderer === prerenderer) {
895
+ return;
896
+ }
897
+ this.#prerenderer = prerenderer;
898
+ }
899
+ teardown(prerenderer?: Prerenderer) {
900
+ if (prerenderer && this.#prerenderer !== prerenderer) {
901
+ return;
902
+ }
903
+ this.#prerenderer = undefined;
904
+ }
905
+ async configureRunner(adapter: RealmAdapter, indexWriter: IndexWriter) {
906
+ this.#adapter = adapter;
907
+ this.#indexWriter = indexWriter;
908
+ }
909
+ get adapter() {
910
+ if (!this.#adapter) {
911
+ throw new Error(`adapter has not been set on MockLocalIndexer`);
912
+ }
913
+ return this.#adapter;
914
+ }
915
+ get indexWriter() {
916
+ if (!this.#indexWriter) {
917
+ throw new Error(`indexWriter not registered with MockLocalIndexer`);
918
+ }
919
+ return this.#indexWriter;
920
+ }
921
+ get prerenderer() {
922
+ if (!this.#prerenderer) {
923
+ throw new Error(`prerenderer not registered with MockLocalIndexer`);
924
+ }
925
+ return this.#prerenderer;
926
+ }
927
+ setPrerenderStatus(status: 'ready' | 'loading' | 'unusable') {
928
+ this.prerenderStatus = status;
929
+ }
930
+ setRenderError(error: string) {
931
+ this.renderError = error;
932
+ }
933
+ }
934
+
935
+ export function setupLocalIndexing(hooks: NestedHooks) {
936
+ hooks.beforeEach(async function () {
937
+ let dbAdapter = await getDbAdapter();
938
+ await dbAdapter.reset();
939
+ this.owner.register('service:local-indexer', MockLocalIndexer);
940
+ });
941
+
942
+ hooks.afterEach(async function () {
943
+ // This is here to allow card prerender component (which renders cards as part
944
+ // of the indexer process) to come to a graceful stop before we tear a test
945
+ // down (this should prevent tests from finishing before the prerender is still doing work).
946
+ // Without this, we have been experiencing test failures related to a destroyed owner, e.g.
947
+ // "Cannot call .factoryFor('template:index-card_error') after the owner has been destroyed"
948
+ await settled();
949
+ let store = getService('store');
950
+ await store.flushSaves();
951
+ await store.loaded();
952
+ // Drain any indexing kicked off async by waitForIndex:false writes
953
+ // (e.g. +source POSTs). Without this drain, async indexing from the
954
+ // current test can settle into the next test and produce ghost state
955
+ // / flaky reads from the index.
956
+ for (let entry of getTestRealmRegistry().values()) {
957
+ await entry.realm.incrementalIndexing();
958
+ }
959
+ let context = this as RenderingContextWithPrerender;
960
+ if (context.__cardPrerenderElement) {
961
+ teardownIsolatedRender(
962
+ context.__cardPrerenderElement as unknown as SimpleElement,
963
+ );
964
+ context.__cardPrerenderElement.remove();
965
+ }
966
+ context.__cardPrerenderElement = undefined;
967
+ context.__hasMountedCardPrerender = undefined;
968
+ // In large suites, preserved references can accumulate across tests and
969
+ // keep GC pressure high enough to cause late test timeouts.
970
+ store.resetCache();
971
+ let renderStore = getService('render-store');
972
+ renderStore.resetCache();
973
+ let loaderService = getService('loader-service');
974
+ loaderService.resetLoader({
975
+ clearFetchCache: true,
976
+ reason: 'test teardown',
977
+ });
978
+ getTestRealmRegistry().clear();
979
+ });
980
+ }
981
+
982
+ export function setupOnSave(hooks: NestedHooks) {
983
+ hooks.beforeEach<TestContextWithSave>(function () {
984
+ let store = getService('store');
985
+ this.onSave = store._onSave.bind(store);
986
+ this.unregisterOnSave = store._unregisterSaveSubscriber.bind(store);
987
+ });
988
+ }
989
+
990
+ interface RealmContents {
991
+ [key: string]:
992
+ | CardDef
993
+ | FieldDef
994
+ | LooseSingleCardDocument
995
+ | RealmInfo
996
+ | Record<string, unknown>
997
+ | string
998
+ | Uint8Array;
999
+ }
1000
+
1001
+ export const SYSTEM_CARD_FIXTURE_CONTENTS: RealmContents = {
1002
+ 'ModelConfiguration/test-gpt.json': {
1003
+ data: {
1004
+ type: 'card',
1005
+ attributes: {
1006
+ cardInfo: {
1007
+ cardTitle: 'OpenAI: GPT-5',
1008
+ cardDescription:
1009
+ 'Test fixture model configuration referencing GPT-5.',
1010
+ cardThumbnailURL: null,
1011
+ notes: null,
1012
+ },
1013
+ modelId: 'openai/gpt-5',
1014
+ toolsSupported: true,
1015
+ reasoningEffort: 'minimal',
1016
+ },
1017
+ relationships: {
1018
+ 'cardInfo.theme': {
1019
+ links: {
1020
+ self: null,
1021
+ },
1022
+ },
1023
+ },
1024
+ meta: {
1025
+ adoptsFrom: {
1026
+ module: 'https://cardstack.com/base/system-card',
1027
+ name: 'ModelConfiguration',
1028
+ },
1029
+ },
1030
+ },
1031
+ },
1032
+ 'ModelConfiguration/test-claude-sonnet-46.json': {
1033
+ data: {
1034
+ type: 'card',
1035
+ attributes: {
1036
+ cardInfo: {
1037
+ cardTitle: 'Anthropic: Claude Sonnet 4.6',
1038
+ cardDescription:
1039
+ 'Test fixture model configuration referencing Claude Sonnet 4.6.',
1040
+ cardThumbnailURL: null,
1041
+ notes: null,
1042
+ },
1043
+ modelId: 'anthropic/claude-sonnet-4.6',
1044
+ toolsSupported: true,
1045
+ },
1046
+ relationships: {
1047
+ 'cardInfo.theme': {
1048
+ links: {
1049
+ self: null,
1050
+ },
1051
+ },
1052
+ },
1053
+ meta: {
1054
+ adoptsFrom: {
1055
+ module: 'https://cardstack.com/base/system-card',
1056
+ name: 'ModelConfiguration',
1057
+ },
1058
+ },
1059
+ },
1060
+ },
1061
+ 'ModelConfiguration/test-claude-sonnet-45.json': {
1062
+ data: {
1063
+ type: 'card',
1064
+ attributes: {
1065
+ cardInfo: {
1066
+ cardTitle: 'Anthropic: Claude Sonnet 4.5',
1067
+ cardDescription:
1068
+ 'Test fixture model configuration referencing Claude Sonnet 4.5.',
1069
+ cardThumbnailURL: null,
1070
+ notes: null,
1071
+ },
1072
+ modelId: 'anthropic/claude-sonnet-4.5',
1073
+ toolsSupported: true,
1074
+ },
1075
+ relationships: {
1076
+ 'cardInfo.theme': {
1077
+ links: {
1078
+ self: null,
1079
+ },
1080
+ },
1081
+ },
1082
+ meta: {
1083
+ adoptsFrom: {
1084
+ module: 'https://cardstack.com/base/system-card',
1085
+ name: 'ModelConfiguration',
1086
+ },
1087
+ },
1088
+ },
1089
+ },
1090
+ 'ModelConfiguration/test-claude-37-sonnet.json': {
1091
+ data: {
1092
+ type: 'card',
1093
+ attributes: {
1094
+ cardInfo: {
1095
+ cardTitle: 'Anthropic: Claude 3.7 Sonnet',
1096
+ cardDescription:
1097
+ 'Test fixture model configuration referencing Claude 3.7 Sonnet.',
1098
+ cardThumbnailURL: null,
1099
+ notes: null,
1100
+ },
1101
+ modelId: 'anthropic/claude-3.7-sonnet',
1102
+ toolsSupported: true,
1103
+ },
1104
+ relationships: {
1105
+ 'cardInfo.theme': {
1106
+ links: {
1107
+ self: null,
1108
+ },
1109
+ },
1110
+ },
1111
+ meta: {
1112
+ adoptsFrom: {
1113
+ module: 'https://cardstack.com/base/system-card',
1114
+ name: 'ModelConfiguration',
1115
+ },
1116
+ },
1117
+ },
1118
+ },
1119
+ 'SystemCard/default.json': {
1120
+ data: {
1121
+ type: 'card',
1122
+ attributes: {},
1123
+ relationships: {
1124
+ defaultModelConfiguration: {
1125
+ links: {
1126
+ self: '../ModelConfiguration/test-claude-sonnet-46',
1127
+ },
1128
+ },
1129
+ 'modelConfigurations.0': {
1130
+ links: {
1131
+ self: '../ModelConfiguration/test-gpt',
1132
+ },
1133
+ },
1134
+ 'modelConfigurations.1': {
1135
+ links: {
1136
+ self: '../ModelConfiguration/test-claude-sonnet-46',
1137
+ },
1138
+ },
1139
+ 'modelConfigurations.2': {
1140
+ links: {
1141
+ self: '../ModelConfiguration/test-claude-sonnet-45',
1142
+ },
1143
+ },
1144
+ 'modelConfigurations.3': {
1145
+ links: {
1146
+ self: '../ModelConfiguration/test-claude-37-sonnet',
1147
+ },
1148
+ },
1149
+ },
1150
+ meta: {
1151
+ adoptsFrom: {
1152
+ module: 'https://cardstack.com/base/system-card',
1153
+ name: 'SystemCard',
1154
+ },
1155
+ },
1156
+ },
1157
+ },
1158
+ };
1159
+ export async function setupAcceptanceTestRealm({
1160
+ contents = {},
1161
+ realmURL,
1162
+ permissions,
1163
+ mockMatrixUtils,
1164
+ startMatrix = true,
1165
+ fileSizeLimitBytes,
1166
+ }: {
1167
+ contents: RealmContents;
1168
+ realmURL?: string;
1169
+ permissions?: RealmPermissions;
1170
+ mockMatrixUtils: MockUtils;
1171
+ startMatrix?: boolean;
1172
+ fileSizeLimitBytes?: number;
1173
+ }) {
1174
+ let resolvedRealmURL = ensureTrailingSlash(realmURL ?? testRealmURL);
1175
+ setupAuthEndpoints({
1176
+ [resolvedRealmURL]: deriveTestUserPermissions(permissions),
1177
+ });
1178
+ let result = await setupTestRealm({
1179
+ contents,
1180
+ realmURL: resolvedRealmURL,
1181
+ isAcceptanceTest: true,
1182
+ permissions,
1183
+ mockMatrixUtils,
1184
+ startMatrix,
1185
+ fileSizeLimitBytes,
1186
+ });
1187
+ getTestRealmRegistry().set(result.realm.url, {
1188
+ realm: result.realm,
1189
+ adapter: result.adapter,
1190
+ });
1191
+ return result;
1192
+ }
1193
+
1194
+ export async function setupIntegrationTestRealm({
1195
+ contents = {},
1196
+ realmURL,
1197
+ permissions,
1198
+ mockMatrixUtils,
1199
+ startMatrix = true,
1200
+ fileSizeLimitBytes,
1201
+ }: {
1202
+ contents: RealmContents;
1203
+ realmURL?: string;
1204
+ permissions?: RealmPermissions;
1205
+ mockMatrixUtils: MockUtils;
1206
+ startMatrix?: boolean;
1207
+ fileSizeLimitBytes?: number;
1208
+ }) {
1209
+ let resolvedRealmURL = ensureTrailingSlash(realmURL ?? testRealmURL);
1210
+ setupAuthEndpoints({
1211
+ [resolvedRealmURL]: deriveTestUserPermissions(permissions),
1212
+ });
1213
+ let result = await setupTestRealm({
1214
+ contents,
1215
+ realmURL: resolvedRealmURL,
1216
+ isAcceptanceTest: false,
1217
+ permissions: permissions as RealmPermissions,
1218
+ mockMatrixUtils,
1219
+ startMatrix,
1220
+ fileSizeLimitBytes,
1221
+ });
1222
+ getTestRealmRegistry().set(result.realm.url, {
1223
+ realm: result.realm,
1224
+ adapter: result.adapter,
1225
+ });
1226
+ return result;
1227
+ }
1228
+
1229
+ export async function withoutLoaderMonitoring<T>(cb: () => Promise<T>) {
1230
+ (globalThis as any).__disableLoaderMonitoring = true;
1231
+ try {
1232
+ return (await cb()) as T;
1233
+ } finally {
1234
+ (globalThis as any).__disableLoaderMonitoring = undefined;
1235
+ }
1236
+ }
1237
+
1238
+ export const createPrerenderAuth = (
1239
+ _userId: string,
1240
+ _permissions: RealmPermissions,
1241
+ ) => {
1242
+ // Host tests prerender via the in-app card-prerender component, so we don't need real JWT auth.
1243
+ return JSON.stringify({});
1244
+ };
1245
+
1246
+ async function setupTestRealm({
1247
+ contents,
1248
+ realmURL,
1249
+ isAcceptanceTest,
1250
+ permissions = { '*': ['read', 'write'] },
1251
+ mockMatrixUtils,
1252
+ startMatrix = true,
1253
+ fileSizeLimitBytes,
1254
+ }: {
1255
+ contents: RealmContents;
1256
+ realmURL?: string;
1257
+ isAcceptanceTest?: boolean;
1258
+ permissions?: RealmPermissions;
1259
+ mockMatrixUtils: MockUtils;
1260
+ startMatrix?: boolean;
1261
+ fileSizeLimitBytes?: number;
1262
+ }) {
1263
+ let owner = (getContext() as TestContext).owner;
1264
+ let { virtualNetwork } = getService('network');
1265
+ let { queue } = getService('queue') as QueueService;
1266
+
1267
+ realmURL = realmURL ?? testRealmURL;
1268
+
1269
+ if (isAcceptanceTest) {
1270
+ await visit('/acceptance-test-setup');
1271
+ } else {
1272
+ // We use a rendered component to facilitate our indexing (this emulates
1273
+ // the work that the prerenderer is doing), which means that the
1274
+ // `setupRenderingTest(hooks)` from ember-qunit must be used in your tests.
1275
+ await makeRenderer();
1276
+ }
1277
+
1278
+ let localIndexer = owner.lookup(
1279
+ 'service:local-indexer',
1280
+ ) as unknown as MockLocalIndexer;
1281
+ let realm: Realm;
1282
+
1283
+ let adapter = new TestRealmAdapter(
1284
+ contents,
1285
+ new URL(realmURL),
1286
+ mockMatrixUtils,
1287
+ owner,
1288
+ );
1289
+ let dbAdapter = await getDbAdapter();
1290
+ let definitionLookup = owner.lookup('definition-lookup:main') as
1291
+ | DefinitionLookup
1292
+ | undefined;
1293
+ if (!definitionLookup) {
1294
+ owner.register(
1295
+ 'definition-lookup:main',
1296
+ new CachingDefinitionLookup(
1297
+ dbAdapter,
1298
+ localIndexer.prerenderer,
1299
+ virtualNetwork,
1300
+ createPrerenderAuth,
1301
+ ),
1302
+ {
1303
+ instantiate: false,
1304
+ },
1305
+ );
1306
+ definitionLookup = owner.lookup(
1307
+ 'definition-lookup:main',
1308
+ ) as DefinitionLookup;
1309
+ }
1310
+ await insertPermissions(dbAdapter, new URL(realmURL), permissions);
1311
+ let worker = new Worker({
1312
+ indexWriter: new IndexWriter(dbAdapter),
1313
+ queue,
1314
+ dbAdapter,
1315
+ queuePublisher: queue,
1316
+ virtualNetwork,
1317
+ matrixURL: baseTestMatrix.url,
1318
+ secretSeed: testRealmSecretSeed,
1319
+ realmServerMatrixUsername: testRealmServerMatrixUsername,
1320
+ prerenderer: localIndexer.prerenderer,
1321
+ createPrerenderAuth,
1322
+ });
1323
+
1324
+ realm = new Realm({
1325
+ url: realmURL,
1326
+ adapter,
1327
+ secretSeed: testRealmSecretSeed,
1328
+ virtualNetwork,
1329
+ dbAdapter,
1330
+ queue,
1331
+ matrixClient: new MatrixClient({
1332
+ matrixURL: baseTestMatrix.url,
1333
+ username: testRealmServerMatrixUsername,
1334
+ seed: testRealmSecretSeed,
1335
+ }),
1336
+ realmServerURL: ensureTrailingSlash(ENV.realmServerURL),
1337
+ definitionLookup,
1338
+ cardSizeLimitBytes: Number(
1339
+ process.env.CARD_SIZE_LIMIT_BYTES ?? DEFAULT_CARD_SIZE_LIMIT_BYTES,
1340
+ ),
1341
+ fileSizeLimitBytes:
1342
+ fileSizeLimitBytes ??
1343
+ Number(
1344
+ process.env.FILE_SIZE_LIMIT_BYTES ?? DEFAULT_FILE_SIZE_LIMIT_BYTES,
1345
+ ),
1346
+ });
1347
+
1348
+ // Register the realm early so realm-server mock _info lookups can resolve
1349
+ // without falling back to real network fetches.
1350
+ getTestRealmRegistry().set(realm.url, {
1351
+ realm,
1352
+ adapter,
1353
+ });
1354
+
1355
+ // we use this to run cards that were added to the test filesystem
1356
+ adapter.setLoader(
1357
+ new Loader(realm.__fetchForTesting, virtualNetwork.resolveImport),
1358
+ );
1359
+
1360
+ // TODO this is the only use of Realm.maybeHandle left--can we get rid of it?
1361
+ virtualNetwork.mount(realm.maybeHandle);
1362
+ await adapter.ready;
1363
+ await worker.run();
1364
+ await realm.start();
1365
+ if (startMatrix) {
1366
+ await mockMatrixUtils.start();
1367
+ }
1368
+
1369
+ let realmServer = getService('realm-server');
1370
+ if (!realmServer.availableRealmURLs.includes(realmURL)) {
1371
+ await realmServer.setAvailableRealmURLs([realmURL]);
1372
+ }
1373
+
1374
+ return { realm, adapter };
1375
+ }
1376
+
1377
+ function deriveTestUserPermissions(
1378
+ permissions?: RealmPermissions,
1379
+ ): RealmAction[] {
1380
+ const TEST_MATRIX_USER = '@testuser:localhost';
1381
+ if (!permissions) {
1382
+ return ['read', 'write'];
1383
+ }
1384
+ let forTestUser = permissions[TEST_MATRIX_USER];
1385
+ if (forTestUser) {
1386
+ return forTestUser as RealmAction[];
1387
+ }
1388
+ let wildcard = permissions['*'];
1389
+ if (wildcard) {
1390
+ return wildcard as RealmAction[];
1391
+ }
1392
+ let firstEntry = Object.values(permissions)[0];
1393
+ if (firstEntry) {
1394
+ return firstEntry as RealmAction[];
1395
+ }
1396
+ return ['read', 'write'];
1397
+ }
1398
+
1399
+ export function setupUserSubscription() {
1400
+ const userResponseBody = {
1401
+ data: {
1402
+ type: 'user',
1403
+ id: 1,
1404
+ attributes: {
1405
+ matrixUserId: '@testuser:localhost',
1406
+ stripeCustomerId: 'stripe-id-1',
1407
+ creditsAvailableInPlanAllowance: 1000,
1408
+ creditsIncludedInPlanAllowance: 1000,
1409
+ extraCreditsAvailableInBalance: 100,
1410
+ },
1411
+ relationships: {
1412
+ subscription: {
1413
+ data: {
1414
+ type: 'subscription',
1415
+ id: 1,
1416
+ },
1417
+ },
1418
+ },
1419
+ },
1420
+ included: [
1421
+ {
1422
+ type: 'subscription',
1423
+ id: 1,
1424
+ attributes: {
1425
+ startedAt: '2024-10-15T03:42:11.000Z',
1426
+ endedAt: '2025-10-15T03:42:11.000Z',
1427
+ status: 'active',
1428
+ },
1429
+ relationships: {
1430
+ plan: {
1431
+ data: {
1432
+ type: 'plan',
1433
+ id: 1,
1434
+ },
1435
+ },
1436
+ },
1437
+ },
1438
+ {
1439
+ type: 'plan',
1440
+ id: 1,
1441
+ attributes: {
1442
+ name: 'Free',
1443
+ monthlyPrice: 0,
1444
+ creditsIncluded: 1000,
1445
+ },
1446
+ },
1447
+ ],
1448
+ };
1449
+
1450
+ getService('network').mount(
1451
+ async (req: Request) => {
1452
+ if (req.url.includes('_user')) {
1453
+ return new Response(JSON.stringify(userResponseBody));
1454
+ }
1455
+ return null;
1456
+ },
1457
+ { prepend: true },
1458
+ );
1459
+ }
1460
+
1461
+ export async function saveCard(
1462
+ instance: CardDef,
1463
+ id: string,
1464
+ loader: Loader,
1465
+ store?: CardStore,
1466
+ realmURL?: RealmIdentifier,
1467
+ ) {
1468
+ let api = await loader.import<CardAPI>(`${baseRealm.url}card-api`);
1469
+ let doc = api.serializeCard(instance);
1470
+ doc.data.id = id;
1471
+ if (realmURL) {
1472
+ doc.data.meta = {
1473
+ ...(doc.data.meta ?? {}),
1474
+ realmURL,
1475
+ };
1476
+ }
1477
+ await api.updateFromSerialized(instance, doc, store);
1478
+ await persistDocumentToTestRealm(id, doc);
1479
+ return doc;
1480
+ }
1481
+
1482
+ async function persistDocumentToTestRealm(
1483
+ id: string,
1484
+ doc: LooseSingleCardDocument,
1485
+ ) {
1486
+ if (!id) {
1487
+ return;
1488
+ }
1489
+ let url = new URL(id);
1490
+ let registry = getTestRealmRegistry();
1491
+ let matching = [...registry.values()].find(({ realm }) =>
1492
+ realm.paths.inRealm(rri(url.href)),
1493
+ );
1494
+ if (!matching) {
1495
+ return;
1496
+ }
1497
+ let owner = matching.adapter.owner as
1498
+ | (Owner & { isDestroying?: boolean; isDestroyed?: boolean })
1499
+ | undefined;
1500
+ if (owner?.isDestroying || owner?.isDestroyed) {
1501
+ getTestRealmRegistry().delete(matching.realm.url);
1502
+ return;
1503
+ }
1504
+ let localPath: string;
1505
+ try {
1506
+ localPath = matching.realm.paths.local(url);
1507
+ } catch {
1508
+ return;
1509
+ }
1510
+ if (!localPath.endsWith('.json')) {
1511
+ localPath = `${localPath}.json`;
1512
+ }
1513
+ await matching.adapter.write(localPath, JSON.stringify(doc, null, 2));
1514
+ await matching.realm.realmIndexUpdater.update(
1515
+ [matching.realm.paths.fileURL(localPath)],
1516
+ {
1517
+ async onInvalidation() {},
1518
+ },
1519
+ );
1520
+ }
1521
+
1522
+ export function setupCardLogs(
1523
+ hooks: NestedHooks,
1524
+ apiThunk: () => Promise<CardAPI>,
1525
+ ) {
1526
+ hooks.afterEach(async function () {
1527
+ let api = await apiThunk();
1528
+ await api.flushLogs();
1529
+ });
1530
+ }
1531
+
1532
+ export function setupCardTest(hooks: NestedHooks): {
1533
+ mockMatrixUtils: MockUtils;
1534
+ } {
1535
+ setupRenderingTest(hooks);
1536
+ setupLocalIndexing(hooks);
1537
+ setupOnSave(hooks);
1538
+ setupRealmCacheTeardown(hooks);
1539
+ setupCardLogs(hooks, async () =>
1540
+ (getService('loader-service') as any).loader.import(
1541
+ `${baseRealm.url}card-api`,
1542
+ ),
1543
+ );
1544
+ return {
1545
+ mockMatrixUtils: setupMockMatrix(hooks, {
1546
+ loggedInAs: '@testuser:localhost',
1547
+ activeRealms: [testRealmURL],
1548
+ autostart: true,
1549
+ }),
1550
+ };
1551
+ }
1552
+
1553
+ export function delay(delayAmountMs: number): Promise<void> {
1554
+ return new Promise((resolve) => {
1555
+ setTimeout(resolve, delayAmountMs);
1556
+ });
1557
+ }
1558
+
1559
+ // Create minimal valid PNG bytes for testing (1x1 pixel by default)
1560
+ export function makeMinimalPng(width = 1, height = 1): Uint8Array {
1561
+ let signature = [137, 80, 78, 71, 13, 10, 26, 10];
1562
+ let ihdrData = new Uint8Array(13);
1563
+ let ihdrView = new DataView(ihdrData.buffer);
1564
+ ihdrView.setUint32(0, width);
1565
+ ihdrView.setUint32(4, height);
1566
+ ihdrData[8] = 8; // bit depth
1567
+ ihdrData[9] = 2; // color type (RGB)
1568
+ ihdrData[10] = 0; // compression
1569
+ ihdrData[11] = 0; // filter
1570
+ ihdrData[12] = 0; // interlace
1571
+ let ihdrChunk = buildPngChunk('IHDR', ihdrData);
1572
+ let idatData = new Uint8Array([
1573
+ 0x08, 0xd7, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x01,
1574
+ ]);
1575
+ let idatChunk = buildPngChunk('IDAT', idatData);
1576
+ let iendChunk = buildPngChunk('IEND', new Uint8Array(0));
1577
+ let totalLength =
1578
+ signature.length + ihdrChunk.length + idatChunk.length + iendChunk.length;
1579
+ let png = new Uint8Array(totalLength);
1580
+ let offset = 0;
1581
+ png.set(signature, offset);
1582
+ offset += signature.length;
1583
+ png.set(ihdrChunk, offset);
1584
+ offset += ihdrChunk.length;
1585
+ png.set(idatChunk, offset);
1586
+ offset += idatChunk.length;
1587
+ png.set(iendChunk, offset);
1588
+ return png;
1589
+ }
1590
+
1591
+ function buildPngChunk(type: string, data: Uint8Array): Uint8Array {
1592
+ let chunk = new Uint8Array(4 + 4 + data.length + 4);
1593
+ let view = new DataView(chunk.buffer);
1594
+ view.setUint32(0, data.length);
1595
+ for (let i = 0; i < 4; i++) {
1596
+ chunk[4 + i] = type.charCodeAt(i);
1597
+ }
1598
+ chunk.set(data, 8);
1599
+ let crc = crc32Png(chunk.slice(4, 8 + data.length));
1600
+ view.setUint32(8 + data.length, crc);
1601
+ return chunk;
1602
+ }
1603
+
1604
+ function crc32Png(data: Uint8Array): number {
1605
+ let crc = 0xffffffff;
1606
+ for (let i = 0; i < data.length; i++) {
1607
+ crc ^= data[i]!;
1608
+ for (let j = 0; j < 8; j++) {
1609
+ crc = crc & 1 ? (crc >>> 1) ^ 0xedb88320 : crc >>> 1;
1610
+ }
1611
+ }
1612
+ return (crc ^ 0xffffffff) >>> 0;
1613
+ }
1614
+
1615
+ // --- Created-at test utilities ---
1616
+ // Returns created_at (epoch seconds) from realm_file_meta for a given local file path like 'Pet/mango.json'.
1617
+ export async function getFileCreatedAt(
1618
+ realm: Realm,
1619
+ localPath: string,
1620
+ ): Promise<number | undefined> {
1621
+ let db = await getDbAdapter();
1622
+ return getCreatedTime(db, realm.url, localPath);
1623
+ }
1624
+
1625
+ function changedEntry(
1626
+ listings: { path: string; lastModified?: number }[],
1627
+ entry: { path: string; lastModified?: number },
1628
+ ) {
1629
+ return listings.some(
1630
+ (item) =>
1631
+ item.path === entry.path && item.lastModified != entry.lastModified,
1632
+ );
1633
+ }
1634
+
1635
+ function hasEntry(
1636
+ listings: { path: string; lastModified?: number }[],
1637
+ entry: { path: string; lastModified?: number },
1638
+ ) {
1639
+ return listings.some((item) => item.path === entry.path);
1640
+ }
1641
+
1642
+ export function diff(
1643
+ prevEntries: { path: string; lastModified?: number }[],
1644
+ currEntries: { path: string; lastModified?: number }[],
1645
+ ) {
1646
+ let changed = prevEntries.filter((entry) => changedEntry(currEntries, entry));
1647
+ let added = currEntries.filter((entry) => !hasEntry(prevEntries, entry));
1648
+ let removed = prevEntries.filter((entry) => !hasEntry(currEntries, entry));
1649
+
1650
+ return {
1651
+ added: added.map((e) => e.path),
1652
+ removed: removed.map((e) => e.path),
1653
+ changed: changed.map((e) => e.path),
1654
+ };
1655
+ }
1656
+
1657
+ export async function elementIsVisible(element: Element) {
1658
+ return new Promise((resolve) => {
1659
+ let intersectionObserver = new IntersectionObserver(function (entries) {
1660
+ intersectionObserver.unobserve(element);
1661
+
1662
+ resolve(entries[0].isIntersecting);
1663
+ });
1664
+
1665
+ intersectionObserver.observe(element);
1666
+ });
1667
+ }
1668
+
1669
+ type RealmServerEndpoint = {
1670
+ route: string;
1671
+ getResponse: (req: Request) => Promise<Response>;
1672
+ };
1673
+ export function setupRealmServerEndpoints(
1674
+ hooks: NestedHooks,
1675
+ endpoints?: RealmServerEndpoint[],
1676
+ ) {
1677
+ let defaultEndpoints: RealmServerEndpoint[] = [
1678
+ {
1679
+ route: '_realm-auth',
1680
+ getResponse: async function (_req: Request) {
1681
+ return new Response(JSON.stringify({}), { status: 200 });
1682
+ },
1683
+ },
1684
+ {
1685
+ route: '_server-session',
1686
+ getResponse: async function (req: Request) {
1687
+ let data = await req.json();
1688
+ if (!data.access_token) {
1689
+ return new Response(
1690
+ JSON.stringify({
1691
+ errors: [`Request body missing 'access_token' property`],
1692
+ }),
1693
+ { status: 400 },
1694
+ );
1695
+ }
1696
+ return new Response(null, {
1697
+ status: 201,
1698
+ headers: {
1699
+ Authorization: createJWT(
1700
+ {
1701
+ user: '@testuser:localhost',
1702
+ sessionRoom: 'boxel-session-room-id',
1703
+ },
1704
+ '1d',
1705
+ testRealmSecretSeed,
1706
+ ),
1707
+ },
1708
+ });
1709
+ },
1710
+ },
1711
+ {
1712
+ route: '_user',
1713
+ getResponse: async function (_req: Request) {
1714
+ return new Response(
1715
+ JSON.stringify({
1716
+ data: {
1717
+ type: 'user',
1718
+ id: 1,
1719
+ attributes: {
1720
+ matrixUserId: '@testuser:localhost',
1721
+ stripeCustomerId: 'stripe-id-1',
1722
+ creditsAvailableInPlanAllowance: null,
1723
+ creditsIncludedInPlanAllowance: null,
1724
+ extraCreditsAvailableInBalance: null,
1725
+ },
1726
+ relationships: {
1727
+ subscription: null,
1728
+ },
1729
+ },
1730
+ included: null,
1731
+ }),
1732
+ );
1733
+ },
1734
+ },
1735
+ {
1736
+ route: '_stripe-links',
1737
+ getResponse: async function (_req: Request) {
1738
+ return new Response(
1739
+ JSON.stringify({
1740
+ data: [
1741
+ {
1742
+ type: 'customer-portal-link',
1743
+ id: '1',
1744
+ attributes: {
1745
+ url: 'https://customer-portal-link',
1746
+ },
1747
+ },
1748
+ {
1749
+ type: 'starter-plan-payment-link',
1750
+ id: 'starter-plan-payment-link',
1751
+ attributes: {
1752
+ url: 'https://buy.stripe.com/starter-plan-payment-link',
1753
+ },
1754
+ },
1755
+ {
1756
+ type: 'creator-plan-payment-link',
1757
+ id: 'creator-plan-payment-link',
1758
+ attributes: {
1759
+ url: 'https://buy.stripe.com/creator-plan-payment-link',
1760
+ },
1761
+ },
1762
+ {
1763
+ type: 'power-user-plan-payment-link',
1764
+ id: 'power-user-plan-payment-link',
1765
+ attributes: {
1766
+ url: 'https://buy.stripe.com/power-user-plan-payment-link',
1767
+ },
1768
+ },
1769
+ {
1770
+ type: 'extra-credits-payment-link',
1771
+ id: 'plink_1QP4pEPUHhctoJxaEp1D3my!',
1772
+ attributes: {
1773
+ url: 'https://extra-credits-payment-link-1250',
1774
+ metadata: {
1775
+ creditReloadAmount: 1250,
1776
+ price: 5,
1777
+ },
1778
+ },
1779
+ },
1780
+ {
1781
+ type: 'extra-credits-payment-link',
1782
+ id: 'plink_1QP4pEPUHhctoJxaEp1D3myP',
1783
+ attributes: {
1784
+ url: 'https://extra-credits-payment-link-15000',
1785
+ metadata: {
1786
+ creditReloadAmount: 15000,
1787
+ price: 30,
1788
+ },
1789
+ },
1790
+ },
1791
+ {
1792
+ type: 'extra-credits-payment-link',
1793
+ id: 'plink_1QP4pEPUHhctoJxaEp1D3my!',
1794
+ attributes: {
1795
+ url: 'https://extra-credits-payment-link-80000',
1796
+ metadata: {
1797
+ creditReloadAmount: 80000,
1798
+ price: 100,
1799
+ },
1800
+ },
1801
+ },
1802
+ ],
1803
+ }),
1804
+ );
1805
+ },
1806
+ },
1807
+ ];
1808
+
1809
+ let handleRealmServerRequest = async (req: Request) => {
1810
+ let pathname = new URL(req.url).pathname;
1811
+ let endpoint = endpoints?.find((e) => pathname === `/${e.route}`);
1812
+ if (endpoint) {
1813
+ return await endpoint.getResponse(req);
1814
+ }
1815
+
1816
+ endpoint = defaultEndpoints.find((e) => pathname === `/${e.route}`);
1817
+ if (endpoint) {
1818
+ return await endpoint.getResponse(req);
1819
+ }
1820
+
1821
+ return null;
1822
+ };
1823
+
1824
+ hooks.beforeEach(function () {
1825
+ getService('network').mount(handleRealmServerRequest, { prepend: true });
1826
+ });
1827
+ }
1828
+
1829
+ export async function assertMessages(
1830
+ assert: Assert,
1831
+ messages: {
1832
+ from: string;
1833
+ message?: string;
1834
+ cards?: { id: string; cardTitle?: string; realmIconUrl?: string }[];
1835
+ files?: { name: string; sourceUrl: string }[];
1836
+ }[],
1837
+ ) {
1838
+ assert.dom('[data-test-message-idx]').exists({ count: messages.length });
1839
+ for (let [index, { from, message, cards, files }] of messages.entries()) {
1840
+ assert
1841
+ .dom(
1842
+ `[data-test-message-idx="${index}"][data-test-boxel-message-from="${from}"]`,
1843
+ )
1844
+ .exists({ count: 1 });
1845
+ if (message != null) {
1846
+ assert
1847
+ .dom(`[data-test-message-idx="${index}"] .content`)
1848
+ .containsText(message);
1849
+ }
1850
+ if (cards?.length) {
1851
+ assert
1852
+ .dom(`[data-test-message-idx="${index}"] [data-test-message-items]`)
1853
+ .exists({ count: 1 });
1854
+ assert
1855
+ .dom(`[data-test-message-idx="${index}"] [data-test-attached-card]`)
1856
+ .exists({ count: cards.length });
1857
+ cards.map((card) => {
1858
+ if (card.cardTitle) {
1859
+ if (message != null && card.cardTitle.includes(message)) {
1860
+ throw new Error(
1861
+ `This is not a good test since the message '${message}' overlaps with the asserted card text '${card.cardTitle}'`,
1862
+ );
1863
+ }
1864
+ assert
1865
+ .dom(
1866
+ `[data-test-message-idx="${index}"] [data-test-attached-card="${card.id}"]`,
1867
+ )
1868
+ .containsText(card.cardTitle);
1869
+ }
1870
+
1871
+ if (card.realmIconUrl) {
1872
+ assert
1873
+ .dom(
1874
+ `[data-test-message-idx="${index}"] [data-test-attached-card="${card.id}"] [data-test-realm-icon-url="${card.realmIconUrl}"]`,
1875
+ )
1876
+ .exists({ count: 1 });
1877
+ }
1878
+ });
1879
+ }
1880
+
1881
+ if (files?.length) {
1882
+ assert
1883
+ .dom(`[data-test-message-idx="${index}"] [data-test-message-items]`)
1884
+ .exists({ count: 1 });
1885
+ assert
1886
+ .dom(`[data-test-message-idx="${index}"] [data-test-attached-file]`)
1887
+ .exists({ count: files.length });
1888
+ files.map((file) => {
1889
+ assert
1890
+ .dom(
1891
+ `[data-test-message-idx="${index}"] [data-test-attached-file="${file.sourceUrl}"]`,
1892
+ )
1893
+ .containsText(file.name);
1894
+ });
1895
+ }
1896
+
1897
+ if (!files?.length && !cards?.length) {
1898
+ assert
1899
+ .dom(`[data-test-message-idx="${index}"] [data-test-message-items]`)
1900
+ .doesNotExist();
1901
+ }
1902
+ }
1903
+ }
1904
+
1905
+ export const cardInfo = Object.freeze({
1906
+ name: null,
1907
+ summary: null,
1908
+ cardThumbnailURL: null,
1909
+ notes: null,
1910
+ });
1911
+
1912
+ // UI interaction helpers for acceptance tests
1913
+
1914
+ /**
1915
+ * Verifies that a specific submode is active in the submode switcher
1916
+ */
1917
+ export async function verifySubmode(assert: Assert, submode: string) {
1918
+ await waitFor(`[data-test-submode-switcher=${submode}]`);
1919
+ assert.dom(`[data-test-submode-switcher=${submode}]`).exists();
1920
+ }
1921
+
1922
+ /**
1923
+ * Toggles the file browser tree panel open/closed
1924
+ */
1925
+ export async function toggleFileTree() {
1926
+ await waitFor('[data-test-file-browser-toggle]');
1927
+ await click('[data-test-file-browser-toggle]');
1928
+ }
1929
+
1930
+ // File tree navigation and verification helpers for acceptance tests
1931
+
1932
+ /**
1933
+ * Opens a directory path in the file tree by clicking through the folder hierarchy
1934
+ */
1935
+ export async function openDir(assert: Assert, path: string) {
1936
+ const isFilePath = !path.endsWith('/');
1937
+ const pathToProcess = isFilePath
1938
+ ? path.substring(0, path.lastIndexOf('/'))
1939
+ : path;
1940
+
1941
+ const pathSegments = pathToProcess
1942
+ .split('/')
1943
+ .filter((segment) => segment.length > 0);
1944
+
1945
+ let currentPath = '';
1946
+
1947
+ for (const segment of pathSegments) {
1948
+ currentPath = currentPath ? `${currentPath}${segment}/` : `${segment}/`;
1949
+
1950
+ let selector = `[data-test-directory="${currentPath}"] .icon`;
1951
+ let element = document.querySelector(selector);
1952
+
1953
+ if ((element as HTMLElement)?.classList.contains('closed')) {
1954
+ await click(`[data-test-directory="${currentPath}"]`);
1955
+ }
1956
+
1957
+ assert.dom(selector).hasClass('open');
1958
+ }
1959
+
1960
+ let finalSelector = `[data-test-directory="${pathToProcess}"] .icon`;
1961
+ let finalElement = document.querySelector(finalSelector);
1962
+ let dirName = finalElement?.getAttribute('data-test-directory');
1963
+ return dirName;
1964
+ }
1965
+
1966
+ /**
1967
+ * Verifies a folder with UUID pattern exists in the file tree and validates the UUID
1968
+ */
1969
+ export async function verifyFolderWithUUIDInFileTree(
1970
+ assert: Assert,
1971
+ dirNamePrefix: string, // name without UUID
1972
+ ) {
1973
+ await waitFor(`[data-test-directory^="${dirNamePrefix}-"]`);
1974
+ const element = document.querySelector(
1975
+ `[data-test-directory^="${dirNamePrefix}-"]`,
1976
+ );
1977
+ const dirName = element?.getAttribute('data-test-directory');
1978
+ const uuid = dirName?.replace(`${dirNamePrefix}-`, '').replace('/', '') || '';
1979
+ const { validate: uuidValidate } = await import('uuid');
1980
+ assert.ok(uuidValidate(uuid), 'uuid is a valid uuid');
1981
+ return dirName;
1982
+ }
1983
+
1984
+ /**
1985
+ * Verifies a file exists in the file tree
1986
+ */
1987
+ export async function verifyFileInFileTree(assert: Assert, fileName: string) {
1988
+ const fileSelector = `[data-test-file="${fileName}"]`;
1989
+ await waitFor(fileSelector);
1990
+ assert.dom(fileSelector).exists();
1991
+ }
1992
+
1993
+ /**
1994
+ * Verifies a JSON file with UUID pattern exists in a folder and validates the UUID
1995
+ * TODO: this api is not very good because it looks for the first file in the directory and users may not assume that
1996
+ */
1997
+
1998
+ export async function verifyJSONWithUUIDInFolder(
1999
+ assert: Assert,
2000
+ dirPath: string,
2001
+ ) {
2002
+ const fileSelector = `[data-test-file^="${dirPath}"]`;
2003
+ await waitFor(fileSelector);
2004
+ assert.dom(fileSelector).exists();
2005
+ const element = document.querySelector(fileSelector);
2006
+ const filePath = element?.getAttribute('data-test-file');
2007
+ let parts = filePath?.split('/');
2008
+ if (parts) {
2009
+ let fileName = parts[parts.length - 1];
2010
+ let uuid = fileName.replace(`.json`, '');
2011
+ assert.ok(uuidValidate(uuid), 'uuid is a valid uuid');
2012
+ return filePath;
2013
+ } else {
2014
+ throw new Error(
2015
+ 'file name shape not as expected when checking for [uuid].[extension]',
2016
+ );
2017
+ }
2018
+ }
2019
+
2020
+ export async function addSkillToAiAssistant(
2021
+ skillCardId: string,
2022
+ roomId?: string,
2023
+ ) {
2024
+ let resolvedRoomId =
2025
+ roomId ??
2026
+ document.querySelector('[data-test-room]')?.getAttribute('data-test-room');
2027
+
2028
+ if (!resolvedRoomId) {
2029
+ throw new Error(
2030
+ `Expected an active AI assistant room before adding skill "${skillCardId}"`,
2031
+ );
2032
+ }
2033
+
2034
+ let command = new UpdateRoomSkillsCommand(
2035
+ getService('command-service').commandContext,
2036
+ );
2037
+ await command.execute({
2038
+ roomId: resolvedRoomId,
2039
+ skillCardIdsToActivate: [skillCardId],
2040
+ });
2041
+
2042
+ // Allow the matrix-service's debounced room-state drain (~100ms) and the
2043
+ // mock client's setTimeout(0) listener fan-out to flush before we poll, so
2044
+ // waitUntil sees a consistent snapshot rather than racing the runloop.
2045
+ await settled();
2046
+
2047
+ let matrixService = getService('matrix-service') as {
2048
+ getRoomData(roomId: string): {
2049
+ skillsConfig: {
2050
+ enabledSkillCards?: Array<{ sourceUrl?: string }>;
2051
+ disabledSkillCards?: Array<{ sourceUrl?: string }>;
2052
+ commandDefinitions?: Array<{ sourceUrl?: string }>;
2053
+ };
2054
+ } | null;
2055
+ };
2056
+
2057
+ try {
2058
+ await waitUntil(
2059
+ () =>
2060
+ Boolean(
2061
+ matrixService
2062
+ .getRoomData(resolvedRoomId)
2063
+ ?.skillsConfig.enabledSkillCards?.some(
2064
+ (fileDef) => fileDef.sourceUrl === skillCardId,
2065
+ ),
2066
+ ),
2067
+ {
2068
+ timeout: 5000,
2069
+ timeoutMessage: `Timed out waiting for room skill state for "${skillCardId}"`,
2070
+ },
2071
+ );
2072
+ } catch (err) {
2073
+ // Re-throw with a richer message so the failure report attributes the
2074
+ // flake without us writing to console.* (which can interleave across
2075
+ // parallel runs and trip console-error guards in some setups). The
2076
+ // snapshot describes what the matrix-service believed the room contained
2077
+ // at the moment of timeout.
2078
+ let snapshot = matrixService.getRoomData(resolvedRoomId)?.skillsConfig;
2079
+ let diagnostic = {
2080
+ roomId: resolvedRoomId,
2081
+ skillCardId,
2082
+ skillsConfigPresent: Boolean(snapshot),
2083
+ enabledSkillCards:
2084
+ snapshot?.enabledSkillCards?.map((f) => f.sourceUrl) ?? null,
2085
+ disabledSkillCards:
2086
+ snapshot?.disabledSkillCards?.map((f) => f.sourceUrl) ?? null,
2087
+ commandDefinitionsCount: snapshot?.commandDefinitions?.length ?? null,
2088
+ };
2089
+ let originalMessage = err instanceof Error ? err.message : String(err);
2090
+ let enriched = new Error(
2091
+ `${originalMessage} | diagnostics: ${JSON.stringify(diagnostic)}`,
2092
+ );
2093
+ // Preserve the original failure for any tooling that walks .cause without
2094
+ // relying on the es2022 Error constructor option (host targets es2020).
2095
+ (enriched as Error & { cause?: unknown }).cause =
2096
+ err instanceof Error ? err : undefined;
2097
+ throw enriched;
2098
+ }
2099
+ }