diff --git a/apps/servicebus-browser-frontend/src/app/app.component.scss b/apps/servicebus-browser-frontend/src/app/app.component.scss
index 6b64db8..a51cc33 100644
--- a/apps/servicebus-browser-frontend/src/app/app.component.scss
+++ b/apps/servicebus-browser-frontend/src/app/app.component.scss
@@ -22,6 +22,11 @@
}
}
+ .sidebar {
+ width: 100%;
+ height: 100%;
+ }
+
.main-content {
width: 100%;
height: 100%;
diff --git a/apps/servicebus-browser-frontend/src/app/app.component.ts b/apps/servicebus-browser-frontend/src/app/app.component.ts
index b34b4cb..bc5635c 100644
--- a/apps/servicebus-browser-frontend/src/app/app.component.ts
+++ b/apps/servicebus-browser-frontend/src/app/app.component.ts
@@ -8,6 +8,9 @@ import { Tab, TabList, Tabs } from 'primeng/tabs';
import { LogsListComponent } from '@service-bus-browser/logs-components';
import { Store } from '@ngrx/store';
import { LogsSelectors } from '@service-bus-browser/logs-store';
+import { TopologySelectors } from '@service-bus-browser/topology-store';
+import { TopologyTreeComponent } from '@service-bus-browser/topology-components';
+import { Namespace, Queue, Topic, Subscription } from '@service-bus-browser/topology-contracts';
@Component({
imports: [
@@ -20,6 +23,7 @@ import { LogsSelectors } from '@service-bus-browser/logs-store';
Tabs,
TabList,
Tab,
+ TopologyTreeComponent,
],
selector: 'app-root',
templateUrl: './app.component.html',
@@ -29,40 +33,32 @@ export class AppComponent {
title = 'servicebus-browser-frontend';
items = [
{
- label: 'Home',
- icon: 'pi pi-home',
- },
- {
- label: 'Projects',
- icon: 'pi pi-search',
- items: [
- {
- label: 'Core',
- icon: 'pi pi-bolt',
- shortcut: '⌘+S',
- },
- {
- label: 'Blocks',
- icon: 'pi pi-server',
- shortcut: '⌘+B',
- },
- {
- separator: true,
- },
- {
- label: 'UI Kit',
- icon: 'pi pi-pencil',
- shortcut: '⌘+U',
- },
- ],
- },
+ label: 'Connections',
+ }
];
store = inject(Store);
logsOpened = signal(false);
logs = this.store.selectSignal(LogsSelectors.selectLogs);
+ namespaces = this.store.selectSignal(TopologySelectors.selectNamespaces);
toggleLogs() {
this.logsOpened.update((value) => !value);
}
+
+ onNamespaceSelected($event: { namespace: Namespace }) {
+ console.log($event);
+ }
+
+ onQueueSelected($event: { namespaceId: string; queue: Queue }) {
+ console.log($event);
+ }
+
+ onTopicSelected($event: { namespaceId: string; topic: Topic }) {
+ console.log($event);
+ }
+
+ onSubscriptionSelected($event: { namespaceId: string; topicId: string, subscription: Subscription }) {
+ console.log($event);
+ }
}
diff --git a/apps/servicebus-browser-frontend/src/app/app.config.ts b/apps/servicebus-browser-frontend/src/app/app.config.ts
index 51d0b88..b91ae7f 100644
--- a/apps/servicebus-browser-frontend/src/app/app.config.ts
+++ b/apps/servicebus-browser-frontend/src/app/app.config.ts
@@ -7,6 +7,7 @@ import { theme } from './theme';
import { provideLogsState } from '@service-bus-browser/logs-store';
import { provideStore } from '@ngrx/store';
import { provideStoreDevtools } from '@ngrx/store-devtools';
+import { provideTopologyState } from '@service-bus-browser/topology-store';
export const appConfig: ApplicationConfig = {
providers: [
@@ -25,6 +26,7 @@ export const appConfig: ApplicationConfig = {
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(appRoutes, withHashLocation()),
provideLogsState(),
+ provideTopologyState(),
// ngrx
provideStore(),
diff --git a/libs/logs/components/src/lib/log-line/log-line.component.scss b/libs/logs/components/src/lib/log-line/log-line.component.scss
index 361da89..669cd08 100644
--- a/libs/logs/components/src/lib/log-line/log-line.component.scss
+++ b/libs/logs/components/src/lib/log-line/log-line.component.scss
@@ -1,7 +1,8 @@
pre {
--color: 'inherit';
margin: 0;
- font-size: 12px;
+ font-size: 14px;
+ line-height: 16px;
padding: 4px 1rem;
color: var(--color);
}
diff --git a/libs/logs/components/src/lib/logs-list/logs-list.component.html b/libs/logs/components/src/lib/logs-list/logs-list.component.html
index 1888a12..00a7067 100644
--- a/libs/logs/components/src/lib/logs-list/logs-list.component.html
+++ b/libs/logs/components/src/lib/logs-list/logs-list.component.html
@@ -1,6 +1,6 @@
diff --git a/libs/logs/components/src/lib/logs-list/logs-list.component.scss b/libs/logs/components/src/lib/logs-list/logs-list.component.scss
index e5065b5..de0a876 100644
--- a/libs/logs/components/src/lib/logs-list/logs-list.component.scss
+++ b/libs/logs/components/src/lib/logs-list/logs-list.component.scss
@@ -1,6 +1,5 @@
sbb-logs-log-line {
display: block;
- height: 20px;
&.odd {
background: var(--p-surface-100);
diff --git a/libs/logs/store/src/lib/logs.store.ts b/libs/logs/store/src/lib/logs.store.ts
index f345947..93f3036 100644
--- a/libs/logs/store/src/lib/logs.store.ts
+++ b/libs/logs/store/src/lib/logs.store.ts
@@ -9,7 +9,7 @@ export type LogsState = {
}
export const initialState: LogsState = {
- logs: Array.from({ length: 1000 }, (_, index) => {
+ logs: Array.from({ length: 100000 }, (_, index) => {
const severity = Math.random();
let severityString: LogLineSeverity = 'verbose';
if (severity > 0.2) {
diff --git a/libs/topology/components/README.md b/libs/topology/components/README.md
new file mode 100644
index 0000000..f306867
--- /dev/null
+++ b/libs/topology/components/README.md
@@ -0,0 +1,7 @@
+# @service-bus-browser/topology-components
+
+This library was generated with [Nx](https://nx.dev).
+
+## Running unit tests
+
+Run `nx test @service-bus-browser/topology-components` to execute the unit tests.
diff --git a/libs/topology/components/eslint.config.js b/libs/topology/components/eslint.config.js
new file mode 100644
index 0000000..3a3ff72
--- /dev/null
+++ b/libs/topology/components/eslint.config.js
@@ -0,0 +1,34 @@
+const nx = require('@nx/eslint-plugin');
+const baseConfig = require('../../../eslint.config.js');
+
+module.exports = [
+ ...baseConfig,
+ ...nx.configs['flat/angular'],
+ ...nx.configs['flat/angular-template'],
+ {
+ files: ['**/*.ts'],
+ rules: {
+ '@angular-eslint/directive-selector': [
+ 'error',
+ {
+ type: 'attribute',
+ prefix: 'sbb-tpl',
+ style: 'camelCase',
+ },
+ ],
+ '@angular-eslint/component-selector': [
+ 'error',
+ {
+ type: 'element',
+ prefix: 'sbb-tpl',
+ style: 'kebab-case',
+ },
+ ],
+ },
+ },
+ {
+ files: ['**/*.html'],
+ // Override or add rules here
+ rules: {},
+ },
+];
diff --git a/libs/topology/components/jest.config.ts b/libs/topology/components/jest.config.ts
new file mode 100644
index 0000000..244d2b8
--- /dev/null
+++ b/libs/topology/components/jest.config.ts
@@ -0,0 +1,21 @@
+export default {
+ displayName: '@service-bus-browser/topology-components',
+ preset: '../../../jest.preset.js',
+ setupFilesAfterEnv: ['/src/test-setup.ts'],
+ coverageDirectory: '../../../coverage/libs/topology/components',
+ transform: {
+ '^.+\\.(ts|mjs|js|html)$': [
+ 'jest-preset-angular',
+ {
+ tsconfig: '/tsconfig.spec.json',
+ stringifyContentPathRegex: '\\.(html|svg)$',
+ },
+ ],
+ },
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
+ snapshotSerializers: [
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
+ 'jest-preset-angular/build/serializers/ng-snapshot',
+ 'jest-preset-angular/build/serializers/html-comment',
+ ],
+};
diff --git a/libs/topology/components/project.json b/libs/topology/components/project.json
new file mode 100644
index 0000000..4ba50d2
--- /dev/null
+++ b/libs/topology/components/project.json
@@ -0,0 +1,20 @@
+{
+ "name": "@service-bus-browser/topology-components",
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "libs/topology/components/src",
+ "prefix": "sbb-tpl",
+ "projectType": "library",
+ "tags": [],
+ "targets": {
+ "test": {
+ "executor": "@nx/jest:jest",
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
+ "options": {
+ "jestConfig": "libs/topology/components/jest.config.ts"
+ }
+ },
+ "lint": {
+ "executor": "@nx/eslint:lint"
+ }
+ }
+}
diff --git a/libs/topology/components/src/index.ts b/libs/topology/components/src/index.ts
new file mode 100644
index 0000000..0582421
--- /dev/null
+++ b/libs/topology/components/src/index.ts
@@ -0,0 +1 @@
+export * from './lib/topology-tree/topology-tree.component';
diff --git a/libs/topology/components/src/lib/namespace-tree-node/namespace-tree-node.component.html b/libs/topology/components/src/lib/namespace-tree-node/namespace-tree-node.component.html
new file mode 100644
index 0000000..83d65e0
--- /dev/null
+++ b/libs/topology/components/src/lib/namespace-tree-node/namespace-tree-node.component.html
@@ -0,0 +1 @@
+ {{ namespace().name }}
diff --git a/libs/topology/components/src/lib/namespace-tree-node/namespace-tree-node.component.scss b/libs/topology/components/src/lib/namespace-tree-node/namespace-tree-node.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/libs/topology/components/src/lib/namespace-tree-node/namespace-tree-node.component.ts b/libs/topology/components/src/lib/namespace-tree-node/namespace-tree-node.component.ts
new file mode 100644
index 0000000..27798fb
--- /dev/null
+++ b/libs/topology/components/src/lib/namespace-tree-node/namespace-tree-node.component.ts
@@ -0,0 +1,16 @@
+import { Component, input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Namespace } from '@service-bus-browser/topology-contracts';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { faServer } from '@fortawesome/free-solid-svg-icons';
+
+@Component({
+ selector: 'sbb-tpl-namespace-tree-node',
+ imports: [CommonModule, FaIconComponent],
+ templateUrl: './namespace-tree-node.component.html',
+ styleUrl: './namespace-tree-node.component.scss',
+})
+export class NamespaceTreeNodeComponent {
+ namespace = input.required();
+ icon = faServer;
+}
diff --git a/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.html b/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.html
new file mode 100644
index 0000000..1d1342a
--- /dev/null
+++ b/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.html
@@ -0,0 +1,2 @@
+ {{ queue().name }}
+({{queue().messageCount}},{{queue().deadLetterMessageCount}},{{queue().transferDeadLetterMessageCount}})
diff --git a/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.scss b/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.scss
new file mode 100644
index 0000000..626f585
--- /dev/null
+++ b/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.scss
@@ -0,0 +1,6 @@
+:host {
+ display: flex;
+ width: 100%;
+ flex-direction: row;
+ justify-content: space-between;
+}
diff --git a/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.ts b/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.ts
new file mode 100644
index 0000000..105ba3c
--- /dev/null
+++ b/libs/topology/components/src/lib/queue-tree-node/queue-tree-node.component.ts
@@ -0,0 +1,16 @@
+import { Component, input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Queue } from '@service-bus-browser/topology-contracts';
+import { faFolder } from '@fortawesome/free-solid-svg-icons';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+
+@Component({
+ selector: 'sbb-tpl-queue-tree-node',
+ imports: [CommonModule, FaIconComponent],
+ templateUrl: './queue-tree-node.component.html',
+ styleUrl: './queue-tree-node.component.scss',
+})
+export class QueueTreeNodeComponent {
+ queue = input.required();
+ icon = faFolder;
+}
diff --git a/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.html b/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.html
new file mode 100644
index 0000000..33df356
--- /dev/null
+++ b/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.html
@@ -0,0 +1,2 @@
+ {{ subscription().name }}
+({{subscription().messageCount}},{{subscription().deadLetterMessageCount}},{{subscription().transferDeadLetterMessageCount}})
diff --git a/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.scss b/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.scss
new file mode 100644
index 0000000..626f585
--- /dev/null
+++ b/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.scss
@@ -0,0 +1,6 @@
+:host {
+ display: flex;
+ width: 100%;
+ flex-direction: row;
+ justify-content: space-between;
+}
diff --git a/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.ts b/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.ts
new file mode 100644
index 0000000..525052f
--- /dev/null
+++ b/libs/topology/components/src/lib/subscription-tree-node/subscription-tree-node.component.ts
@@ -0,0 +1,16 @@
+import { Component, input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Subscription } from '@service-bus-browser/topology-contracts';
+import { faFolder } from '@fortawesome/free-regular-svg-icons';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+
+@Component({
+ selector: 'sbb-tpl-subscription-tree-node',
+ imports: [CommonModule, FaIconComponent],
+ templateUrl: './subscription-tree-node.component.html',
+ styleUrl: './subscription-tree-node.component.scss',
+})
+export class SubscriptionTreeNodeComponent {
+ subscription = input.required();
+ icon = faFolder;
+}
diff --git a/libs/topology/components/src/lib/topic-tree-node/topic-tree-node.component.html b/libs/topology/components/src/lib/topic-tree-node/topic-tree-node.component.html
new file mode 100644
index 0000000..c3e3d59
--- /dev/null
+++ b/libs/topology/components/src/lib/topic-tree-node/topic-tree-node.component.html
@@ -0,0 +1 @@
+ {{ topic().name }}
diff --git a/libs/topology/components/src/lib/topic-tree-node/topic-tree-node.component.scss b/libs/topology/components/src/lib/topic-tree-node/topic-tree-node.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/libs/topology/components/src/lib/topic-tree-node/topic-tree-node.component.ts b/libs/topology/components/src/lib/topic-tree-node/topic-tree-node.component.ts
new file mode 100644
index 0000000..601623d
--- /dev/null
+++ b/libs/topology/components/src/lib/topic-tree-node/topic-tree-node.component.ts
@@ -0,0 +1,16 @@
+import { Component, input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Topic } from '@service-bus-browser/topology-contracts';
+import { faFolderTree } from '@fortawesome/free-solid-svg-icons';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+
+@Component({
+ selector: 'sbb-tpl-topic-tree-node',
+ imports: [CommonModule, FaIconComponent],
+ templateUrl: './topic-tree-node.component.html',
+ styleUrl: './topic-tree-node.component.scss',
+})
+export class TopicTreeNodeComponent {
+ topic = input.required();
+ icon = faFolderTree;
+}
diff --git a/libs/topology/components/src/lib/topology-tree/topology-tree.component.html b/libs/topology/components/src/lib/topology-tree/topology-tree.component.html
new file mode 100644
index 0000000..44856c5
--- /dev/null
+++ b/libs/topology/components/src/lib/topology-tree/topology-tree.component.html
@@ -0,0 +1,17 @@
+
+
+ {{ node.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libs/topology/components/src/lib/topology-tree/topology-tree.component.scss b/libs/topology/components/src/lib/topology-tree/topology-tree.component.scss
new file mode 100644
index 0000000..92331e0
--- /dev/null
+++ b/libs/topology/components/src/lib/topology-tree/topology-tree.component.scss
@@ -0,0 +1,22 @@
+:host {
+ ::ng-deep .p-tree-node-label {
+ width: 100%;
+ }
+
+ ::ng-deep .p-tree-node-leaf .p-tree-node-toggle-button {
+ display: none;
+ }
+
+ ::ng-deep .p-tree-node-leaf {
+ line-height: 28px;
+ .p-tree-node-content {
+ padding: 0 0.4rem;
+ }
+ }
+}
+
+p-tree {
+ --p-tree-padding: 0rem;
+ --p-tree-node-padding: 0;
+ --p-tree-indent: 28px;
+}
diff --git a/libs/topology/components/src/lib/topology-tree/topology-tree.component.ts b/libs/topology/components/src/lib/topology-tree/topology-tree.component.ts
new file mode 100644
index 0000000..86f6276
--- /dev/null
+++ b/libs/topology/components/src/lib/topology-tree/topology-tree.component.ts
@@ -0,0 +1,157 @@
+import { Component, computed, input, output } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import {
+ Namespace,
+ NamespaceWithChildren,
+ Queue,
+ Subscription,
+ TopicWithChildren
+} from '@service-bus-browser/topology-contracts';
+import { Tree } from 'primeng/tree';
+import { PrimeTemplate, TreeNode } from 'primeng/api';
+import { NamespaceTreeNodeComponent } from '../namespace-tree-node/namespace-tree-node.component';
+import { TopicTreeNodeComponent } from '../topic-tree-node/topic-tree-node.component';
+import { SubscriptionTreeNodeComponent } from '../subscription-tree-node/subscription-tree-node.component';
+import { QueueTreeNodeComponent } from '../queue-tree-node/queue-tree-node.component';
+
+@Component({
+ selector: 'sbb-tpl-topology-tree',
+ imports: [
+ CommonModule,
+ Tree,
+ PrimeTemplate,
+ NamespaceTreeNodeComponent,
+ TopicTreeNodeComponent,
+ SubscriptionTreeNodeComponent,
+ QueueTreeNodeComponent,
+ ],
+ templateUrl: './topology-tree.component.html',
+ styleUrl: './topology-tree.component.scss',
+})
+export class TopologyTreeComponent {
+ namespaces =
+ input.required[]>();
+ nodes = computed(() =>
+ this.namespaces().map((ns, ns_index) => ({
+ key: ns_index.toString(),
+ label: ns.name,
+ type: 'namespace',
+ data: ns,
+ children: [
+ {
+ key: `${ns_index}-queues`,
+ label: 'Queues',
+ selectable: false,
+ children: ns.queues.map((queue, queue_index) => ({
+ key: `${ns_index}-queue-${queue_index}`,
+ label: queue.name,
+ type: 'queue',
+ data: {
+ namespace: ns,
+ queue,
+ },
+ leaf: true,
+ })),
+ },
+ {
+ key: `${ns_index}-topics`,
+ label: 'Topics',
+ selectable: false,
+ children: ns.topics.map((topic, topic_index) => ({
+ key: `${ns_index}-topic-${topic_index}`,
+ label: topic.name,
+ type: 'topic',
+ data: {
+ namespace: ns,
+ topic,
+ },
+ children: topic.subscriptions.map((sub, sub_index) => ({
+ key: `${ns_index}-topic-${topic_index}-subscription-${sub_index}`,
+ label: sub.name,
+ type: 'subscription',
+ data: {
+ namespace: ns,
+ topic,
+ subscription: sub,
+ },
+ leaf: true,
+ })),
+
+ })),
+ },
+ ],
+ }))
+ );
+
+ namespaceSelected = output<{
+ namespace: Namespace;
+ }>();
+ queueSelected = output<{
+ namespaceId: string;
+ queue: Queue;
+ }>();
+ topicSelected = output<{
+ namespaceId: string;
+ topic: TopicWithChildren;
+ }>();
+ subscriptionSelected = output<{
+ namespaceId: string;
+ topicId: string;
+ subscription: Subscription;
+ }>();
+
+
+ onSelectionChange(event: TreeNode | TreeNode[] | null) {
+ // should not be an array since we have selection mode single
+ if (!event || event instanceof Array) {
+ return;
+ }
+
+ switch (event.type) {
+ case 'namespace':
+ this.onNamespaceSelected(event.data);
+ break;
+ case 'queue':
+ this.onQueueSelected(event.data.namespace, event.data.queue);
+ break;
+ case 'topic':
+ this.onTopicSelected(event.data.namespace, event.data.topic);
+ break;
+ case 'subscription':
+ this.onSubscriptionSelected(
+ event.data.namespace,
+ event.data.topic,
+ event.data.subscription
+ );
+ break
+ }
+ }
+
+ private onNamespaceSelected(namespace: Namespace) {
+ this.namespaceSelected.emit({
+ namespace,
+ })
+ }
+
+ private onQueueSelected(namespace: Namespace, queue: Queue) {
+ this.queueSelected.emit({
+ namespaceId: namespace.id,
+ queue,
+ })
+ }
+
+ private onTopicSelected(namespace: Namespace, topic: TopicWithChildren) {
+ this.topicSelected.emit({
+ namespaceId: namespace.id,
+ topic,
+ })
+ }
+
+ private onSubscriptionSelected(namespace: Namespace, topic: TopicWithChildren, subscription: Subscription) {
+ this.subscriptionSelected.emit({
+ namespaceId: namespace.id,
+ topicId: topic.id,
+ subscription,
+ })
+ }
+}
diff --git a/libs/topology/components/src/test-setup.ts b/libs/topology/components/src/test-setup.ts
new file mode 100644
index 0000000..ea41401
--- /dev/null
+++ b/libs/topology/components/src/test-setup.ts
@@ -0,0 +1,6 @@
+import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
+
+setupZoneTestEnv({
+ errorOnUnknownElements: true,
+ errorOnUnknownProperties: true,
+});
diff --git a/libs/topology/components/tsconfig.json b/libs/topology/components/tsconfig.json
new file mode 100644
index 0000000..fde35ea
--- /dev/null
+++ b/libs/topology/components/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "target": "es2022",
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ },
+ {
+ "path": "./tsconfig.spec.json"
+ }
+ ],
+ "extends": "../../../tsconfig.base.json",
+ "angularCompilerOptions": {
+ "enableI18nLegacyMessageIdFormat": false,
+ "strictInjectionParameters": true,
+ "strictInputAccessModifiers": true,
+ "strictTemplates": true
+ }
+}
diff --git a/libs/topology/components/tsconfig.lib.json b/libs/topology/components/tsconfig.lib.json
new file mode 100644
index 0000000..9b49be7
--- /dev/null
+++ b/libs/topology/components/tsconfig.lib.json
@@ -0,0 +1,17 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "declaration": true,
+ "declarationMap": true,
+ "inlineSources": true,
+ "types": []
+ },
+ "exclude": [
+ "src/**/*.spec.ts",
+ "src/test-setup.ts",
+ "jest.config.ts",
+ "src/**/*.test.ts"
+ ],
+ "include": ["src/**/*.ts"]
+}
diff --git a/libs/topology/components/tsconfig.spec.json b/libs/topology/components/tsconfig.spec.json
new file mode 100644
index 0000000..f858ef7
--- /dev/null
+++ b/libs/topology/components/tsconfig.spec.json
@@ -0,0 +1,16 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "module": "commonjs",
+ "target": "es2016",
+ "types": ["jest", "node"]
+ },
+ "files": ["src/test-setup.ts"],
+ "include": [
+ "jest.config.ts",
+ "src/**/*.test.ts",
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/libs/topology/contracts/README.md b/libs/topology/contracts/README.md
new file mode 100644
index 0000000..fffb78c
--- /dev/null
+++ b/libs/topology/contracts/README.md
@@ -0,0 +1,11 @@
+# contracts
+
+This library was generated with [Nx](https://nx.dev).
+
+## Building
+
+Run `nx build contracts` to build the library.
+
+## Running unit tests
+
+Run `nx test contracts` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/topology/contracts/eslint.config.js b/libs/topology/contracts/eslint.config.js
new file mode 100644
index 0000000..ee950eb
--- /dev/null
+++ b/libs/topology/contracts/eslint.config.js
@@ -0,0 +1,19 @@
+const baseConfig = require('../../../eslint.config.js');
+
+module.exports = [
+ ...baseConfig,
+ {
+ files: ['**/*.json'],
+ rules: {
+ '@nx/dependency-checks': [
+ 'error',
+ {
+ ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
+ },
+ ],
+ },
+ languageOptions: {
+ parser: require('jsonc-eslint-parser'),
+ },
+ },
+];
diff --git a/libs/topology/contracts/jest.config.ts b/libs/topology/contracts/jest.config.ts
new file mode 100644
index 0000000..22bca9c
--- /dev/null
+++ b/libs/topology/contracts/jest.config.ts
@@ -0,0 +1,10 @@
+export default {
+ displayName: 'contracts',
+ preset: '../../../jest.preset.js',
+ testEnvironment: 'node',
+ transform: {
+ '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }],
+ },
+ moduleFileExtensions: ['ts', 'js', 'html'],
+ coverageDirectory: '../../../coverage/libs/topology/contracts',
+};
diff --git a/libs/topology/contracts/package.json b/libs/topology/contracts/package.json
new file mode 100644
index 0000000..5d0e9c4
--- /dev/null
+++ b/libs/topology/contracts/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "@service-bus-browser/topology-contracts",
+ "version": "0.0.1",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "type": "commonjs",
+ "main": "./src/index.js",
+ "typings": "./src/index.d.ts",
+ "private": true
+}
diff --git a/libs/topology/contracts/project.json b/libs/topology/contracts/project.json
new file mode 100644
index 0000000..9058f33
--- /dev/null
+++ b/libs/topology/contracts/project.json
@@ -0,0 +1,26 @@
+{
+ "name": "contracts",
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "libs/topology/contracts/src",
+ "projectType": "library",
+ "tags": [],
+ "targets": {
+ "build": {
+ "executor": "@nx/js:tsc",
+ "outputs": ["{options.outputPath}"],
+ "options": {
+ "outputPath": "dist/libs/topology/contracts",
+ "main": "libs/topology/contracts/src/index.ts",
+ "tsConfig": "libs/topology/contracts/tsconfig.lib.json",
+ "assets": ["libs/topology/contracts/*.md"]
+ }
+ },
+ "test": {
+ "executor": "@nx/jest:jest",
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
+ "options": {
+ "jestConfig": "libs/topology/contracts/jest.config.ts"
+ }
+ }
+ }
+}
diff --git a/libs/topology/contracts/src/index.ts b/libs/topology/contracts/src/index.ts
new file mode 100644
index 0000000..6102413
--- /dev/null
+++ b/libs/topology/contracts/src/index.ts
@@ -0,0 +1,4 @@
+export * from './lib/namespace';
+export * from './lib/subscription';
+export * from './lib/topic';
+export * from './lib/queue';
diff --git a/libs/topology/contracts/src/lib/namespace.ts b/libs/topology/contracts/src/lib/namespace.ts
new file mode 100644
index 0000000..f886482
--- /dev/null
+++ b/libs/topology/contracts/src/lib/namespace.ts
@@ -0,0 +1,15 @@
+import { Queue } from './queue';
+import { Topic } from './topic';
+
+export type Namespace = {
+ id: string;
+ name: string;
+}
+
+export type NamespaceWithChildren<
+ TQueue extends Queue = Queue,
+ TTopic extends Topic = Topic
+> = Namespace & {
+ topics: TTopic[];
+ queues: TQueue[];
+}
diff --git a/libs/topology/contracts/src/lib/queue.ts b/libs/topology/contracts/src/lib/queue.ts
new file mode 100644
index 0000000..d185615
--- /dev/null
+++ b/libs/topology/contracts/src/lib/queue.ts
@@ -0,0 +1,7 @@
+export type Queue = {
+ id: string;
+ name: string;
+ messageCount: number;
+ deadLetterMessageCount: number;
+ transferDeadLetterMessageCount: number;
+}
diff --git a/libs/topology/contracts/src/lib/subscription.ts b/libs/topology/contracts/src/lib/subscription.ts
new file mode 100644
index 0000000..7dfe181
--- /dev/null
+++ b/libs/topology/contracts/src/lib/subscription.ts
@@ -0,0 +1,7 @@
+export type Subscription = {
+ id: string;
+ name: string;
+ messageCount: number;
+ deadLetterMessageCount: number;
+ transferDeadLetterMessageCount: number;
+}
diff --git a/libs/topology/contracts/src/lib/topic.ts b/libs/topology/contracts/src/lib/topic.ts
new file mode 100644
index 0000000..b47eed0
--- /dev/null
+++ b/libs/topology/contracts/src/lib/topic.ts
@@ -0,0 +1,10 @@
+import { Subscription } from './subscription';
+
+export type Topic = {
+ id: string;
+ name: string;
+}
+
+export type TopicWithChildren = Topic & {
+ subscriptions: TSubscription[];
+}
diff --git a/libs/topology/contracts/tsconfig.json b/libs/topology/contracts/tsconfig.json
new file mode 100644
index 0000000..0dc79ca
--- /dev/null
+++ b/libs/topology/contracts/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "compilerOptions": {
+ "module": "commonjs",
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "noPropertyAccessFromIndexSignature": true
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ },
+ {
+ "path": "./tsconfig.spec.json"
+ }
+ ]
+}
diff --git a/libs/topology/contracts/tsconfig.lib.json b/libs/topology/contracts/tsconfig.lib.json
new file mode 100644
index 0000000..4befa7f
--- /dev/null
+++ b/libs/topology/contracts/tsconfig.lib.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "declaration": true,
+ "types": ["node"]
+ },
+ "include": ["src/**/*.ts"],
+ "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
+}
diff --git a/libs/topology/contracts/tsconfig.spec.json b/libs/topology/contracts/tsconfig.spec.json
new file mode 100644
index 0000000..ab55b7c
--- /dev/null
+++ b/libs/topology/contracts/tsconfig.spec.json
@@ -0,0 +1,15 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "module": "commonjs",
+ "moduleResolution": "node10",
+ "types": ["jest", "node"]
+ },
+ "include": [
+ "jest.config.ts",
+ "src/**/*.test.ts",
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/libs/topology/store/README.md b/libs/topology/store/README.md
new file mode 100644
index 0000000..bc35631
--- /dev/null
+++ b/libs/topology/store/README.md
@@ -0,0 +1,7 @@
+# @service-bus-browser/topology-store
+
+This library was generated with [Nx](https://nx.dev).
+
+## Running unit tests
+
+Run `nx test @service-bus-browser/topology-store` to execute the unit tests.
diff --git a/libs/topology/store/eslint.config.js b/libs/topology/store/eslint.config.js
new file mode 100644
index 0000000..d36ea77
--- /dev/null
+++ b/libs/topology/store/eslint.config.js
@@ -0,0 +1,34 @@
+const nx = require('@nx/eslint-plugin');
+const baseConfig = require('../../../eslint.config.js');
+
+module.exports = [
+ ...baseConfig,
+ ...nx.configs['flat/angular'],
+ ...nx.configs['flat/angular-template'],
+ {
+ files: ['**/*.ts'],
+ rules: {
+ '@angular-eslint/directive-selector': [
+ 'error',
+ {
+ type: 'attribute',
+ prefix: 'lib',
+ style: 'camelCase',
+ },
+ ],
+ '@angular-eslint/component-selector': [
+ 'error',
+ {
+ type: 'element',
+ prefix: 'lib',
+ style: 'kebab-case',
+ },
+ ],
+ },
+ },
+ {
+ files: ['**/*.html'],
+ // Override or add rules here
+ rules: {},
+ },
+];
diff --git a/libs/topology/store/jest.config.ts b/libs/topology/store/jest.config.ts
new file mode 100644
index 0000000..67a57a7
--- /dev/null
+++ b/libs/topology/store/jest.config.ts
@@ -0,0 +1,21 @@
+export default {
+ displayName: '@service-bus-browser/topology-store',
+ preset: '../../../jest.preset.js',
+ setupFilesAfterEnv: ['/src/test-setup.ts'],
+ coverageDirectory: '../../../coverage/libs/topology/store',
+ transform: {
+ '^.+\\.(ts|mjs|js|html)$': [
+ 'jest-preset-angular',
+ {
+ tsconfig: '/tsconfig.spec.json',
+ stringifyContentPathRegex: '\\.(html|svg)$',
+ },
+ ],
+ },
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
+ snapshotSerializers: [
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
+ 'jest-preset-angular/build/serializers/ng-snapshot',
+ 'jest-preset-angular/build/serializers/html-comment',
+ ],
+};
diff --git a/libs/topology/store/project.json b/libs/topology/store/project.json
new file mode 100644
index 0000000..1086627
--- /dev/null
+++ b/libs/topology/store/project.json
@@ -0,0 +1,20 @@
+{
+ "name": "@service-bus-browser/topology-store",
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "libs/topology/store/src",
+ "prefix": "lib",
+ "projectType": "library",
+ "tags": [],
+ "targets": {
+ "test": {
+ "executor": "@nx/jest:jest",
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
+ "options": {
+ "jestConfig": "libs/topology/store/jest.config.ts"
+ }
+ },
+ "lint": {
+ "executor": "@nx/eslint:lint"
+ }
+ }
+}
diff --git a/libs/topology/store/src/index.ts b/libs/topology/store/src/index.ts
new file mode 100644
index 0000000..d6a6797
--- /dev/null
+++ b/libs/topology/store/src/index.ts
@@ -0,0 +1,14 @@
+import { EnvironmentProviders, Provider } from '@angular/core';
+import { provideState } from '@ngrx/store';
+import { topologyFeature } from './lib/topology.store';
+
+export * as TopologySelectors from './lib/topology.selectors'
+
+export function provideTopologyState(): (
+ | Provider
+ | EnvironmentProviders
+ )[] {
+ return [
+ provideState(topologyFeature),
+ ];
+}
diff --git a/libs/topology/store/src/lib/topology.selectors.ts b/libs/topology/store/src/lib/topology.selectors.ts
new file mode 100644
index 0000000..e04d89e
--- /dev/null
+++ b/libs/topology/store/src/lib/topology.selectors.ts
@@ -0,0 +1,17 @@
+import { featureKey, TopologyState } from './topology.store';
+import { createFeatureSelector, createSelector } from '@ngrx/store';
+import { NamespaceWithChildren, Queue, TopicWithChildren } from '@service-bus-browser/topology-contracts';
+
+const featureSelector = createFeatureSelector(featureKey);
+
+export const selectNamespaces = createSelector(
+ featureSelector,
+ (state) => state.namespaces.map>((ns) => ({
+ ...ns,
+ queues: state.queuesPerNamespace[ns.id] ?? [],
+ topics: (state.topicsPerNamespace[ns.id] ?? []).map((topic) => ({
+ ...topic,
+ subscriptions: state.subscriptionsPerNamespaceAndTopic[ns.id]?.[topic.id] ?? [],
+ })),
+ }))
+);
diff --git a/libs/topology/store/src/lib/topology.store.ts b/libs/topology/store/src/lib/topology.store.ts
new file mode 100644
index 0000000..e2e6cbf
--- /dev/null
+++ b/libs/topology/store/src/lib/topology.store.ts
@@ -0,0 +1,59 @@
+import { createFeature, createReducer, on } from '@ngrx/store';
+import { Namespace, Queue, Subscription, Topic } from '@service-bus-browser/topology-contracts';
+
+export const featureKey = 'topology';
+
+export type TopologyState = {
+ namespaces: Namespace[];
+ queuesPerNamespace: Record;
+ topicsPerNamespace: Record;
+ subscriptionsPerNamespaceAndTopic: Record>;
+}
+
+export const initialState: TopologyState = {
+ namespaces: [
+ {
+ name: 'test-namespace',
+ id: 'test-namespace-id',
+ }
+ ],
+ queuesPerNamespace: {
+ "test-namespace-id": [
+ {
+ name: 'test-queue',
+ id: 'test-queue-id',
+ messageCount: 10,
+ deadLetterMessageCount: 0,
+ transferDeadLetterMessageCount: 0
+ }
+ ]
+ },
+ topicsPerNamespace: {
+ "test-namespace-id": [
+ {
+ name: 'test-topic',
+ id: 'test-topic-id'
+ }
+ ]
+ },
+ subscriptionsPerNamespaceAndTopic: {
+ "test-namespace-id": {
+ "test-topic-id": [{
+ name: 'test-subscription',
+ id: 'test-subscription-id',
+ messageCount: 20,
+ deadLetterMessageCount: 0,
+ transferDeadLetterMessageCount: 0
+ }]
+ }
+ }
+};
+
+export const logsReducer = createReducer(
+ initialState,
+);
+
+export const topologyFeature = createFeature({
+ name: featureKey,
+ reducer: logsReducer
+});
diff --git a/libs/topology/store/src/test-setup.ts b/libs/topology/store/src/test-setup.ts
new file mode 100644
index 0000000..ea41401
--- /dev/null
+++ b/libs/topology/store/src/test-setup.ts
@@ -0,0 +1,6 @@
+import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
+
+setupZoneTestEnv({
+ errorOnUnknownElements: true,
+ errorOnUnknownProperties: true,
+});
diff --git a/libs/topology/store/tsconfig.json b/libs/topology/store/tsconfig.json
new file mode 100644
index 0000000..fde35ea
--- /dev/null
+++ b/libs/topology/store/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "target": "es2022",
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ },
+ {
+ "path": "./tsconfig.spec.json"
+ }
+ ],
+ "extends": "../../../tsconfig.base.json",
+ "angularCompilerOptions": {
+ "enableI18nLegacyMessageIdFormat": false,
+ "strictInjectionParameters": true,
+ "strictInputAccessModifiers": true,
+ "strictTemplates": true
+ }
+}
diff --git a/libs/topology/store/tsconfig.lib.json b/libs/topology/store/tsconfig.lib.json
new file mode 100644
index 0000000..9b49be7
--- /dev/null
+++ b/libs/topology/store/tsconfig.lib.json
@@ -0,0 +1,17 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "declaration": true,
+ "declarationMap": true,
+ "inlineSources": true,
+ "types": []
+ },
+ "exclude": [
+ "src/**/*.spec.ts",
+ "src/test-setup.ts",
+ "jest.config.ts",
+ "src/**/*.test.ts"
+ ],
+ "include": ["src/**/*.ts"]
+}
diff --git a/libs/topology/store/tsconfig.spec.json b/libs/topology/store/tsconfig.spec.json
new file mode 100644
index 0000000..f858ef7
--- /dev/null
+++ b/libs/topology/store/tsconfig.spec.json
@@ -0,0 +1,16 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "module": "commonjs",
+ "target": "es2016",
+ "types": ["jest", "node"]
+ },
+ "files": ["src/test-setup.ts"],
+ "include": [
+ "jest.config.ts",
+ "src/**/*.test.ts",
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/nx.json b/nx.json
index 269599f..983c8a8 100644
--- a/nx.json
+++ b/nx.json
@@ -42,6 +42,11 @@
"codeCoverage": true
}
}
+ },
+ "@nx/js:tsc": {
+ "cache": true,
+ "dependsOn": ["^build"],
+ "inputs": ["production", "^production"]
}
},
"generators": {
@@ -56,7 +61,7 @@
"unitTestRunner": "jest"
},
"@nx/angular:component": {
- "style": "css"
+ "style": "scss"
}
},
"defaultProject": "servicebus-browser-app",
diff --git a/package-lock.json b/package-lock.json
index f4313f3..1eab907 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,11 @@
"@angular/platform-browser": "~19.0.0",
"@angular/platform-browser-dynamic": "~19.0.0",
"@angular/router": "~19.0.0",
+ "@fortawesome/angular-fontawesome": "^1.0.0",
+ "@fortawesome/fontawesome-svg-core": "^6.7.2",
+ "@fortawesome/free-brands-svg-icons": "^6.7.2",
+ "@fortawesome/free-regular-svg-icons": "^6.7.2",
+ "@fortawesome/free-solid-svg-icons": "^6.7.2",
"@ngrx/effects": "^19.0.0",
"@ngrx/store": "^19.0.0",
"@ngrx/store-devtools": "^19.0.0",
@@ -5016,6 +5021,76 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@fortawesome/angular-fontawesome": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-1.0.0.tgz",
+ "integrity": "sha512-EC2fYuXIuw2ld1kzJi+zysWus6OeGGfLQtbh0hW9zyyq5aBo8ZJkcJKBsVQ8E6Mg7nHyTWaXn+sdcXTPDWz+UQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^6.7.1",
+ "tslib": "^2.8.1"
+ },
+ "peerDependencies": {
+ "@angular/core": "^19.0.0"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz",
+ "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
+ "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
+ "license": "MIT",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.7.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-brands-svg-icons": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.2.tgz",
+ "integrity": "sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.7.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-regular-svg-icons": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz",
+ "integrity": "sha512-7Z/ur0gvCMW8G93dXIQOkQqHo2M5HLhYrRVC0//fakJXxcF1VmMPsxnG6Ee8qEylA8b8Q3peQXWMNZ62lYF28g==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.7.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz",
+ "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.7.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
diff --git a/package.json b/package.json
index a664d97..ee16f08 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,11 @@
"@angular/platform-browser": "~19.0.0",
"@angular/platform-browser-dynamic": "~19.0.0",
"@angular/router": "~19.0.0",
+ "@fortawesome/angular-fontawesome": "^1.0.0",
+ "@fortawesome/fontawesome-svg-core": "^6.7.2",
+ "@fortawesome/free-brands-svg-icons": "^6.7.2",
+ "@fortawesome/free-regular-svg-icons": "^6.7.2",
+ "@fortawesome/free-solid-svg-icons": "^6.7.2",
"@ngrx/effects": "^19.0.0",
"@ngrx/store": "^19.0.0",
"@ngrx/store-devtools": "^19.0.0",
diff --git a/tsconfig.base.json b/tsconfig.base.json
index da02b6d..a538fe7 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -21,7 +21,16 @@
"@service-bus-browser/logs-contracts": [
"libs/logs/contract/src/index.ts"
],
- "@service-bus-browser/logs-store": ["libs/logs/store/src/index.ts"]
+ "@service-bus-browser/logs-store": ["libs/logs/store/src/index.ts"],
+ "@service-bus-browser/topology-components": [
+ "libs/topology/components/src/index.ts"
+ ],
+ "@service-bus-browser/topology-contracts": [
+ "libs/topology/contracts/src/index.ts"
+ ],
+ "@service-bus-browser/topology-store": [
+ "libs/topology/store/src/index.ts"
+ ]
}
},
"exclude": ["node_modules", "tmp"]