@k-4u/resource-mapper-core 0.0.1

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 (250) hide show
  1. package/.aws-icons-last-updated +1 -0
  2. package/.svelte-kit/ambient.d.ts +263 -0
  3. package/.svelte-kit/generated/client/app.js +31 -0
  4. package/.svelte-kit/generated/client/matchers.js +1 -0
  5. package/.svelte-kit/generated/client/nodes/0.js +1 -0
  6. package/.svelte-kit/generated/client/nodes/1.js +1 -0
  7. package/.svelte-kit/generated/client/nodes/2.js +3 -0
  8. package/.svelte-kit/generated/client/nodes/3.js +3 -0
  9. package/.svelte-kit/generated/client-optimized/app.js +31 -0
  10. package/.svelte-kit/generated/client-optimized/matchers.js +1 -0
  11. package/.svelte-kit/generated/client-optimized/nodes/0.js +1 -0
  12. package/.svelte-kit/generated/client-optimized/nodes/1.js +1 -0
  13. package/.svelte-kit/generated/client-optimized/nodes/2.js +3 -0
  14. package/.svelte-kit/generated/client-optimized/nodes/3.js +3 -0
  15. package/.svelte-kit/generated/root.js +3 -0
  16. package/.svelte-kit/generated/root.svelte +68 -0
  17. package/.svelte-kit/generated/server/internal.js +53 -0
  18. package/.svelte-kit/non-ambient.d.ts +43 -0
  19. package/.svelte-kit/output/client/.vite/manifest.json +175 -0
  20. package/.svelte-kit/output/client/_app/immutable/assets/0.Czt_67iE.css +1 -0
  21. package/.svelte-kit/output/client/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css +1 -0
  22. package/.svelte-kit/output/client/_app/immutable/assets/helpers.ysDrpaDf.css +1 -0
  23. package/.svelte-kit/output/client/_app/immutable/assets/libavoid.DQJapW5w.wasm +0 -0
  24. package/.svelte-kit/output/client/_app/immutable/chunks/BlLuv0eP.js +46 -0
  25. package/.svelte-kit/output/client/_app/immutable/chunks/CSBHmwYv.js +1 -0
  26. package/.svelte-kit/output/client/_app/immutable/chunks/CTCi5ueQ.js +1 -0
  27. package/.svelte-kit/output/client/_app/immutable/chunks/CfOzjaik.js +2 -0
  28. package/.svelte-kit/output/client/_app/immutable/chunks/D4PdvFNs.js +1 -0
  29. package/.svelte-kit/output/client/_app/immutable/chunks/DXgP-QUS.js +2 -0
  30. package/.svelte-kit/output/client/_app/immutable/chunks/DlbDC5An.js +1 -0
  31. package/.svelte-kit/output/client/_app/immutable/chunks/wRWe7aK9.js +1 -0
  32. package/.svelte-kit/output/client/_app/immutable/entry/app.ConrMuHl.js +2 -0
  33. package/.svelte-kit/output/client/_app/immutable/entry/start.Bm6FyGme.js +1 -0
  34. package/.svelte-kit/output/client/_app/immutable/nodes/0.d3cL-ETU.js +1 -0
  35. package/.svelte-kit/output/client/_app/immutable/nodes/1.D6z9rPGv.js +1 -0
  36. package/.svelte-kit/output/client/_app/immutable/nodes/2.CLD-8chl.js +1 -0
  37. package/.svelte-kit/output/client/_app/immutable/nodes/3.DXYeBoel.js +1 -0
  38. package/.svelte-kit/output/client/_app/version.json +1 -0
  39. package/.svelte-kit/output/client/libavoid.wasm +0 -0
  40. package/.svelte-kit/output/client/static/robots.txt +3 -0
  41. package/.svelte-kit/output/prerendered/dependencies/_app/env.js +1 -0
  42. package/.svelte-kit/output/server/.vite/manifest.json +224 -0
  43. package/.svelte-kit/output/server/_app/immutable/assets/LoadingOverlay.DBbe6V8W.css +1 -0
  44. package/.svelte-kit/output/server/_app/immutable/assets/_layout.Czt_67iE.css +1 -0
  45. package/.svelte-kit/output/server/_app/immutable/assets/_page.D9P41uDZ.css +1 -0
  46. package/.svelte-kit/output/server/chunks/ErrorDisplay.js +59 -0
  47. package/.svelte-kit/output/server/chunks/LoadingOverlay.js +12 -0
  48. package/.svelte-kit/output/server/chunks/LoadingOverlay.svelte_svelte_type_style_lang.js +1671 -0
  49. package/.svelte-kit/output/server/chunks/connections.js +33 -0
  50. package/.svelte-kit/output/server/chunks/diagram.js +7 -0
  51. package/.svelte-kit/output/server/chunks/environment.js +34 -0
  52. package/.svelte-kit/output/server/chunks/equality.js +57 -0
  53. package/.svelte-kit/output/server/chunks/exports.js +174 -0
  54. package/.svelte-kit/output/server/chunks/false.js +4 -0
  55. package/.svelte-kit/output/server/chunks/index.js +59 -0
  56. package/.svelte-kit/output/server/chunks/index2.js +2939 -0
  57. package/.svelte-kit/output/server/chunks/index3.js +20 -0
  58. package/.svelte-kit/output/server/chunks/internal.js +1017 -0
  59. package/.svelte-kit/output/server/chunks/shared.js +770 -0
  60. package/.svelte-kit/output/server/chunks/utils.js +43 -0
  61. package/.svelte-kit/output/server/entries/pages/_error.svelte.js +64 -0
  62. package/.svelte-kit/output/server/entries/pages/_layout.svelte.js +65 -0
  63. package/.svelte-kit/output/server/entries/pages/_page.svelte.js +3991 -0
  64. package/.svelte-kit/output/server/entries/pages/_page.ts.js +30 -0
  65. package/.svelte-kit/output/server/entries/pages/group/_groupId_/_page.svelte.js +67 -0
  66. package/.svelte-kit/output/server/entries/pages/group/_groupId_/_page.ts.js +47 -0
  67. package/.svelte-kit/output/server/index.js +3747 -0
  68. package/.svelte-kit/output/server/internal.js +13 -0
  69. package/.svelte-kit/output/server/manifest-full.js +47 -0
  70. package/.svelte-kit/output/server/manifest.js +47 -0
  71. package/.svelte-kit/output/server/nodes/0.js +8 -0
  72. package/.svelte-kit/output/server/nodes/1.js +8 -0
  73. package/.svelte-kit/output/server/nodes/2.js +10 -0
  74. package/.svelte-kit/output/server/nodes/3.js +10 -0
  75. package/.svelte-kit/output/server/remote-entry.js +557 -0
  76. package/.svelte-kit/tsconfig.json +61 -0
  77. package/.svelte-kit/types/route_meta_data.json +8 -0
  78. package/.svelte-kit/types/src/routes/$types.d.ts +26 -0
  79. package/.svelte-kit/types/src/routes/group/[groupId]/$types.d.ts +21 -0
  80. package/.svelte-kit/types/src/routes/group/[groupId]/proxy+page.ts +49 -0
  81. package/.svelte-kit/types/src/routes/proxy+page.ts +33 -0
  82. package/build/_app/env.js +1 -0
  83. package/build/_app/env.js.br +1 -0
  84. package/build/_app/env.js.gz +0 -0
  85. package/build/_app/immutable/assets/0.Czt_67iE.css +1 -0
  86. package/build/_app/immutable/assets/0.Czt_67iE.css.br +0 -0
  87. package/build/_app/immutable/assets/0.Czt_67iE.css.gz +0 -0
  88. package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css +1 -0
  89. package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css.br +0 -0
  90. package/build/_app/immutable/assets/TeamContactCard.Dxj5nUCr.css.gz +0 -0
  91. package/build/_app/immutable/assets/helpers.ysDrpaDf.css +1 -0
  92. package/build/_app/immutable/assets/helpers.ysDrpaDf.css.br +0 -0
  93. package/build/_app/immutable/assets/helpers.ysDrpaDf.css.gz +0 -0
  94. package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm +0 -0
  95. package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm.br +0 -0
  96. package/build/_app/immutable/assets/libavoid.DQJapW5w.wasm.gz +0 -0
  97. package/build/_app/immutable/chunks/BlLuv0eP.js +46 -0
  98. package/build/_app/immutable/chunks/BlLuv0eP.js.br +0 -0
  99. package/build/_app/immutable/chunks/BlLuv0eP.js.gz +0 -0
  100. package/build/_app/immutable/chunks/CSBHmwYv.js +1 -0
  101. package/build/_app/immutable/chunks/CSBHmwYv.js.br +0 -0
  102. package/build/_app/immutable/chunks/CSBHmwYv.js.gz +0 -0
  103. package/build/_app/immutable/chunks/CTCi5ueQ.js +1 -0
  104. package/build/_app/immutable/chunks/CTCi5ueQ.js.br +0 -0
  105. package/build/_app/immutable/chunks/CTCi5ueQ.js.gz +0 -0
  106. package/build/_app/immutable/chunks/CfOzjaik.js +2 -0
  107. package/build/_app/immutable/chunks/CfOzjaik.js.br +0 -0
  108. package/build/_app/immutable/chunks/CfOzjaik.js.gz +0 -0
  109. package/build/_app/immutable/chunks/D4PdvFNs.js +1 -0
  110. package/build/_app/immutable/chunks/D4PdvFNs.js.br +0 -0
  111. package/build/_app/immutable/chunks/D4PdvFNs.js.gz +0 -0
  112. package/build/_app/immutable/chunks/DXgP-QUS.js +2 -0
  113. package/build/_app/immutable/chunks/DXgP-QUS.js.br +0 -0
  114. package/build/_app/immutable/chunks/DXgP-QUS.js.gz +0 -0
  115. package/build/_app/immutable/chunks/DlbDC5An.js +1 -0
  116. package/build/_app/immutable/chunks/DlbDC5An.js.br +0 -0
  117. package/build/_app/immutable/chunks/DlbDC5An.js.gz +0 -0
  118. package/build/_app/immutable/chunks/wRWe7aK9.js +1 -0
  119. package/build/_app/immutable/chunks/wRWe7aK9.js.br +0 -0
  120. package/build/_app/immutable/chunks/wRWe7aK9.js.gz +0 -0
  121. package/build/_app/immutable/entry/app.ConrMuHl.js +2 -0
  122. package/build/_app/immutable/entry/app.ConrMuHl.js.br +0 -0
  123. package/build/_app/immutable/entry/app.ConrMuHl.js.gz +0 -0
  124. package/build/_app/immutable/entry/start.Bm6FyGme.js +1 -0
  125. package/build/_app/immutable/entry/start.Bm6FyGme.js.br +2 -0
  126. package/build/_app/immutable/entry/start.Bm6FyGme.js.gz +0 -0
  127. package/build/_app/immutable/nodes/0.d3cL-ETU.js +1 -0
  128. package/build/_app/immutable/nodes/0.d3cL-ETU.js.br +0 -0
  129. package/build/_app/immutable/nodes/0.d3cL-ETU.js.gz +0 -0
  130. package/build/_app/immutable/nodes/1.D6z9rPGv.js +1 -0
  131. package/build/_app/immutable/nodes/1.D6z9rPGv.js.br +0 -0
  132. package/build/_app/immutable/nodes/1.D6z9rPGv.js.gz +0 -0
  133. package/build/_app/immutable/nodes/2.CLD-8chl.js +1 -0
  134. package/build/_app/immutable/nodes/2.CLD-8chl.js.br +0 -0
  135. package/build/_app/immutable/nodes/2.CLD-8chl.js.gz +0 -0
  136. package/build/_app/immutable/nodes/3.DXYeBoel.js +1 -0
  137. package/build/_app/immutable/nodes/3.DXYeBoel.js.br +0 -0
  138. package/build/_app/immutable/nodes/3.DXYeBoel.js.gz +0 -0
  139. package/build/_app/version.json +1 -0
  140. package/build/_app/version.json.br +0 -0
  141. package/build/_app/version.json.gz +0 -0
  142. package/build/index.html +34 -0
  143. package/build/index.html.br +0 -0
  144. package/build/index.html.gz +0 -0
  145. package/build/libavoid.wasm +0 -0
  146. package/build/libavoid.wasm.br +0 -0
  147. package/build/libavoid.wasm.gz +0 -0
  148. package/build/static/robots.txt +3 -0
  149. package/coverage/coverage-final.json +6 -0
  150. package/coverage/coverage-summary.json +7 -0
  151. package/coverage/junit.xml +57 -0
  152. package/coverage/lcov-report/base.css +224 -0
  153. package/coverage/lcov-report/block-navigation.js +87 -0
  154. package/coverage/lcov-report/favicon.png +0 -0
  155. package/coverage/lcov-report/index.html +131 -0
  156. package/coverage/lcov-report/prettify.css +1 -0
  157. package/coverage/lcov-report/prettify.js +2 -0
  158. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  159. package/coverage/lcov-report/sorter.js +210 -0
  160. package/coverage/lcov-report/stores/index.html +116 -0
  161. package/coverage/lcov-report/stores/routingStore.ts.html +781 -0
  162. package/coverage/lcov-report/utils/flow/helpers.ts.html +127 -0
  163. package/coverage/lcov-report/utils/flow/index.html +161 -0
  164. package/coverage/lcov-report/utils/flow/layout.ts.html +805 -0
  165. package/coverage/lcov-report/utils/flow/serviceIds.ts.html +97 -0
  166. package/coverage/lcov-report/utils/flow/servicesGraph.ts.html +859 -0
  167. package/coverage/lcov.info +646 -0
  168. package/coverage/test-results.json +1 -0
  169. package/data/services/api/api-gateway.yaml +18 -0
  170. package/data/services/api/group-info.yaml +7 -0
  171. package/data/services/api/lambda-orders.yaml +21 -0
  172. package/data/services/api/lambda-products.yaml +15 -0
  173. package/data/services/api/lambda-users.yaml +15 -0
  174. package/data/services/compute/alb.yaml +15 -0
  175. package/data/services/compute/ecs-inventory.yaml +16 -0
  176. package/data/services/compute/ecs-notification.yaml +15 -0
  177. package/data/services/compute/group-info.yaml +7 -0
  178. package/data/services/data/dynamodb-notifications.yaml +12 -0
  179. package/data/services/data/dynamodb-orders.yaml +9 -0
  180. package/data/services/data/dynamodb-products.yaml +9 -0
  181. package/data/services/data/dynamodb-users.yaml +9 -0
  182. package/data/services/data/group-info.yaml +7 -0
  183. package/data/services/data/rds-postgres.yaml +9 -0
  184. package/data/services/data/redis.yaml +10 -0
  185. package/data/services/frontend/cloudfront.yaml +12 -0
  186. package/data/services/frontend/group-info.yaml +7 -0
  187. package/data/services/frontend/route53.yaml +15 -0
  188. package/data/services/frontend/s3-website.yaml +9 -0
  189. package/data/teams/cloud-shepherds.yaml +15 -0
  190. package/data/teams/data-wizards.yaml +15 -0
  191. package/data/teams/interface-architects.yaml +19 -0
  192. package/e2e/demo.test.ts +54 -0
  193. package/e2e/header-toolbar.simple.spec.ts +0 -0
  194. package/e2e/header-toolbar.spec.ts +53 -0
  195. package/e2e/layout.spec.ts +30 -0
  196. package/package.json +69 -0
  197. package/playwright.config.ts +10 -0
  198. package/plugins/mapper-data-plugin.ts +32 -0
  199. package/project.json +23 -0
  200. package/src/app.css +125 -0
  201. package/src/app.d.ts +31 -0
  202. package/src/app.html +11 -0
  203. package/src/lib/assets/favicon.svg +19 -0
  204. package/src/lib/components/EmptyState.svelte +37 -0
  205. package/src/lib/components/ErrorDisplay.svelte +82 -0
  206. package/src/lib/components/FlowCanvas.svelte +223 -0
  207. package/src/lib/components/GenericSidebarCard.svelte +44 -0
  208. package/src/lib/components/GroupDetailSidebar.svelte +31 -0
  209. package/src/lib/components/Header.svelte +57 -0
  210. package/src/lib/components/Legend.svelte +25 -0
  211. package/src/lib/components/LoadingOverlay.svelte +42 -0
  212. package/src/lib/components/LoadingSpinner.svelte +10 -0
  213. package/src/lib/components/ServiceDetailSidebar.svelte +90 -0
  214. package/src/lib/components/TeamContactCard.svelte +166 -0
  215. package/src/lib/components/flow/ExternalNode.svelte +45 -0
  216. package/src/lib/components/flow/MainGroupNode.svelte +24 -0
  217. package/src/lib/components/flow/ServiceGroupNode.svelte +17 -0
  218. package/src/lib/components/flow/ServiceNode.svelte +40 -0
  219. package/src/lib/components/flow/SnakeEdge.svelte +206 -0
  220. package/src/lib/components/flow/index.ts +6 -0
  221. package/src/lib/components/index.ts +12 -0
  222. package/src/lib/data/connections.ts +26 -0
  223. package/src/lib/data/groups.ts +11 -0
  224. package/src/lib/data/services.ts +12 -0
  225. package/src/lib/data/teams.ts +11 -0
  226. package/src/lib/index.ts +1 -0
  227. package/src/lib/state/theme.svelte.ts +21 -0
  228. package/src/lib/stores/diagram.ts +6 -0
  229. package/src/lib/stores/routingStore.test.ts +133 -0
  230. package/src/lib/stores/routingStore.ts +232 -0
  231. package/src/lib/utils/awsIcons.ts +117 -0
  232. package/src/lib/utils/flow/groupOverviewGraph.ts +73 -0
  233. package/src/lib/utils/flow/helpers.ts +14 -0
  234. package/src/lib/utils/flow/layout.test.ts +271 -0
  235. package/src/lib/utils/flow/layout.ts +240 -0
  236. package/src/lib/utils/flow/serviceIds.ts +5 -0
  237. package/src/lib/utils/flow/servicesGraph.test.ts +119 -0
  238. package/src/lib/utils/flow/servicesGraph.ts +258 -0
  239. package/src/routes/+error.svelte +36 -0
  240. package/src/routes/+layout.svelte +27 -0
  241. package/src/routes/+page.svelte +81 -0
  242. package/src/routes/+page.ts +31 -0
  243. package/src/routes/group/[groupId]/+page.svelte +102 -0
  244. package/src/routes/group/[groupId]/+page.ts +48 -0
  245. package/src/routes/layout.css +0 -0
  246. package/static/static/robots.txt +3 -0
  247. package/svelte.config.js +28 -0
  248. package/tailwind.config.js +12 -0
  249. package/tsconfig.json +22 -0
  250. package/vite.config.ts +81 -0
package/src/app.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ // See https://svelte.dev/docs/kit/types#app.d.ts
2
+ // for information about these interfaces
3
+ declare global {
4
+ namespace App {
5
+ // interface Error {}
6
+ // interface Locals {}
7
+ // interface PageData {}
8
+ // interface PageState {}
9
+ // interface Platform {}
10
+ }
11
+ }
12
+
13
+ console.log('Adding virtual:mapper-data module declaration');
14
+ declare global {
15
+ declare module 'virtual:mapper-data' {
16
+ import type {GroupInfo, GroupConnection, ServiceDefinition, ExternalGroupServices, Team} from '$shared/types';
17
+
18
+ const bakedData: {
19
+ groups: Record<string, GroupInfo>;
20
+ services: Record<string, ServiceDefinition>;
21
+ teams: Record<string, Team>;
22
+ groupConnections: GroupConnection[];
23
+ servicesByGroup: Record<string, ServiceDefinition[]>;
24
+ externalServices: Record<string, ExternalGroupServices[]>;
25
+ generatedAt: Date
26
+ }
27
+ export default bakedData;
28
+ }
29
+ }
30
+
31
+ export {};
package/src/app.html ADDED
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ %sveltekit.head%
7
+ </head>
8
+ <body data-sveltekit-preload-data="hover" class="bg-white dark:bg-gray-900">
9
+ <div style="display: contents">%sveltekit.body%</div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,19 @@
1
+ <svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+
3
+ <path d="M64 42V58M64 58H34V72M64 58H94V72" stroke="#38BDF8" stroke-width="4" stroke-linejoin="round" stroke-linecap="round"/>
4
+
5
+ <rect x="34" y="20" width="60" height="22" rx="6" fill="#92400E" stroke="#F59E0B" stroke-width="2"/>
6
+ <rect x="42" y="28" width="10" height="6" rx="1" fill="#F59E0B"/>
7
+ <rect x="76" y="28" width="10" height="6" rx="1" fill="#F59E0B"/>
8
+
9
+ <rect x="14" y="72" width="44" height="28" rx="6" fill="#065F46" stroke="#10B981" stroke-width="2"/>
10
+ <circle cx="26" cy="86" r="5" fill="#10B981" />
11
+ <rect x="36" y="83" width="14" height="6" rx="1" fill="#10B981"/>
12
+
13
+ <rect x="70" y="72" width="44" height="28" rx="6" fill="#065F46" stroke="#10B981" stroke-width="2"/>
14
+ <circle cx="82" cy="86" r="5" fill="#10B981"/>
15
+ <rect x="92" y="83" width="14" height="6" rx="1" fill="#10B981"/>
16
+
17
+ <rect x="22" y="55" width="16" height="8" rx="2" fill="#1E293B" stroke="#38BDF8" stroke-width="1"/>
18
+ <rect x="90" y="55" width="16" height="8" rx="2" fill="#1E293B" stroke="#38BDF8" stroke-width="1"/>
19
+ </svg>
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import Icon from '@iconify/svelte';
3
+
4
+ interface Props {
5
+ title?: string
6
+ message?: string
7
+ actionLabel?: string | null
8
+ onAction?: (() => void) | null
9
+ }
10
+
11
+ let {
12
+ title = 'Nothing to show',
13
+ message = '',
14
+ actionLabel = null,
15
+ onAction = null
16
+ }: Props = $props()
17
+ </script>
18
+
19
+ <div class="flex h-full w-full items-center justify-center p-8">
20
+ <div class="max-w-md rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 p-8 text-center shadow">
21
+ <div class="flex items-center justify-center mb-4">
22
+ <Icon icon="mdi:package-variant" class="text-4xl text-gray-400 dark:text-gray-300" aria-hidden="true" />
23
+ </div>
24
+ <h2 class="big-title text-xl font-semibold text-gray-900 dark:text-gray-100">{title}</h2>
25
+ {#if message}
26
+ <p class="mt-2 text-base text-gray-700 dark:text-gray-300">{message}</p>
27
+ {/if}
28
+ {#if actionLabel && onAction}
29
+ <button
30
+ class="mt-6 w-full rounded-md bg-blue-600 py-2 text-white hover:bg-blue-500"
31
+ on:click={onAction}
32
+ >
33
+ {actionLabel}
34
+ </button>
35
+ {/if}
36
+ </div>
37
+ </div>
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import Icon from '@iconify/svelte';
3
+
4
+ let {
5
+ title = 'Something went wrong',
6
+ message = 'An unexpected error occurred.',
7
+ checkList = [],
8
+ technicalDetails = null,
9
+ onRetry = null,
10
+ onBack = null
11
+ } = $props<{
12
+ title?: string;
13
+ message?: string;
14
+ checkList?: string[];
15
+ technicalDetails?: string | Record<string, unknown> | null;
16
+ onRetry?: (() => void) | null;
17
+ onBack?: (() => void) | null;
18
+ }>();
19
+
20
+ let formattedDetails = $derived(
21
+ technicalDetails
22
+ ? typeof technicalDetails === 'string'
23
+ ? technicalDetails
24
+ : JSON.stringify(technicalDetails, null, 2)
25
+ : ''
26
+ );
27
+
28
+ let copied = $state(false)
29
+
30
+ async function copyDetails() {
31
+ if (!formattedDetails) return
32
+ await navigator.clipboard.writeText(formattedDetails)
33
+ copied = true
34
+ setTimeout(() => (copied = false), 2000)
35
+ }
36
+ </script>
37
+
38
+ <div class="flex h-full w-full items-center justify-center p-8">
39
+ <div class="w-full max-w-2xl rounded-2xl border border-red-500/40 bg-red-500/10 p-6 shadow">
40
+ <div class="flex items-center text-red-300">
41
+ <Icon icon="mdi:alert-circle-outline" class="mr-2 h-7 w-7 text-red-400" aria-hidden="true" />
42
+ <h2 class="big-title text-xl font-semibold">{title}</h2>
43
+ </div>
44
+ <p class="mt-2 text-base text-gray-700 dark:text-gray-200">{message}</p>
45
+
46
+ {#if checkList.length}
47
+ <div class="mt-4 rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 p-4">
48
+ <p class="text-sm font-semibold text-gray-700 dark:text-gray-300">Please check:</p>
49
+ <ul class="mt-2 list-disc space-y-1 pl-5 text-sm text-gray-600 dark:text-gray-400">
50
+ {#each checkList as item}
51
+ <li>{item}</li>
52
+ {/each}
53
+ </ul>
54
+ </div>
55
+ {/if}
56
+
57
+ {#if formattedDetails}
58
+ <div class="mt-4 rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 p-4">
59
+ <div class="flex items-center justify-between">
60
+ <span class="text-sm font-semibold text-gray-700 dark:text-gray-300">Technical details</span>
61
+ <button class="text-sm text-blue-600 dark:text-blue-400 hover:text-blue-500 dark:hover:text-blue-300" onclick={copyDetails}>
62
+ {copied ? 'Copied' : 'Copy'}
63
+ </button>
64
+ </div>
65
+ <pre class="mt-2 max-h-48 overflow-auto rounded bg-gray-50 dark:bg-black/40 p-3 text-xs text-gray-700 dark:text-gray-300">{formattedDetails}</pre>
66
+ </div>
67
+ {/if}
68
+
69
+ <div class="mt-6 flex flex-wrap gap-3">
70
+ {#if onRetry}
71
+ <button class="rounded-md bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-500" onclick={onRetry}>
72
+ Retry
73
+ </button>
74
+ {/if}
75
+ {#if onBack}
76
+ <button class="rounded-md border border-gray-300 dark:border-gray-700 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800" onclick={onBack}>
77
+ Back
78
+ </button>
79
+ {/if}
80
+ </div>
81
+ </div>
82
+ </div>
@@ -0,0 +1,223 @@
1
+ <script lang="ts">
2
+ import {onMount} from 'svelte'
3
+ import {
4
+ Background,
5
+ BackgroundVariant,
6
+ ControlButton,
7
+ Controls,
8
+ type Edge,
9
+ type EdgeTypes,
10
+ MiniMap,
11
+ type Node,
12
+ type NodeTypes,
13
+ Panel,
14
+ SvelteFlow
15
+ } from '@xyflow/svelte'
16
+ import {ExternalNode, Legend, MainGroupNode, ServiceGroupNode, ServiceNode, SnakeEdge} from '$lib/components'
17
+ import {layoutFlowGraph} from '$lib/utils/flow/layout'
18
+ import type {FlowGraphInput} from '$shared/flow-types'
19
+ import {logDiagramAction, showLegend} from '$lib/stores/diagram';
20
+ import Icon from "@iconify/svelte";
21
+ import {goto} from "$app/navigation";
22
+ import {theme} from '$lib/state/theme.svelte';
23
+
24
+ const DOUBLE_CLICK_THRESHOLD = 400
25
+
26
+ // Svelte 5 Props
27
+ let {
28
+ graph = null,
29
+ pending = false,
30
+ onnodeDoubleClick
31
+ } = $props<{
32
+ graph?: FlowGraphInput | null;
33
+ pending?: boolean;
34
+ onnodeDoubleClick?: (nodeId: string) => void;
35
+ }>();
36
+
37
+
38
+ const nodeTypes = {
39
+ mainGroup: MainGroupNode,
40
+ service: ServiceNode,
41
+ external: ExternalNode,
42
+ serviceGroup: ServiceGroupNode
43
+ } as NodeTypes
44
+ const edgeTypes = {snake: SnakeEdge} as EdgeTypes
45
+
46
+ // Svelte 5 State
47
+ let nodes: Node[] = $state([])
48
+ let edges: Edge[] = $state([])
49
+
50
+ let emptyStateLabel = $state('No diagram available.')
51
+ let lastClickedNode: string | null = $state(null)
52
+ let lastClickTimestamp = $state(0)
53
+ let layoutError = $state('')
54
+ let layoutBusy = $state(false)
55
+ let graphReady = $state(false)
56
+ let currentSignature = $state('')
57
+
58
+ // Svelte 5 Derived
59
+ let hasGraphData = $derived(!!graph && graph.serviceNodes.length > 0)
60
+ let showOverlay = $derived(pending || layoutBusy || !graphReady)
61
+
62
+ // TODO: This console.debug might be better as an effect or just removed if not needed for production
63
+ $effect(() => {
64
+ console.debug('[FlowCanvas] overlay state', {showOverlay, pending, layoutBusy, graphReady})
65
+ })
66
+
67
+ onMount(() => {
68
+ let lastLogAction = 0;
69
+ // TODO: Consider converting logDiagramAction store to a rune-based state if possible, or use $effect
70
+ const unsubscribe = logDiagramAction.subscribe((n) => {
71
+ if (n !== lastLogAction) {
72
+ lastLogAction = n;
73
+ logDiagram();
74
+ }
75
+ });
76
+ return unsubscribe;
77
+ })
78
+
79
+ // 3. Fix the Reactive Trigger
80
+ // Ensure this ONLY triggers when the parent passes a brand new graph
81
+ $effect(() => {
82
+ const newTargetSignature = graph?.signature ?? '';
83
+ if (newTargetSignature !== '' && newTargetSignature !== currentSignature) {
84
+ runLayout(graph, newTargetSignature);
85
+ }
86
+ });
87
+
88
+ async function runLayout(sourceGraph: FlowGraphInput | null, signature: string) {
89
+ layoutError = ''
90
+
91
+ graphReady = false
92
+
93
+ console.debug('[FlowCanvas] runLayout start', {hasGraph: !!sourceGraph, signature})
94
+ if (!sourceGraph) {
95
+ nodes = []
96
+ edges = []
97
+ emptyStateLabel = pending ? 'Rendering diagram…' : 'No diagram available.'
98
+ currentSignature = signature
99
+ console.debug('[FlowCanvas] runLayout skipped - no graph data')
100
+ return
101
+ }
102
+ layoutBusy = true
103
+ try {
104
+ const result = await layoutFlowGraph(sourceGraph)
105
+ if (result.signature !== signature) {
106
+ console.debug('[FlowCanvas] runLayout result ignored due to stale signature', {
107
+ expected: signature,
108
+ received: result.signature
109
+ })
110
+ return
111
+ }
112
+ nodes = result.nodes as Node[]
113
+ edges = result.edges as Edge[]
114
+
115
+ currentSignature = result.signature;
116
+
117
+ graphReady = true
118
+ console.debug('[FlowCanvas] runLayout complete', {
119
+ nodeCount: result.nodes.length,
120
+ edgeCount: result.edges.length
121
+ })
122
+ } catch (error) {
123
+ layoutError = error instanceof Error ? error.message : 'Failed to lay out diagram.'
124
+ console.error('[FlowCanvas] layout error', error)
125
+ nodes = []
126
+ edges = []
127
+ graphReady = false
128
+ } finally {
129
+ layoutBusy = false
130
+ console.debug('[FlowCanvas] runLayout finished', {graphReady, layoutBusy})
131
+ }
132
+ }
133
+
134
+ //TODO: This handleFlowNodeClick function seems to be unused in the template. If it's intended for Node components, consider passing it down.
135
+ function handleFlowNodeClick(event: CustomEvent<{ node?: { id?: string } }>) {
136
+ const nodeId = event.detail?.node?.id
137
+ if (!nodeId) return
138
+ const now = Date.now()
139
+ if (lastClickedNode === nodeId && now - lastClickTimestamp <= DOUBLE_CLICK_THRESHOLD) {
140
+ onnodeDoubleClick?.(nodeId)
141
+ lastClickedNode = null
142
+ lastClickTimestamp = 0
143
+ return
144
+ }
145
+ lastClickedNode = nodeId
146
+ lastClickTimestamp = now
147
+ }
148
+
149
+ // TODO: logDiagram is only called from logDiagramAction subscription, verify if this is actually needed.
150
+ function logDiagram() {
151
+ console.log('[FlowCanvas] Diagram log triggered', {
152
+ nodes,
153
+ edges,
154
+ graph: graph
155
+ });
156
+ }
157
+
158
+ function goHome() {
159
+ goto('/');
160
+ }
161
+
162
+ </script>
163
+
164
+ <div data-testid="flow-canvas"
165
+ class="flex h-full flex-auto flex-col overflow-hidden rounded-2xl border border-white/5 bg-black/20 shadow-xl">
166
+ {#if hasGraphData}
167
+
168
+ <SvelteFlow
169
+ bind:nodes
170
+ bind:edges
171
+ {nodeTypes}
172
+ {edgeTypes}
173
+ nodesDraggable
174
+ nodesConnectable={false}
175
+ panOnDrag
176
+ zoomOnScroll
177
+ selectionOnDrag={false}
178
+ colorMode={theme.isDark ? 'dark' : 'light'}
179
+ fitView
180
+ fitViewOptions={{ padding: 0.2 }}
181
+ >
182
+ <!-- Todo: See if we can use tailwind here-->
183
+ <Background bgColor={theme.isDark ? '#1f2937' : '#e5e7eb'} variant={BackgroundVariant.Dots} gap={24}/>
184
+ <MiniMap nodeColor={(n) => {
185
+ if (n.type === 'group') return '#2563eb'
186
+ if (n.type === 'service') return '#16a34a'
187
+ if (n.type === 'external') return '#d97706'
188
+ return '#9ca3af'
189
+ }} position="bottom-left"/>
190
+ <Controls position="top-left">
191
+ <!-- Go home button -->
192
+ <ControlButton onclick={goHome} title="Go Home" aria-label="Go to home page">
193
+ <Icon icon="mdi:home"/>
194
+ </ControlButton>
195
+ </Controls>
196
+ <Panel position="bottom-right">
197
+ {#if $showLegend && !showOverlay}
198
+ <Legend/>
199
+ {/if}
200
+ </Panel>
201
+ </SvelteFlow>
202
+
203
+
204
+ {:else}
205
+ <div class="flex h-full flex-col items-center justify-center gap-2 text-sm text-gray-400 dark:text-gray-300">
206
+ <span class="text-3xl" aria-hidden="true">🌀</span>
207
+ <span>{emptyStateLabel}</span>
208
+ </div>
209
+ {/if}
210
+
211
+ {#if showOverlay}
212
+ <div
213
+ data-testid="flow-loading-overlay"
214
+ class="pointer-events-none absolute inset-0 flex items-center justify-center bg-white/80 text-sm text-gray-700 dark:bg-black/70 dark:text-gray-200 backdrop-blur"
215
+ >
216
+ Rendering diagram…
217
+ </div>
218
+ {/if}
219
+
220
+ {#if layoutError}
221
+ <div class="border-t border-red-200 bg-red-100 px-4 py-2 text-sm text-red-700 dark:text-red-200 dark:bg-red-500/20 dark:border-red-500/40">{layoutError}</div>
222
+ {/if}
223
+ </div>
@@ -0,0 +1,44 @@
1
+ <script lang="ts">
2
+
3
+ import type {Snippet} from "svelte";
4
+
5
+ const {title, subtitle, description, children, iconPath, iconAlt, classes} = $props<{
6
+ title: string,
7
+ subtitle?: string,
8
+ description?: string,
9
+ children?: Snippet,
10
+ iconPath?: string,
11
+ iconAlt?: string,
12
+ classes?: string
13
+ }>();
14
+
15
+
16
+ </script>
17
+
18
+ <div class="sidebar-card {classes}">
19
+ <div class="flex items-start justify-between">
20
+ <div class="flex flex-col">
21
+ <div class="sidebar-card-title">{title}</div>
22
+ {#if subtitle}
23
+ <h3 class="sidebar-card-subtitle">{subtitle}</h3>
24
+ {/if}
25
+ </div>
26
+ {#if iconPath}
27
+ <img
28
+ src={iconPath}
29
+ alt={iconAlt}
30
+ class="h-10 w-10 rounded-lg bg-white/5 object-contain"
31
+ />
32
+ {:else if iconAlt}
33
+ <div class="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-800 text-xs font-semibold text-gray-200">
34
+ {iconAlt}
35
+ </div>
36
+ {/if}
37
+ </div>
38
+ {#if description}
39
+ <p class="sidebar-card-contents">{description}</p>
40
+ {/if}
41
+ {#if children}
42
+ {@render children()}
43
+ {/if}
44
+ </div>
@@ -0,0 +1,31 @@
1
+ <script lang="ts">
2
+ import type {GroupInfo} from '$shared/types'
3
+ import {TeamContactCard, GenericSidebarCard} from '$lib/components'
4
+ import {useNodes} from '@xyflow/svelte';
5
+
6
+ const {groupMap, groups} = $props<{
7
+ groupMap: Record<string, string>;
8
+ groups: Record<string, GroupInfo>;
9
+ }>()
10
+
11
+ const nodes = useNodes();
12
+ let selectedNodeId = $derived(nodes.current.find(n => n.selected)?.id ?? null);
13
+
14
+ let groupId = $derived(selectedNodeId ? (groupMap[selectedNodeId] ?? null) : null);
15
+ let groupInfo = $derived(groupId ? (groups[groupId] ?? null) : null);
16
+ </script>
17
+
18
+ <aside data-testid="group-sidebar"
19
+ class="flex h-full flex-col basis-1/6 border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 text-gray-900 dark:text-gray-100 overflow-hidden rounded-2xl border shadow-xl p-3">
20
+ {#if !groupInfo}
21
+ <GenericSidebarCard title="No Group Selected" description="Select a group to view details." />
22
+ {:else}
23
+ <GenericSidebarCard title="Group" subtitle={groupInfo.name} description={groupInfo.description}/>
24
+
25
+ {#if groupInfo.teamId}
26
+ <div class="mt-4">
27
+ <TeamContactCard teamId={groupInfo.teamId}/>
28
+ </div>
29
+ {/if}
30
+ {/if}
31
+ </aside>
@@ -0,0 +1,57 @@
1
+ <script lang="ts">
2
+ import { goto } from '$app/navigation';
3
+ import { selectedGroup, showLegend, logDiagramAction } from '$lib/stores/diagram';
4
+ import { theme } from '$lib/state/theme.svelte';
5
+ import Icon from '@iconify/svelte';
6
+
7
+ function goHome() {
8
+ goto('/');
9
+ }
10
+
11
+ function toggleLegend() {
12
+ showLegend.update((v: boolean) => !v);
13
+ }
14
+
15
+ function toggleDarkMode() {
16
+ theme.toggle();
17
+ }
18
+
19
+ function logDiagram() {
20
+ logDiagramAction.update(n => n + 1);
21
+ }
22
+ </script>
23
+
24
+ <header class="border-b border-gray-200 bg-gray-200 dark:bg-black dark:border-white/10">
25
+ <div class="flex w-full items-center justify-between px-8 py-4">
26
+ <button class="cursor-pointer select-none text-left" on:click={goHome} aria-label="Go to home page">
27
+ <span class="block text-xs big-title">Resource Mapper</span>
28
+ <span class="block text-2xl font-semibold text-gray-900 dark:text-gray-100">Architecture Atlas</span>
29
+ </button>
30
+ <div class="flex-1 flex items-center justify-center big-title text-xl">
31
+ {#if $selectedGroup}
32
+ {$selectedGroup.name}
33
+ {:else}
34
+ Overview
35
+ {/if}
36
+ </div>
37
+ <nav class="flex items-center gap-4">
38
+ <span class="text-gray-700 dark:text-gray-200" on:click={toggleLegend} role="button" tabindex="0" on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && toggleLegend()} title={$showLegend ? 'Hide Legend' : 'Show Legend'}>
39
+ {#if $showLegend}
40
+ <Icon icon="mdi:eye-off-outline" width="24" height="24" />
41
+ {:else}
42
+ <Icon icon="mdi:eye-outline" width="24" height="24" />
43
+ {/if}
44
+ </span>
45
+ <span class="text-gray-700 dark:text-gray-200" on:click={toggleDarkMode} role="button" tabindex="0" on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && toggleDarkMode()} title={theme.isDark ? 'Light Mode' : 'Dark Mode'}>
46
+ {#if theme.isDark}
47
+ <Icon icon="mdi:weather-sunny" width="24" height="24" />
48
+ {:else}
49
+ <Icon icon="mdi:weather-night" width="24" height="24" />
50
+ {/if}
51
+ </span>
52
+ <span class="text-gray-700 dark:text-gray-200" on:click={logDiagram} role="button" tabindex="0" on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && logDiagram()} title="Log Diagram">
53
+ <Icon icon="mdi:bug-outline" width="24" height="24" />
54
+ </span>
55
+ </nav>
56
+ </div>
57
+ </header>
@@ -0,0 +1,25 @@
1
+ <div class="rounded-2xl border border-gray-200 dark:border-gray-700 bg-gray-100 dark:bg-slate-800 p-4 text-sm text-gray-700 dark:text-gray-200 shadow-lg">
2
+ <h3 class="mb-3 text-sm font-semibold text-gray-900 dark:text-gray-300">Legend</h3>
3
+ <ul class="space-y-2">
4
+ <li class="flex items-center gap-2">
5
+ <span class="h-3 w-3 rounded bg-blue-500/80 dark:bg-blue-700"></span>
6
+ <span>Internal group</span>
7
+ </li>
8
+ <li class="flex items-center gap-2">
9
+ <span class="h-3 w-3 rounded bg-amber-400/80 dark:bg-amber-600"></span>
10
+ <span>External group</span>
11
+ </li>
12
+ <li class="flex items-center gap-2">
13
+ <span class="h-1.5 w-6 rounded bg-blue-400 dark:bg-blue-600"></span>
14
+ <span>Internal connection</span>
15
+ </li>
16
+ <li class="flex items-center gap-2">
17
+ <span class="h-1.5 w-6 rounded bg-green-400 dark:bg-green-600"></span>
18
+ <span>Incoming connection</span>
19
+ </li>
20
+ <li class="flex items-center gap-2">
21
+ <span class="h-1.5 w-6 rounded bg-sky-400 dark:bg-sky-600"></span>
22
+ <span>Outgoing connection</span>
23
+ </li>
24
+ </ul>
25
+ </div>
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import { fade } from 'svelte/transition';
3
+
4
+ let { message = 'Loading...', progress = null } = $props<{ message?: string, progress?: number | null }>()
5
+
6
+ // Auto-progress simulation for indeterminate state
7
+ let simulatedProgress = $state(0);
8
+ $effect(() => {
9
+ if (progress !== null) return;
10
+ const interval = setInterval(() => {
11
+ simulatedProgress = (simulatedProgress + 1) % 100;
12
+ }, 30);
13
+ return () => clearInterval(interval);
14
+ });
15
+ </script>
16
+
17
+ <div class="fixed inset-0 z-[100] flex flex-col items-center justify-center bg-white/40 dark:bg-slate-900/60 backdrop-blur-md" transition:fade={{ duration: 250 }}>
18
+ <!-- Top progress bar -->
19
+ <div class="fixed top-0 left-0 right-0 h-1 bg-gray-200/20 dark:bg-white/5 overflow-hidden">
20
+ <div
21
+ class="h-full bg-blue-500 shadow-[0_0_8px_rgba(59,130,246,0.5)] transition-all duration-300 ease-out"
22
+ style="width: {progress !== null ? progress : simulatedProgress}%"
23
+ ></div>
24
+ </div>
25
+
26
+ <div class="flex flex-col items-center">
27
+ <div class="h-16 w-16 animate-spin rounded-full border-4 border-gray-200/50 border-t-blue-500 dark:border-white/10 dark:border-t-blue-400"></div>
28
+ <p class="mt-6 text-lg font-medium text-gray-800 dark:text-gray-100 tracking-wide">{message}</p>
29
+ </div>
30
+ </div>
31
+
32
+ <style>
33
+ @keyframes progress-indeterminate {
34
+ 0% { transform: translateX(-100%) scaleX(0.2); }
35
+ 50% { transform: translateX(0) scaleX(0.5); }
36
+ 100% { transform: translateX(100%) scaleX(0.2); }
37
+ }
38
+
39
+ .animate-progress-indeterminate {
40
+ animation: progress-indeterminate 1.5s infinite linear;
41
+ }
42
+ </style>
@@ -0,0 +1,10 @@
1
+ <script lang="ts">
2
+ let { message = 'Loading...' } = $props<{ message?: string }>()
3
+ </script>
4
+
5
+ <div class="flex h-full w-full items-center justify-center p-8 text-center">
6
+ <div>
7
+ <div class="mx-auto h-16 w-16 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500 dark:border-gray-700 dark:border-t-blue-400"></div>
8
+ <p class="mt-4 text-base text-gray-500 dark:text-gray-400">{message}</p>
9
+ </div>
10
+ </div>