From e96a4cd135fb20b6cc436deef5dbccb992bb3e6f Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Fri, 25 Apr 2025 23:15:08 +0200
Subject: [PATCH 01/25] use sbom in frontend application
---
src/FacturXDotNet.WebEditor/.gitignore | 3 +
src/FacturXDotNet.WebEditor/package-lock.json | 8 +
src/FacturXDotNet.WebEditor/package.json | 3 +-
.../scripts/dump-licenses.js | 26 ++-
.../about/about-licenses.component.ts | 175 ++++++++++++----
.../src/app/features/about/about.page.ts | 58 +++--
.../src/licenses/licenses.json | 198 ------------------
src/FacturXDotNet.sln.DotSettings.user | 1 +
8 files changed, 214 insertions(+), 258 deletions(-)
delete mode 100644 src/FacturXDotNet.WebEditor/src/licenses/licenses.json
diff --git a/src/FacturXDotNet.WebEditor/.gitignore b/src/FacturXDotNet.WebEditor/.gitignore
index cc7b1413..50a92bcb 100644
--- a/src/FacturXDotNet.WebEditor/.gitignore
+++ b/src/FacturXDotNet.WebEditor/.gitignore
@@ -40,3 +40,6 @@ testem.log
# System files
.DS_Store
Thumbs.db
+
+src/dependencies/dependencies.json
+src/dependencies/direct-dependencies.json
diff --git a/src/FacturXDotNet.WebEditor/package-lock.json b/src/FacturXDotNet.WebEditor/package-lock.json
index 7d28246d..80992d0b 100644
--- a/src/FacturXDotNet.WebEditor/package-lock.json
+++ b/src/FacturXDotNet.WebEditor/package-lock.json
@@ -19,6 +19,7 @@
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
"@popperjs/core": "^2.11.8",
"@types/bootstrap": "^5.2.10",
+ "@types/semver": "^7.7.0",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"idb": "^8.0.2",
@@ -27,6 +28,7 @@
"ngx-markdown": "^19.1.1",
"pdfjs-dist": "^5.1.91",
"rxjs": "~7.8.0",
+ "semver": "^7.7.1",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
@@ -5932,6 +5934,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/semver": {
+ "version": "7.7.0",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz",
+ "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==",
+ "license": "MIT"
+ },
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
diff --git a/src/FacturXDotNet.WebEditor/package.json b/src/FacturXDotNet.WebEditor/package.json
index b7d73734..9ae7bda6 100644
--- a/src/FacturXDotNet.WebEditor/package.json
+++ b/src/FacturXDotNet.WebEditor/package.json
@@ -21,6 +21,7 @@
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
"@popperjs/core": "^2.11.8",
"@types/bootstrap": "^5.2.10",
+ "@types/semver": "^7.7.0",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"idb": "^8.0.2",
@@ -29,6 +30,7 @@
"ngx-markdown": "^19.1.1",
"pdfjs-dist": "^5.1.91",
"rxjs": "~7.8.0",
+ "semver": "^7.7.1",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
@@ -44,7 +46,6 @@
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
- "license-report": "^6.7.2",
"nswag": "^14.3.0",
"prettier": "3.5.3",
"typescript": "~5.7.2"
diff --git a/src/FacturXDotNet.WebEditor/scripts/dump-licenses.js b/src/FacturXDotNet.WebEditor/scripts/dump-licenses.js
index 03398029..f285fc4e 100644
--- a/src/FacturXDotNet.WebEditor/scripts/dump-licenses.js
+++ b/src/FacturXDotNet.WebEditor/scripts/dump-licenses.js
@@ -2,16 +2,20 @@ const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
-const configFilePath = path.join(__dirname, "..", "license-report-config.json");
-const outputFilePath = path.join(__dirname, "..", "src", "licenses", "licenses.json");
+const dependenciesFileDirectory = path.join(__dirname, "..", "src", "dependencies");
+fs.mkdirSync(dependenciesFileDirectory, { recursive: true });
-execSync("npm i -g license-report", { stdio: "inherit" });
-console.log();
+const dependenciesFilePath = path.join(dependenciesFileDirectory, "dependencies.json");
+const dependenciesFile = fs.openSync(dependenciesFilePath, "w+");
+execSync("npm sbom --sbom-format cyclonedx", { stdio: ["inherit", dependenciesFile, "inherit"] });
+fs.closeSync(dependenciesFile);
+console.log(`Dependencies have been written at ${dependenciesFilePath}`);
-console.log(`Using configuration at at ${configFilePath}`);
-
-const outputFile = fs.openSync(outputFilePath, "w+");
-execSync(`license-report --config ${configFilePath}`, { stdio: ["inherit", outputFile, "inherit"] });
-fs.closeSync(outputFile);
-
-console.log(`Licenses have been written at ${outputFilePath}`);
+const packageJson = JSON.parse(fs.readFileSync("package.json", "utf8"));
+const directDependenciesNames = [
+ ...Object.entries(packageJson.dependencies).map(([name, version]) => ({ name, version })),
+ ...Object.entries(packageJson.devDependencies).map(([name, version]) => ({ name, version })),
+];
+const directDependenciesFilePath = path.join(dependenciesFileDirectory, "direct-dependencies.json");
+fs.writeFileSync(directDependenciesFilePath, JSON.stringify(directDependenciesNames));
+console.log(`Direct dependencies have been written at ${dependenciesFilePath}`);
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
index e2da08f8..5fe88266 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
@@ -1,50 +1,155 @@
-import { Component, computed, input, Signal } from '@angular/core';
-import { IPackageDto } from '../../core/api/api.models';
+import { Component, computed, input, signal, Signal } from '@angular/core';
+import semver from 'semver/preload';
+import { NgTemplateOutlet } from '@angular/common';
@Component({
selector: 'app-about-licenses',
+ imports: [NgTemplateOutlet],
template: `
-
- @for (license of licenses(); track license) {
-
-
{{ license }} ({{ packagesRecord()[license].length }})
-
- @for (p of packagesRecord()[license]; track p.name) {
-
-
{{ p.name }}
- v{{ p.version }}
- -
-
- {{ p.author }}
-
-
- }
-
-
- }
+
+
+
+
+ Direct dependencies ({{ directDependenciesRecord().packagesCount }})
+
+
+
+
+ All dependencies ({{ allDependenciesRecord().packagesCount }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @for (license of record.licenses; track license.license) {
+
+
{{ license.license }} ({{ license.packages.length }})
+
+ @for (package_ of license.packages; track package_.name) {
+
+ {{ package_.latest.name }}
+
+ @switch (package_.versions.length) {
+ @case (0) {}
+ @case (1) {
+ v{{ package_.versions[0] }}
+ }
+ @default {
+ @for (version of package_.versions; track version) {
+ @if ($last) {
+ v{{ version }}
+ } @else {
+ v{{ version }} ,
+ }
+ }
+ }
+ }
+
+ @if (package_.latest.description) {
+ -
+
+ {{ package_.latest.description }}
+
+ }
+ @if (package_.latest.author) {
+ -
+
+ {{ package_.latest.author }}
+
+ }
+
+ }
+
+
+ }
+
+
`,
})
export class AboutLicensesComponent {
- packages = input.required
();
+ packages = input.required();
- protected packagesRecord: Signal> = computed(() => {
- const result: Record = {};
+ protected directDependenciesRecord: Signal = computed(() =>
+ groupPackages(
+ this.packages().filter((p) => p.direct),
+ true,
+ ),
+ );
+ protected allDependenciesRecord: Signal = computed(() => groupPackages(this.packages()));
- for (const p of this.packages()) {
- if (!result[p.license]) {
- result[p.license] = [];
- }
+ protected activeTab = signal<'direct' | 'all'>('direct');
+}
- result[p.license].push(p);
+export interface Package {
+ name: string;
+ description?: string;
+ author?: string;
+ version: string;
+ license?: string;
+ link?: string;
+ direct: boolean;
+}
+
+interface GroupedPackages {
+ packagesCount: number;
+ licenses: {
+ license: string;
+ packages: {
+ name: string;
+ versions: string[];
+ latest: Package;
+ packages: Package[];
+ }[];
+ }[];
+}
+
+function groupPackages(packages: Package[], keepLatestOnly: boolean = false): GroupedPackages {
+ const licenses: Record> = {};
+
+ for (const p of packages) {
+ const license = p.license ?? 'N/A';
+
+ if (!licenses[license]) {
+ licenses[license] = {};
+ }
+
+ if (!licenses[license][p.name]) {
+ licenses[license][p.name] = [];
}
- return result;
- });
+ if (keepLatestOnly) {
+ if (licenses[license][p.name].every((x) => semver.lt(x.version, p.version))) {
+ licenses[license][p.name] = [p];
+ }
+ } else {
+ licenses[license][p.name].push(p);
+ }
+ }
- protected licenses: Signal = computed(() => {
- const result = Object.keys(this.packagesRecord());
- result.sort((a, b) => a.localeCompare(b));
- return result;
- });
+ return {
+ packagesCount: Object.values(licenses)
+ .map((l) => Object.keys(l).length)
+ .reduce((a, b) => a + b),
+ licenses: Object.entries(licenses).map(([license, packages]) => ({
+ license,
+ packages: Object.entries(packages)
+ .map(([name, packages]) => ({
+ name,
+ versions: [...new Set(packages.map((p) => p.version).filter((v) => v !== undefined && v !== null && v != ''))].sort((a, b) => -semver.compare(a, b)),
+ latest: packages.sort((a, b) => -semver.compare(a.version, b.version))[0],
+ packages,
+ }))
+ .sort((a, b) => a.name.localeCompare(b.name)),
+ })),
+ };
}
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
index 794451db..03293632 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
@@ -1,13 +1,14 @@
import { Component, computed, inject } from '@angular/core';
import { RouterLink } from '@angular/router';
-import { AboutLicensesComponent } from './about-licenses.component';
+import { AboutLicensesComponent, Package } from './about-licenses.component';
import { environment } from '../../../environments/environment';
import { DatePipe, NgOptimizedImage } from '@angular/common';
import { API_BASE_URL } from '../../app.config';
-import licenses from '../../../licenses/licenses.json';
+import dependencies from '../../../dependencies/dependencies.json';
+import directDependencies from '../../../dependencies/direct-dependencies.json';
import { ApiServerStatusComponent } from '../../core/api/components/api-server-status.component';
import { ApiConstantsService } from '../../core/api/services/api-constants.service';
-import { IPackageDto } from '../../core/api/api.models';
+import semver from 'semver/preload';
@Component({
selector: 'app-about',
@@ -144,8 +145,9 @@ import { IPackageDto } from '../../core/api/api.models';
}
- Dependencies
-
+
+
+
}
}
@@ -173,7 +175,8 @@ import { IPackageDto } from '../../core/api/api.models';
on GitHub .
- Dependencies
+
+
@@ -184,13 +187,17 @@ import { IPackageDto } from '../../core/api/api.models';
imports: [RouterLink, AboutLicensesComponent, DatePipe, ApiServerStatusComponent, NgOptimizedImage],
})
export class AboutPage {
- protected webEditorPackages: IPackageDto[] = licenses.map((l) => ({
- name: l.name,
- author: l.author,
- version: l.installedVersion,
- license: l.licenseType,
- link: l.link,
- }));
+ protected webEditorPackages: Package[] = dependencies.components.map((l) => {
+ return {
+ name: l.name,
+ description: l.description,
+ author: l.author,
+ version: l.version,
+ license: getLicense(l.licenses?.[0]),
+ link: l.externalReferences.find((r) => r.type === 'website')?.url,
+ direct: directDependencies.some((d) => l.name === d.name && semver.satisfies(l.version, d.version)),
+ };
+ });
protected webEditorVersion = removeBuildInformation(environment.version);
protected webEditorBuildMetadata = extractBuildInformation(environment.version);
@@ -217,6 +224,15 @@ export class AboutPage {
return extractBuildInformation(apiConstants.build.version);
});
+ protected dependencies = computed(() => {
+ const info = this.apiConstantsService.info.value();
+ if (info === undefined) {
+ return [];
+ }
+
+ return info.dependencies.map((d) => ({ ...d, direct: true }));
+ });
+
protected apiConstants = this.apiConstantsService.info;
}
@@ -237,3 +253,19 @@ function removeBuildInformation(version: string) {
return version.substring(0, indexOfPlus);
}
+
+function getLicense(license: { license: { id: string } } | { expression: string } | undefined): string | undefined {
+ if (license === undefined) {
+ return undefined;
+ }
+
+ if (isExpressionLicense(license)) {
+ return license.expression;
+ }
+
+ return license.license.id;
+}
+
+function isExpressionLicense(license: { license: { id: string } } | { expression: string }): license is { expression: string } {
+ return Object.keys(license).includes('expression');
+}
diff --git a/src/FacturXDotNet.WebEditor/src/licenses/licenses.json b/src/FacturXDotNet.WebEditor/src/licenses/licenses.json
deleted file mode 100644
index dada8320..00000000
--- a/src/FacturXDotNet.WebEditor/src/licenses/licenses.json
+++ /dev/null
@@ -1,198 +0,0 @@
-[
- {
- "name": "@angular/animations",
- "author": "angular",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@angular/common",
- "author": "angular",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@angular/compiler",
- "author": "angular",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@angular/core",
- "author": "angular",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@angular/forms",
- "author": "angular",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@angular/platform-browser",
- "author": "angular",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@angular/platform-browser-dynamic",
- "author": "angular",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@angular/router",
- "author": "angular",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@types/bootstrap",
- "author": "n/a",
- "installedVersion": "5.2.10",
- "licenseType": "MIT",
- "link": "https://github.com/DefinitelyTyped/DefinitelyTyped.git"
- },
- {
- "name": "bootstrap",
- "author": "The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)",
- "installedVersion": "5.3.3",
- "licenseType": "MIT",
- "link": "git+https://github.com/twbs/bootstrap.git"
- },
- {
- "name": "bootstrap-icons",
- "author": "mdo",
- "installedVersion": "1.11.3",
- "licenseType": "MIT",
- "link": "git+https://github.com/twbs/icons.git"
- },
- {
- "name": "idb",
- "author": "Jake Archibald",
- "installedVersion": "8.0.2",
- "licenseType": "ISC",
- "link": "git://github.com/jakearchibald/idb.git"
- },
- {
- "name": "rxjs",
- "author": "Ben Lesh ",
- "installedVersion": "7.8.2",
- "licenseType": "Apache-2.0",
- "link": "git+https://github.com/reactivex/rxjs.git"
- },
- {
- "name": "tslib",
- "author": "Microsoft Corp.",
- "installedVersion": "2.8.1",
- "licenseType": "0BSD",
- "link": "git+https://github.com/Microsoft/tslib.git"
- },
- {
- "name": "zone.js",
- "author": "Brian Ford",
- "installedVersion": "0.15.0",
- "licenseType": "MIT",
- "link": "git://github.com/angular/angular.git"
- },
- {
- "name": "@angular-devkit/build-angular",
- "author": "Angular Authors",
- "installedVersion": "19.2.5",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular-cli.git"
- },
- {
- "name": "@angular/cli",
- "author": "Angular Authors",
- "installedVersion": "19.2.5",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular-cli.git"
- },
- {
- "name": "@angular/compiler-cli",
- "author": "n/a",
- "installedVersion": "19.2.4",
- "licenseType": "MIT",
- "link": "git+https://github.com/angular/angular.git"
- },
- {
- "name": "@types/jasmine",
- "author": "n/a",
- "installedVersion": "5.1.7",
- "licenseType": "MIT",
- "link": "https://github.com/DefinitelyTyped/DefinitelyTyped.git"
- },
- {
- "name": "jasmine-core",
- "author": "n/a",
- "installedVersion": "5.5.0",
- "licenseType": "MIT",
- "link": "git+https://github.com/jasmine/jasmine.git"
- },
- {
- "name": "karma",
- "author": "Vojta Jína ",
- "installedVersion": "6.4.4",
- "licenseType": "MIT",
- "link": "git://github.com/karma-runner/karma.git"
- },
- {
- "name": "karma-chrome-launcher",
- "author": "Vojta Jina ",
- "installedVersion": "3.2.0",
- "licenseType": "MIT",
- "link": "git://github.com/karma-runner/karma-chrome-launcher.git"
- },
- {
- "name": "karma-coverage",
- "author": "SATO taichi ",
- "installedVersion": "2.2.1",
- "licenseType": "MIT",
- "link": "git://github.com/karma-runner/karma-coverage.git"
- },
- {
- "name": "karma-jasmine",
- "author": "Vojta Jina ",
- "installedVersion": "5.1.0",
- "licenseType": "MIT",
- "link": "git://github.com/karma-runner/karma-jasmine.git"
- },
- {
- "name": "karma-jasmine-html-reporter",
- "author": "David Federman (https://github.com/dfederm)",
- "installedVersion": "2.1.0",
- "licenseType": "MIT",
- "link": "git+https://github.com/dfederm/karma-jasmine-html-reporter.git"
- },
- {
- "name": "license-report",
- "author": "Yaniv Kessler",
- "installedVersion": "6.7.2",
- "licenseType": "MIT",
- "link": "git+https://github.com/kessler/license-report.git"
- },
- {
- "name": "prettier",
- "author": "James Long",
- "installedVersion": "3.5.3",
- "licenseType": "MIT",
- "link": "git+https://github.com/prettier/prettier.git"
- },
- {
- "name": "typescript",
- "author": "Microsoft Corp.",
- "installedVersion": "5.7.3",
- "licenseType": "Apache-2.0",
- "link": "git+https://github.com/microsoft/TypeScript.git"
- }
-]
diff --git a/src/FacturXDotNet.sln.DotSettings.user b/src/FacturXDotNet.sln.DotSettings.user
index 520b3860..8da17acf 100644
--- a/src/FacturXDotNet.sln.DotSettings.user
+++ b/src/FacturXDotNet.sln.DotSettings.user
@@ -38,6 +38,7 @@
ForceIncluded
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
From aafd1c63a5fed82bbbebc1df632754063c4c0f70 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 00:59:11 +0200
Subject: [PATCH 02/25] allow to fold and unfold license blocks
---
.../about/about-licenses.component.ts | 62 +++++++++++++++----
1 file changed, 51 insertions(+), 11 deletions(-)
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
index 5fe88266..15beb03c 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
@@ -1,12 +1,13 @@
-import { Component, computed, input, signal, Signal } from '@angular/core';
+import { Component, computed, input, linkedSignal, signal, Signal, WritableSignal } from '@angular/core';
import semver from 'semver/preload';
import { NgTemplateOutlet } from '@angular/common';
+import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-about-licenses',
- imports: [NgTemplateOutlet],
+ imports: [NgTemplateOutlet, NgbTooltip],
template: `
-
+
@@ -19,6 +20,14 @@ import { NgTemplateOutlet } from '@angular/common';
+
+
+
+
+
+
+
+
@@ -32,8 +41,15 @@ import { NgTemplateOutlet } from '@angular/common';
@for (license of record.licenses; track license.license) {
-
{{ license.license }} ({{ license.packages.length }})
-
+
+ @if (hideLicense()[license.license]()) {
+
+ } @else {
+
+ }
+ {{ license.license }} ({{ license.packages.length }})
+
+
@@ -88,6 +104,30 @@ export class AboutLicensesComponent {
protected allDependenciesRecord: Signal = computed(() => groupPackages(this.packages()));
protected activeTab = signal<'direct' | 'all'>('direct');
+ protected hideLicense: Signal>> = linkedSignal({
+ source: this.allDependenciesRecord,
+ computation: (source, previous) => {
+ const newValue = previous !== undefined ? { ...previous.value } : {};
+ for (const license of source.licenses) {
+ newValue[license.license] = newValue[license.license] ?? signal(false);
+ }
+ return newValue;
+ },
+ });
+
+ protected collapseAll() {
+ const hideLicense = this.hideLicense();
+ for (const value of Object.values(hideLicense)) {
+ value.set(true);
+ }
+ }
+
+ protected expandAll() {
+ const hideLicense = this.hideLicense();
+ for (const value of Object.values(hideLicense)) {
+ value.set(false);
+ }
+ }
}
export interface Package {
From 84fe46b89ebf82ab3247bea950b1d61174f6a9f3 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 01:56:10 +0200
Subject: [PATCH 03/25] add search to dependencies
---
src/FacturXDotNet.WebEditor/package-lock.json | 490 +-----------------
src/FacturXDotNet.WebEditor/package.json | 1 +
.../highlight-text/highlight-text.pipe.ts | 19 +
.../about/about-licenses.component.ts | 115 ++--
src/FacturXDotNet.WebEditor/src/styles.css | 5 +
5 files changed, 116 insertions(+), 514 deletions(-)
create mode 100644 src/FacturXDotNet.WebEditor/src/app/core/highlight-text/highlight-text.pipe.ts
diff --git a/src/FacturXDotNet.WebEditor/package-lock.json b/src/FacturXDotNet.WebEditor/package-lock.json
index 80992d0b..b0e64034 100644
--- a/src/FacturXDotNet.WebEditor/package-lock.json
+++ b/src/FacturXDotNet.WebEditor/package-lock.json
@@ -24,6 +24,7 @@
"bootstrap-icons": "^1.11.3",
"idb": "^8.0.2",
"marked": "^15.0.8",
+ "minisearch": "^7.1.2",
"ngx-filesize": "^3.0.5",
"ngx-markdown": "^19.1.1",
"pdfjs-dist": "^5.1.91",
@@ -44,7 +45,6 @@
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
- "license-report": "^6.7.2",
"nswag": "^14.3.0",
"prettier": "3.5.3",
"typescript": "~5.7.2"
@@ -3505,13 +3505,6 @@
"tslib": "2"
}
},
- "node_modules/@kessler/tableify": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@kessler/tableify/-/tableify-1.0.2.tgz",
- "integrity": "sha512-e4psVV9Fe2eBfS9xK2rzQ9lE5xS4tARm7EJzDb6sVZy3F+EMyHJ67i0NdBVR9BRyQx7YhogMCbB6R1QwXuBxMg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@leichtgewicht/ip-codec": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
@@ -5204,13 +5197,6 @@
"yarn": ">= 1.13.0"
}
},
- "node_modules/@sec-ant/readable-stream": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
- "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@sigstore/bundle": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-3.1.0.tgz",
@@ -5291,19 +5277,6 @@
"node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/@sindresorhus/is": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.1.tgz",
- "integrity": "sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/is?sponsor=1"
- }
- },
"node_modules/@sindresorhus/merge-streams": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
@@ -5324,19 +5297,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@szmarczak/http-timer": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
- "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "defer-to-connect": "^2.0.1"
- },
- "engines": {
- "node": ">=14.16"
- }
- },
"node_modules/@tufjs/canonical-json": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
@@ -5848,13 +5808,6 @@
"license": "MIT",
"optional": true
},
- "node_modules/@types/http-cache-semantics": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
- "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
@@ -6985,35 +6938,6 @@
"node": ">=18"
}
},
- "node_modules/cacheable-lookup": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
- "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- }
- },
- "node_modules/cacheable-request": {
- "version": "12.0.1",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-12.0.1.tgz",
- "integrity": "sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/http-cache-semantics": "^4.0.4",
- "get-stream": "^9.0.1",
- "http-cache-semantics": "^4.1.1",
- "keyv": "^4.5.4",
- "mimic-response": "^4.0.0",
- "normalize-url": "^8.0.1",
- "responselike": "^3.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -8375,45 +8299,6 @@
}
}
},
- "node_modules/decompress-response": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
- "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mimic-response": "^3.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/decompress-response/node_modules/mimic-response": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
- "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
"node_modules/default-browser": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz",
@@ -8457,16 +8342,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/defer-to-connect": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
- "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/define-lazy-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
@@ -8847,16 +8722,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/eol": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/eol/-/eol-0.10.0.tgz",
- "integrity": "sha512-+w3ktYrOphcIqC1XKmhQYvM+o2uxgQFiimL7B6JPZJlWVxf7Lno9e/JWLPIgbHo7DoZ+b7jsf/NzrUcNe6ZTZQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ryanve"
- }
- },
"node_modules/err-code": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
@@ -9465,16 +9330,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/form-data-encoder": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz",
- "integrity": "sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 18"
- }
- },
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -9639,33 +9494,6 @@
"node": ">= 0.4"
}
},
- "node_modules/get-stdin": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
- "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/get-stream": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
- "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@sec-ant/readable-stream": "^0.4.1",
- "is-stream": "^4.0.1"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -9761,45 +9589,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/got": {
- "version": "14.4.7",
- "resolved": "https://registry.npmjs.org/got/-/got-14.4.7.tgz",
- "integrity": "sha512-DI8zV1231tqiGzOiOzQWDhsBmncFW7oQDH6Zgy6pDPrqJuVZMtoSgPLLsBZQj8Jg4JFfwoOsDA8NGtLQLnIx2g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@sindresorhus/is": "^7.0.1",
- "@szmarczak/http-timer": "^5.0.1",
- "cacheable-lookup": "^7.0.0",
- "cacheable-request": "^12.0.1",
- "decompress-response": "^6.0.0",
- "form-data-encoder": "^4.0.2",
- "http2-wrapper": "^2.2.1",
- "lowercase-keys": "^3.0.0",
- "p-cancelable": "^4.0.1",
- "responselike": "^3.0.0",
- "type-fest": "^4.26.1"
- },
- "engines": {
- "node": ">=20"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/got?sponsor=1"
- }
- },
- "node_modules/got/node_modules/type-fest": {
- "version": "4.39.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz",
- "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -10074,20 +9863,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/http2-wrapper": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
- "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "quick-lru": "^5.1.1",
- "resolve-alpn": "^1.2.0"
- },
- "engines": {
- "node": ">=10.19.0"
- }
- },
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
@@ -10504,19 +10279,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-stream": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
- "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-unicode-supported": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
@@ -10773,13 +10535,6 @@
"node": ">=6"
}
},
- "node_modules/json-buffer": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
- "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/json-parse-even-better-errors": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz",
@@ -11200,16 +10955,6 @@
"node": ">= 12"
}
},
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "json-buffer": "3.0.1"
- }
- },
"node_modules/khroma": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
@@ -11373,30 +11118,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/license-report": {
- "version": "6.7.2",
- "resolved": "https://registry.npmjs.org/license-report/-/license-report-6.7.2.tgz",
- "integrity": "sha512-DWWy7Hmm266CAtx9T+rPNnBwqLNT/Gddc6nToMERQTrsp/caQjrS8PEOl/ymiER24rk12GDLqJqri73mBQj0dg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@kessler/tableify": "^1.0.2",
- "debug": "^4.4.0",
- "eol": "^0.10.0",
- "got": "^14.4.6",
- "rc": "^1.2.8",
- "semver": "^7.7.1",
- "tablemark": "^3.1.0",
- "text-table": "^0.2.0",
- "visit-values": "^2.0.0"
- },
- "bin": {
- "license-report": "index.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/license-webpack-plugin": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz",
@@ -11714,29 +11435,6 @@
"node": ">=8.0"
}
},
- "node_modules/lower-case": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
- "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.0.3"
- }
- },
- "node_modules/lowercase-keys": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
- "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -12010,19 +11708,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/mimic-response": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
- "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/mini-css-extract-plugin": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz",
@@ -12214,6 +11899,12 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/minisearch": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz",
+ "integrity": "sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==",
+ "license": "MIT"
+ },
"node_modules/minizlib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz",
@@ -12516,17 +12207,6 @@
"zone.js": "~0.15.0"
}
},
- "node_modules/no-case": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
- "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "lower-case": "^2.0.2",
- "tslib": "^2.0.3"
- }
- },
"node_modules/node-addon-api": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
@@ -12755,19 +12435,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/normalize-url": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
- "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/npm-bundled": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz",
@@ -13108,16 +12775,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/p-cancelable": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz",
- "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- }
- },
"node_modules/p-limit": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
@@ -13844,19 +13501,6 @@
],
"license": "MIT"
},
- "node_modules/quick-lru": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
- "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -13893,29 +13537,6 @@
"node": ">= 0.8"
}
},
- "node_modules/rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "dev": true,
- "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
- "dependencies": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "bin": {
- "rc": "cli.js"
- }
- },
- "node_modules/rc/node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -14092,13 +13713,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/resolve-alpn": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
- "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -14151,22 +13765,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/responselike": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
- "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "lowercase-keys": "^3.0.0"
- },
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/restore-cursor": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
@@ -14598,18 +14196,6 @@
"node": ">= 0.8"
}
},
- "node_modules/sentence-case": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz",
- "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "no-case": "^3.0.4",
- "tslib": "^2.0.3",
- "upper-case-first": "^2.0.2"
- }
- },
"node_modules/serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
@@ -15222,20 +14808,6 @@
"wbuf": "^1.7.3"
}
},
- "node_modules/split-text-to-chunks": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/split-text-to-chunks/-/split-text-to-chunks-1.0.0.tgz",
- "integrity": "sha512-HLtEwXK/T4l7QZSJ/kOSsZC0o5e2Xg3GzKKFxm0ZexJXw0Bo4CaEl39l7MCSRHk9EOOL5jT8JIDjmhTtcoe6lQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-stdin": "^5.0.1",
- "minimist": "^1.2.0"
- },
- "bin": {
- "wordwrap": "cli.js"
- }
- },
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
@@ -15405,16 +14977,6 @@
"node": ">=8"
}
},
- "node_modules/strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/stylis": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
@@ -15458,20 +15020,6 @@
"node": ">=0.10"
}
},
- "node_modules/tablemark": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/tablemark/-/tablemark-3.1.0.tgz",
- "integrity": "sha512-IwO6f0SEzp1Z+zqz/7ANUmeEac4gaNlknWyj/S9aSg11wZmWYnLeyI/xXvEOU88BYUIf8y30y0wxB58xIKrVlQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "sentence-case": "^3.0.4",
- "split-text-to-chunks": "^1.0.0"
- },
- "engines": {
- "node": ">=14.16"
- }
- },
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -15637,13 +15185,6 @@
}
}
},
- "node_modules/text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/thingies": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz",
@@ -15992,16 +15533,6 @@
"browserslist": ">= 4.21.0"
}
},
- "node_modules/upper-case-first": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz",
- "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.0.3"
- }
- },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -16060,13 +15591,6 @@
"node": ">= 0.8"
}
},
- "node_modules/visit-values": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/visit-values/-/visit-values-2.0.0.tgz",
- "integrity": "sha512-vLFU70y3D915d611GnHYeHkEmq6ZZETzTH4P1hM6I9E3lBwH2VeBBEESe/bGCY+gAyK0qqLFn5bNFpui/GKmww==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/vite": {
"version": "6.2.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
diff --git a/src/FacturXDotNet.WebEditor/package.json b/src/FacturXDotNet.WebEditor/package.json
index 9ae7bda6..32db7d8d 100644
--- a/src/FacturXDotNet.WebEditor/package.json
+++ b/src/FacturXDotNet.WebEditor/package.json
@@ -26,6 +26,7 @@
"bootstrap-icons": "^1.11.3",
"idb": "^8.0.2",
"marked": "^15.0.8",
+ "minisearch": "^7.1.2",
"ngx-filesize": "^3.0.5",
"ngx-markdown": "^19.1.1",
"pdfjs-dist": "^5.1.91",
diff --git a/src/FacturXDotNet.WebEditor/src/app/core/highlight-text/highlight-text.pipe.ts b/src/FacturXDotNet.WebEditor/src/app/core/highlight-text/highlight-text.pipe.ts
new file mode 100644
index 00000000..0b51f181
--- /dev/null
+++ b/src/FacturXDotNet.WebEditor/src/app/core/highlight-text/highlight-text.pipe.ts
@@ -0,0 +1,19 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'highlightText',
+})
+export class HighlightTextPipe implements PipeTransform {
+ transform(value: string, args: string[] | undefined): any {
+ if (args === undefined || args.length === 0) {
+ return value;
+ }
+
+ let result = value;
+ for (const arg of args) {
+ const regex = new RegExp(arg.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'ig');
+ result = result.replace(regex, `$& `);
+ }
+ return result;
+ }
+}
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
index 15beb03c..a696854f 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about-licenses.component.ts
@@ -1,11 +1,16 @@
-import { Component, computed, input, linkedSignal, signal, Signal, WritableSignal } from '@angular/core';
+import { Component, computed, input, linkedSignal, signal, Signal, untracked, WritableSignal } from '@angular/core';
import semver from 'semver/preload';
import { NgTemplateOutlet } from '@angular/common';
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
+import { FormsModule } from '@angular/forms';
+import MiniSearch from 'minisearch';
+import { HighlightTextPipe } from '../../core/highlight-text/highlight-text.pipe';
+
+const UnknownLicenseName = 'N/A';
@Component({
selector: 'app-about-licenses',
- imports: [NgTemplateOutlet, NgbTooltip],
+ imports: [NgTemplateOutlet, NgbTooltip, FormsModule, HighlightTextPipe],
template: `
@@ -30,15 +35,23 @@ import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
-
-
-
-
-
+
+
{{ searchResult().packagesCount }} result(s)
+
+
+
-
`,
- imports: [RouterLink, AboutLicensesComponent, DatePipe, ApiServerStatusComponent, NgOptimizedImage],
+ imports: [RouterLink, AboutSbomComponent, DatePipe, ApiServerStatusComponent, NgOptimizedImage],
})
export class AboutPage {
- protected webEditorPackages: Package[] = dependencies.components.map((l) => {
- return {
- name: l.name,
- description: l.description,
- author: l.author,
- version: l.version,
- license: getLicense(l.licenses?.[0]),
- link: l.externalReferences.find((r) => r.type === 'website')?.url,
- direct: directDependencies.some((d) => l.name === d.name && semver.satisfies(l.version, d.version)),
- };
- });
+ protected webEditorSbom = sbom;
protected webEditorVersion = removeBuildInformation(environment.version);
protected webEditorBuildMetadata = extractBuildInformation(environment.version);
@@ -224,15 +212,6 @@ export class AboutPage {
return extractBuildInformation(apiConstants.build.version);
});
- protected dependencies = computed(() => {
- const info = this.apiConstantsService.info.value();
- if (info === undefined) {
- return [];
- }
-
- return info.dependencies.map((d) => ({ ...d, direct: true }));
- });
-
protected apiConstants = this.apiConstantsService.info;
}
@@ -253,19 +232,3 @@ function removeBuildInformation(version: string) {
return version.substring(0, indexOfPlus);
}
-
-function getLicense(license: { license: { id: string } } | { expression: string } | undefined): string | undefined {
- if (license === undefined) {
- return undefined;
- }
-
- if (isExpressionLicense(license)) {
- return license.expression;
- }
-
- return license.license.id;
-}
-
-function isExpressionLicense(license: { license: { id: string } } | { expression: string }): license is { expression: string } {
- return Object.keys(license).includes('expression');
-}
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/dependency.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/dependency.ts
new file mode 100644
index 00000000..b7364ea1
--- /dev/null
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/dependency.ts
@@ -0,0 +1,47 @@
+import { isSbomLicenseExpression, Sbom, SbomComponent, SbomLicense, SbomLicenseExpression } from '../../core/sbom';
+
+export interface Dependency {
+ name: string;
+ description?: string;
+ author?: string;
+ version: string;
+ license?: string;
+ link?: string;
+ direct: boolean;
+}
+
+export function extractDependenciesFromSbom(sbom: Sbom) {
+ return sbom.components.map((component) => {
+ return {
+ name: component.name,
+ description: component.description,
+ author: component.author,
+ version: component.version,
+ license: getLicense(component.licenses?.[0]),
+ link: component.externalReferences.find((r) => r.type === 'website')?.url,
+ direct: isDirectDependency(sbom, component),
+ };
+ });
+}
+
+function getLicense(license: SbomLicense | undefined): string | undefined {
+ if (license === undefined) {
+ return undefined;
+ }
+
+ if (isSbomLicenseExpression(license)) {
+ return license.expression;
+ }
+
+ return license.license.id;
+}
+
+function isDirectDependency(sbom: Sbom, component: SbomComponent): boolean {
+ const thisComponent = sbom.metadata.component['bom-ref'];
+ const dependencies = sbom.dependencies.find((d) => d.ref === thisComponent);
+ if (dependencies === undefined) {
+ return false;
+ }
+
+ return dependencies.dependsOn.includes(component['bom-ref']);
+}
From 6b30617785071be262bd5d58749f7af1631904b1 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 15:56:39 +0200
Subject: [PATCH 05/25] improve frontend layout
---
.../features/about/about-sbom.component.ts | 45 ++++++++------
.../src/app/features/about/dependency.ts | 58 ++++++++++++++++---
2 files changed, 77 insertions(+), 26 deletions(-)
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
index 18d1531f..dcbd364f 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
@@ -5,12 +5,9 @@ import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
import { FormsModule } from '@angular/forms';
import MiniSearch from 'minisearch';
import { HighlightTextPipe } from '../../core/highlight-text/highlight-text.pipe';
-import sbom from '../../../dependencies/sbom.json';
-import { Sbom, SbomComponent } from '../../core/sbom';
+import { Sbom } from '../../core/sbom';
import { Dependency, extractDependenciesFromSbom } from './dependency';
-const UnknownLicenseName = 'N/A';
-
@Component({
selector: 'app-about-licenses',
imports: [NgTemplateOutlet, NgbTooltip, FormsModule, HighlightTextPipe],
@@ -61,7 +58,13 @@ const UnknownLicenseName = 'N/A';
} @else {
}
- {{ license.license }} ({{ license.packages.length }})
+
+ @if (license.license === '') {
+ Unknown
+ } @else {
+ {{ license.license }}
+ }
+ ({{ license.packages.length }})
@for (package_ of license.packages; track package_.name) {
@@ -156,7 +159,7 @@ export class AboutSbomComponent {
protected collapsedBlocks: Signal>> = linkedSignal({
source: this.dependencies,
computation: (source, previous) => {
- const licenses = new Set(source.map((p) => p.license ?? UnknownLicenseName));
+ const licenses = new Set(source.map((p) => licenseNameOrDefault(p.license)));
const newValue = previous !== undefined ? { ...previous.value } : {};
for (const license of licenses) {
@@ -207,7 +210,7 @@ function groupPackages(packages: TPackage[], keepLa
const licenses: Record> = {};
for (const p of packages) {
- const license = p.license ?? UnknownLicenseName;
+ const license = licenseNameOrDefault(p.license);
if (!licenses[license]) {
licenses[license] = {};
@@ -233,16 +236,22 @@ function groupPackages(packages: TPackage[], keepLa
: Object.values(licenses)
.map((l) => Object.keys(l).length)
.reduce((a, b) => a + b),
- licenses: Object.entries(licenses).map(([license, packages]) => ({
- license,
- packages: Object.entries(packages)
- .map(([name, packages]) => ({
- name,
- versions: [...new Set(packages.map((p) => p.version).filter((v) => v !== undefined && v !== null && v != ''))].sort((a, b) => -semver.compare(a, b)),
- latest: packages.sort((a, b) => -semver.compare(a.version, b.version))[0],
- packages,
- }))
- .sort((a, b) => a.name.localeCompare(b.name)),
- })),
+ licenses: Object.entries(licenses)
+ .map(([license, packages]) => ({
+ license,
+ packages: Object.entries(packages)
+ .map(([name, packages]) => ({
+ name,
+ versions: [...new Set(packages.map((p) => p.version).filter((v) => v !== undefined && v !== null && v != ''))].sort((a, b) => -semver.compare(a, b)),
+ latest: packages.sort((a, b) => -semver.compare(a.version, b.version))[0],
+ packages,
+ }))
+ .sort((a, b) => a.name.localeCompare(b.name)),
+ }))
+ .sort((a, b) => (a.license === '' && b.license === '' ? 0 : a.license === '' ? 1 : b.license === '' ? -1 : a.license.localeCompare(b.license))),
};
}
+
+function licenseNameOrDefault(name: string | undefined): string {
+ return name === undefined || name === '' ? '' : name;
+}
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/dependency.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/dependency.ts
index b7364ea1..ab5ac8d3 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/dependency.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/dependency.ts
@@ -1,4 +1,4 @@
-import { isSbomLicenseExpression, Sbom, SbomComponent, SbomLicense, SbomLicenseExpression } from '../../core/sbom';
+import { isSbomLicenseExpression, Sbom, SbomComponent, SbomExternalReference, SbomLicense } from '../../core/sbom';
export interface Dependency {
name: string;
@@ -17,23 +17,44 @@ export function extractDependenciesFromSbom(sbom: Sbom) {
description: component.description,
author: component.author,
version: component.version,
- license: getLicense(component.licenses?.[0]),
- link: component.externalReferences.find((r) => r.type === 'website')?.url,
+ license: getLicense(component.licenses),
+ link: getLink(component.externalReferences),
direct: isDirectDependency(sbom, component),
};
});
}
-function getLicense(license: SbomLicense | undefined): string | undefined {
- if (license === undefined) {
+function getLicense(licenses: SbomLicense[] | undefined): string | undefined {
+ if (licenses === undefined) {
return undefined;
}
- if (isSbomLicenseExpression(license)) {
- return license.expression;
+ const licenseNames = licenses.map((license) => {
+ if (isSbomLicenseExpression(license)) {
+ if (license.expression.startsWith('(') && license.expression.endsWith(')')) {
+ return license.expression.substring(1, license.expression.length - 2);
+ }
+
+ return license.expression;
+ }
+
+ return license.license.id;
+ });
+
+ return licenseNames.join(' OR ');
+}
+
+function getLink(externalReferences: SbomExternalReference[] | undefined): string | undefined {
+ if (externalReferences === undefined) {
+ return undefined;
+ }
+
+ const vcsLink = externalReferences.find((r) => r.type === 'vcs')?.url;
+ if (vcsLink !== undefined) {
+ return getRepositoryUrl(vcsLink);
}
- return license.license.id;
+ return externalReferences.find((r) => r.type === 'website')?.url;
}
function isDirectDependency(sbom: Sbom, component: SbomComponent): boolean {
@@ -45,3 +66,24 @@ function isDirectDependency(sbom: Sbom, component: SbomComponent): boolean {
return dependencies.dependsOn.includes(component['bom-ref']);
}
+
+const gitPlusUrlRegExp = new RegExp(/git\+(.*)\.git/);
+const gitSchemeUrlRegExp = new RegExp(/git:\/\/(.*)\.git/);
+
+function getRepositoryUrl(url: string): string {
+ if (url === undefined || url === null) {
+ return url;
+ }
+
+ const gitPlusUrlRegExpMatch = url.match(gitPlusUrlRegExp);
+ if (gitPlusUrlRegExpMatch !== null) {
+ return gitPlusUrlRegExpMatch[1];
+ }
+
+ const gitSchemeUrlRegExpMatch = url.match(gitSchemeUrlRegExp);
+ if (gitSchemeUrlRegExpMatch !== null) {
+ return `https://${gitSchemeUrlRegExpMatch[1]}`;
+ }
+
+ return url;
+}
From 85c109fc33f83622fb9747447fdfd881a8d908df Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 16:02:05 +0200
Subject: [PATCH 06/25] add download button
---
.../src/app/features/about/about-sbom.component.ts | 13 +++++++++++++
.../src/app/features/about/about.page.ts | 8 ++++----
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
index dcbd364f..07c23255 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
@@ -7,6 +7,7 @@ import MiniSearch from 'minisearch';
import { HighlightTextPipe } from '../../core/highlight-text/highlight-text.pipe';
import { Sbom } from '../../core/sbom';
import { Dependency, extractDependenciesFromSbom } from './dependency';
+import { downloadBlob, downloadFile } from '../../core/utils/download-blob';
@Component({
selector: 'app-about-licenses',
@@ -30,6 +31,9 @@ import { Dependency, extractDependenciesFromSbom } from './dependency';
+
+
+
@@ -106,6 +110,7 @@ import { Dependency, extractDependenciesFromSbom } from './dependency';
})
export class AboutSbomComponent {
sbom = input.required
();
+ sbomName = input();
protected dependencies = computed(() => extractDependenciesFromSbom(this.sbom()));
@@ -183,6 +188,14 @@ export class AboutSbomComponent {
}
}
+ protected downloadSbom() {
+ const sbom = this.sbom();
+ const name = this.sbomName();
+ const serialized = JSON.stringify(sbom);
+ const file = new File([serialized], name ?? 'sbom.json', { type: 'application/json' });
+ downloadFile(file);
+ }
+
protected search(term: string | undefined) {
if (term === undefined || term === '') {
this.searchTerm.set(undefined);
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
index 40204cae..a4688689 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
@@ -93,7 +93,7 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
-
+
API
@@ -145,14 +145,14 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
-
+
}
}
-
+
Web Editor
@@ -175,7 +175,7 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
-
+
From 0bbd6b3217c9ad6242e4fbc9bacb81203dd7d472 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 16:10:35 +0200
Subject: [PATCH 07/25] improve layout on small screens
---
.../src/app/features/about/about-sbom.component.ts | 13 ++++++-------
.../src/app/features/about/about.page.ts | 8 ++------
2 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
index 07c23255..21d1dbfd 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
@@ -13,18 +13,17 @@ import { downloadBlob, downloadFile } from '../../core/utils/download-blob';
selector: 'app-about-licenses',
imports: [NgTemplateOutlet, NgbTooltip, FormsModule, HighlightTextPipe],
template: `
-
+
Dependencies
+
-
- Direct dependencies ({{ directDependenciesCount() }})
-
+ Direct ({{ directDependenciesCount() }})
- All dependencies ({{ allDependenciesCount() }})
+ All ({{ allDependenciesCount() }})
-
+
@@ -37,7 +36,7 @@ import { downloadBlob, downloadFile } from '../../core/utils/download-blob';
-
+
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
index a4688689..3486aef1 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
@@ -93,7 +93,7 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
-
+
API
@@ -143,8 +143,6 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
}
-
-
}
}
@@ -152,7 +150,7 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
-
+
Web Editor
@@ -173,8 +171,6 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
on GitHub .
-
-
From ada8016edc28cacbc2f26d41373cb424f069c26d Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 16:19:53 +0200
Subject: [PATCH 08/25] format package descriptions as markdown
---
.../app/core/escape-html/escape-html.pipe.ts | 10 ++++++++++
.../features/about/about-sbom.component.ts | 20 ++++++++++++-------
2 files changed, 23 insertions(+), 7 deletions(-)
create mode 100644 src/FacturXDotNet.WebEditor/src/app/core/escape-html/escape-html.pipe.ts
diff --git a/src/FacturXDotNet.WebEditor/src/app/core/escape-html/escape-html.pipe.ts b/src/FacturXDotNet.WebEditor/src/app/core/escape-html/escape-html.pipe.ts
new file mode 100644
index 00000000..f917deb0
--- /dev/null
+++ b/src/FacturXDotNet.WebEditor/src/app/core/escape-html/escape-html.pipe.ts
@@ -0,0 +1,10 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'escapeHtml',
+})
+export class EscapeHtmlPipe implements PipeTransform {
+ transform(value: string): string {
+ return value.replaceAll('<', '<').replaceAll('>', '>');
+ }
+}
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
index 21d1dbfd..a1f1eaf1 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
@@ -8,10 +8,12 @@ import { HighlightTextPipe } from '../../core/highlight-text/highlight-text.pipe
import { Sbom } from '../../core/sbom';
import { Dependency, extractDependenciesFromSbom } from './dependency';
import { downloadBlob, downloadFile } from '../../core/utils/download-blob';
+import { MarkdownComponent } from 'ngx-markdown';
+import { EscapeHtmlPipe } from '../../core/escape-html/escape-html.pipe';
@Component({
selector: 'app-about-licenses',
- imports: [NgTemplateOutlet, NgbTooltip, FormsModule, HighlightTextPipe],
+ imports: [NgTemplateOutlet, NgbTooltip, FormsModule, HighlightTextPipe, MarkdownComponent, EscapeHtmlPipe],
template: `
Dependencies
@@ -72,19 +74,19 @@ import { downloadBlob, downloadFile } from '../../core/utils/download-blob';
@for (package_ of license.packages; track package_.name) {
-
+
@switch (package_.versions.length) {
@case (0) {}
@case (1) {
-
+
}
@default {
@for (version of package_.versions; track version) {
@if ($last) {
-
+
} @else {
- ,
+ ,
}
}
}
@@ -92,11 +94,15 @@ import { downloadBlob, downloadFile } from '../../core/utils/download-blob';
@if (package_.latest.author) {
-
-
+
}
@if (package_.latest.description) {
-
+
+
+ {{ package_.latest.description | escapeHtml | highlightText: package_.latest.terms }}
+
+
}
}
From 79377bc646e5228e6377326ccf3cb4fd4c470f66 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 19:38:44 +0200
Subject: [PATCH 09/25] improve error messages when api is unreachable or api
sbom is not found
---
.../src/app/core/api/api-errors.ts | 13 ++++++
.../api/services/api-constants.service.ts | 20 ++++-----
.../src/app/core/toasts/toast.service.ts | 26 ++++-------
.../features/about/about-sbom.component.ts | 4 +-
.../src/app/features/about/about.page.ts | 45 ++++++++++++++-----
.../src/app/features/editor/editor.page.ts | 42 ++++++++---------
6 files changed, 87 insertions(+), 63 deletions(-)
create mode 100644 src/FacturXDotNet.WebEditor/src/app/core/api/api-errors.ts
diff --git a/src/FacturXDotNet.WebEditor/src/app/core/api/api-errors.ts b/src/FacturXDotNet.WebEditor/src/app/core/api/api-errors.ts
new file mode 100644
index 00000000..1dbd030e
--- /dev/null
+++ b/src/FacturXDotNet.WebEditor/src/app/core/api/api-errors.ts
@@ -0,0 +1,13 @@
+import { HttpErrorResponse } from '@angular/common/http';
+
+export function getApiErrorMessage(err: unknown): string {
+ if (err instanceof HttpErrorResponse) {
+ if (err.status === 0) {
+ return "Can't connect to server. Please check your internet connection.";
+ }
+
+ return err.message;
+ }
+
+ return 'Unknown error';
+}
diff --git a/src/FacturXDotNet.WebEditor/src/app/core/api/services/api-constants.service.ts b/src/FacturXDotNet.WebEditor/src/app/core/api/services/api-constants.service.ts
index bd20d9f2..7e31579b 100644
--- a/src/FacturXDotNet.WebEditor/src/app/core/api/services/api-constants.service.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/core/api/services/api-constants.service.ts
@@ -1,7 +1,7 @@
import { inject, Injectable } from '@angular/core';
import { InfoApi } from '../info.api';
import { rxResource } from '@angular/core/rxjs-interop';
-import { forkJoin, map, switchMap } from 'rxjs';
+import { map, switchMap } from 'rxjs';
import { Sbom } from '../../sbom';
@Injectable({
@@ -10,17 +10,13 @@ import { Sbom } from '../../sbom';
export class ApiConstantsService {
private infoApi = inject(InfoApi);
- info = rxResource({
+ buildInfo = rxResource({ loader: () => this.infoApi.getBuildInformation() });
+ hostingInfo = rxResource({ loader: () => this.infoApi.getHostingInformation() });
+ sbom = rxResource({
loader: () =>
- forkJoin({
- build: this.infoApi.getBuildInformation(),
- hosting: this.infoApi.getHostingInformation(),
- sbom: this.infoApi.getSbom().pipe(
- switchMap((sbomFile) => {
- return sbomFile.text();
- }),
- map((sbomContent) => JSON.parse(sbomContent) as Sbom),
- ),
- }),
+ this.infoApi.getSbom().pipe(
+ switchMap((sbomFile) => sbomFile.text()),
+ map((sbomContent) => JSON.parse(sbomContent) as Sbom),
+ ),
});
}
diff --git a/src/FacturXDotNet.WebEditor/src/app/core/toasts/toast.service.ts b/src/FacturXDotNet.WebEditor/src/app/core/toasts/toast.service.ts
index dc9070c1..e895406e 100644
--- a/src/FacturXDotNet.WebEditor/src/app/core/toasts/toast.service.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/core/toasts/toast.service.ts
@@ -1,6 +1,6 @@
-import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of, Subject } from 'rxjs';
+import { getApiErrorMessage } from '../api/api-errors';
@Injectable({
providedIn: 'root',
@@ -24,6 +24,14 @@ export class ToastService {
}
}
+function getErrorMessage(error: unknown) {
+ if (error instanceof Error) {
+ return error.message;
+ }
+
+ return getApiErrorMessage(error);
+}
+
export type Toast = SuccessToast | InfoToast | ErrorToast;
export type ToastInstance = Toast & {
@@ -46,19 +54,3 @@ export interface InfoToast extends BaseToast {
export interface ErrorToast extends BaseToast {
readonly type: 'error';
}
-
-function getErrorMessage(err: unknown): string {
- if (err instanceof Error) {
- return err.message;
- }
-
- if (err instanceof HttpErrorResponse) {
- if (err.status === 0) {
- return "Can't connect to server. Please check your internet connection.";
- }
-
- return `${err.status} ${err.statusText} - ${err.message}`;
- }
-
- return 'Unknown error';
-}
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
index a1f1eaf1..9ffe6348 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about-sbom.component.ts
@@ -7,7 +7,7 @@ import MiniSearch from 'minisearch';
import { HighlightTextPipe } from '../../core/highlight-text/highlight-text.pipe';
import { Sbom } from '../../core/sbom';
import { Dependency, extractDependenciesFromSbom } from './dependency';
-import { downloadBlob, downloadFile } from '../../core/utils/download-blob';
+import { downloadFile } from '../../core/utils/download-blob';
import { MarkdownComponent } from 'ngx-markdown';
import { EscapeHtmlPipe } from '../../core/escape-html/escape-html.pipe';
@@ -15,7 +15,7 @@ import { EscapeHtmlPipe } from '../../core/escape-html/escape-html.pipe';
selector: 'app-about-licenses',
imports: [NgTemplateOutlet, NgbTooltip, FormsModule, HighlightTextPipe, MarkdownComponent, EscapeHtmlPipe],
template: `
- Dependencies
+ Dependencies
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts b/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
index 3486aef1..e0488fbf 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/about/about.page.ts
@@ -7,6 +7,7 @@ import { API_BASE_URL } from '../../app.config';
import sbom from '../../../dependencies/sbom.json';
import { ApiServerStatusComponent } from '../../core/api/components/api-server-status.component';
import { ApiConstantsService } from '../../core/api/services/api-constants.service';
+import { getApiErrorMessage } from '../../core/api/api-errors';
@Component({
selector: 'app-about',
@@ -105,7 +106,7 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
- @if (apiConstants.isLoading()) {
+ @if (buildInfo.isLoading()) {
@@ -132,18 +133,33 @@ import { ApiConstantsService } from '../../core/api/services/api-constants.servi
} @else {
- @if (apiConstants.value(); as apiConstants) {
+ @if (buildInfo.value(); as buildInfo) {
The API server is currently in version {{ apiVersion() }} and was built on
- {{ apiConstants.build.buildDate | date }} {{ buildInfo.buildDate | date }}.
@if (apiVersionBuildMetadata(); as apiVersionBuildMetadata) {
Build metadata : {{ apiVersionBuildMetadata }}
}
+ }
-
+ @if (sbom.isLoading()) {
+
+ Loading...
+
+ } @else if (sbom.error()) {
+
+
Failed to load dependencies:
+
+ {{ getApiErrorMessage(sbom.error()) }}
+
+
+ } @else {
+ @if (sbom.value(); as sbom) {
+
+ }
}
}
@@ -190,25 +206,32 @@ export class AboutPage {
protected apiUrl = inject(API_BASE_URL);
private apiConstantsService = inject(ApiConstantsService);
+
+ protected buildInfo = this.apiConstantsService.buildInfo;
+
protected apiVersion = computed(() => {
- const apiConstants = this.apiConstantsService.info.value();
- if (apiConstants === undefined) {
+ const buildInfo = this.buildInfo.value();
+ if (buildInfo === undefined) {
return undefined;
}
- return removeBuildInformation(apiConstants.build.version);
+ return removeBuildInformation(buildInfo.version);
});
protected apiVersionBuildMetadata = computed(() => {
- const apiConstants = this.apiConstantsService.info.value();
- if (apiConstants === undefined) {
+ const buildInfo = this.buildInfo.value();
+ if (buildInfo === undefined) {
return undefined;
}
- return extractBuildInformation(apiConstants.build.version);
+ return extractBuildInformation(buildInfo.version);
});
- protected apiConstants = this.apiConstantsService.info;
+ protected sbom = this.apiConstantsService.sbom;
+
+ protected getApiErrorMessage(error: unknown) {
+ return getApiErrorMessage(error);
+ }
}
function extractBuildInformation(version: string) {
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.page.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.page.ts
index cfd39047..ea843c21 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.page.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.page.ts
@@ -1,21 +1,21 @@
-import {Component, computed, effect, HostListener, inject, linkedSignal, Resource, signal, Signal} from '@angular/core';
-import {NgOptimizedImage} from '@angular/common';
-import {EditorSettings, EditorSettingsService, PdfModel} from './editor-settings.service';
-import {EditorMenuComponent} from './components/editor-menu/editor-menu.component';
-import {FormsModule} from '@angular/forms';
-import {TwoColumnsComponent} from '../../core/two-columns/two-columns.component';
-import {EditorSavedState, EditorStateService} from './editor-state.service';
-import {EditorLeftPaneHeaderComponent} from './components/editor-header/editor-left-pane-header.component';
-import {EditorWelcomeComponent} from './editor-welcome.component';
-import {API_BASE_URL} from '../../app.config';
-import {ApiServerStatusComponent} from '../../core/api/components/api-server-status.component';
-import {ApiConstantsService} from '../../core/api/services/api-constants.service';
-import {EditorMenuService} from './components/editor-menu/editor-menu.service';
-import {RouterOutlet} from '@angular/router';
-import {EditorPdfViewerComponent} from './components/editor-pdf-viewer/editor-pdf-viewer.component';
-import {EditorHeaderNameComponent} from './components/editor-header/editor-header-name.component';
-import {EditorRightPaneHeaderComponent} from './components/editor-header/editor-right-pane-header.component';
-import {EditorResponsivenessService} from './editor-responsiveness.service';
+import { Component, computed, effect, HostListener, inject, linkedSignal, Resource, signal, Signal } from '@angular/core';
+import { NgOptimizedImage } from '@angular/common';
+import { EditorSettings, EditorSettingsService, PdfModel } from './editor-settings.service';
+import { EditorMenuComponent } from './components/editor-menu/editor-menu.component';
+import { FormsModule } from '@angular/forms';
+import { TwoColumnsComponent } from '../../core/two-columns/two-columns.component';
+import { EditorSavedState, EditorStateService } from './editor-state.service';
+import { EditorLeftPaneHeaderComponent } from './components/editor-header/editor-left-pane-header.component';
+import { EditorWelcomeComponent } from './editor-welcome.component';
+import { API_BASE_URL } from '../../app.config';
+import { ApiServerStatusComponent } from '../../core/api/components/api-server-status.component';
+import { ApiConstantsService } from '../../core/api/services/api-constants.service';
+import { EditorMenuService } from './components/editor-menu/editor-menu.service';
+import { RouterOutlet } from '@angular/router';
+import { EditorPdfViewerComponent } from './components/editor-pdf-viewer/editor-pdf-viewer.component';
+import { EditorHeaderNameComponent } from './components/editor-header/editor-header-name.component';
+import { EditorRightPaneHeaderComponent } from './components/editor-header/editor-right-pane-header.component';
+import { EditorResponsivenessService } from './editor-responsiveness.service';
@Component({
selector: 'app-editor',
@@ -131,8 +131,8 @@ import {EditorResponsivenessService} from './editor-responsiveness.service';
- © 2025 Ismail Bennani , made with and . The tools are open source and released under the MIT
- License, feel free to use, modify, and share.
+ © 2025 Ismail Bennani , made with and . The tools are open source and released under the
+ MIT License, feel free to use, modify, and share.
@@ -160,7 +160,7 @@ export class EditorPage {
},
});
private apiConstantsService = inject(ApiConstantsService);
- protected unsafeEnvironment = computed(() => this.apiConstantsService.info.value()?.hosting.unsafeEnvironment ?? false);
+ protected unsafeEnvironment = computed(() => this.apiConstantsService.hostingInfo.value()?.unsafeEnvironment ?? false);
private editorStateService = inject(EditorStateService);
protected state: Resource
= this.editorStateService.savedState;
private editorMenuService = inject(EditorMenuService);
From 59b2e32e9c3d7a40d0e1e9c0123a953e3fe30727 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 19:40:48 +0200
Subject: [PATCH 10/25] use cyclonedx to generate license file in api CI
---
.../reusable-build-publish-api-docker-image.yml | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/reusable-build-publish-api-docker-image.yml b/.github/workflows/reusable-build-publish-api-docker-image.yml
index b510045d..02e0ac98 100644
--- a/.github/workflows/reusable-build-publish-api-docker-image.yml
+++ b/.github/workflows/reusable-build-publish-api-docker-image.yml
@@ -48,16 +48,11 @@ jobs:
with:
fetch-depth: 0
- - name: Setup .NET 6 (for dotnet-project-licenses)
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 6.0.x
-
- name: Install dotnet-project-licenses
- run: dotnet tool install -g dotnet-project-licenses --framework net6.0
+ run: dotnet tool install --global CycloneDX
- name: Generate licenses file
- run: cd src/FacturXDotNet.API; dotnet-project-licenses -i dotnet-project-licenses-input.json --json --output-directory Resources
+ run: cd src/FacturXDotNet.API; dotnet-CycloneDX FacturXDotNet.API.csproj -o Resources --json -fn api.bom.json
- name: Build the Docker image
run: cd src; docker build . --file FacturXDotNet.API/Dockerfile --tag facturxdotnet-api --label "runnumber=${GITHUB_RUN_ID}" --build-arg VERSION=${{ inputs.version }}
From 8488ff29fe4dff69ee4a02da386cb9d7757533f5 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 19:43:18 +0200
Subject: [PATCH 11/25] cleanup
---
src/FacturXDotNet.WebEditor/src/app/core/api/api.models.ts | 1 +
src/FacturXDotNet.WebEditor/src/app/core/api/info.api.ts | 2 +-
.../src/app/core/global-overlay/global-overlay.component.ts | 2 +-
.../editor-header/editor-left-pane-header.component.ts | 2 +-
.../components/editor-menu/editor-export-menu.component.ts | 2 +-
.../components/editor-settings-general-form.component.ts | 2 +-
...-settings-language-pack-document-types-form.component.ts | 2 +-
.../editor-settings-language-pack-form.component.ts | 2 +-
.../editor-settings-pdf-profile-form.component.ts | 6 +++---
.../editor-settings-pdf-profile-create.tab.ts | 2 +-
.../editor-settings-pdf-profile-edit.tab.ts | 2 +-
.../src/app/features/editor/editor.layout.ts | 4 +---
.../src/app/features/editor/editor.page.ts | 1 -
13 files changed, 14 insertions(+), 16 deletions(-)
diff --git a/src/FacturXDotNet.WebEditor/src/app/core/api/api.models.ts b/src/FacturXDotNet.WebEditor/src/app/core/api/api.models.ts
index 588a0d4a..535bf425 100644
--- a/src/FacturXDotNet.WebEditor/src/app/core/api/api.models.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/core/api/api.models.ts
@@ -6,6 +6,7 @@
/* tslint:disable */
/* eslint-disable */
+
// ReSharper disable InconsistentNaming
export class ApplicableHeaderTradeAgreement implements IApplicableHeaderTradeAgreement {
diff --git a/src/FacturXDotNet.WebEditor/src/app/core/api/info.api.ts b/src/FacturXDotNet.WebEditor/src/app/core/api/info.api.ts
index 18410c3c..5758c19a 100644
--- a/src/FacturXDotNet.WebEditor/src/app/core/api/info.api.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/core/api/info.api.ts
@@ -2,7 +2,7 @@ import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, Observable } from 'rxjs';
import { API_BASE_URL } from '../../app.config';
-import { IBuildInformationDto, IHostingInformationDto, IPackageDto } from './api.models';
+import { IBuildInformationDto, IHostingInformationDto } from './api.models';
@Injectable({
providedIn: 'root',
diff --git a/src/FacturXDotNet.WebEditor/src/app/core/global-overlay/global-overlay.component.ts b/src/FacturXDotNet.WebEditor/src/app/core/global-overlay/global-overlay.component.ts
index aa54a2f8..3008cbb9 100644
--- a/src/FacturXDotNet.WebEditor/src/app/core/global-overlay/global-overlay.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/core/global-overlay/global-overlay.component.ts
@@ -1,4 +1,4 @@
-import { Component, inject, signal } from '@angular/core';
+import { Component, inject } from '@angular/core';
import { GlobalOverlayService } from './global-overlay.service';
@Component({
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/components/editor-header/editor-left-pane-header.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/components/editor-header/editor-left-pane-header.component.ts
index d5a09f9e..e61ec41e 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/components/editor-header/editor-left-pane-header.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/components/editor-header/editor-left-pane-header.component.ts
@@ -6,7 +6,7 @@ import { NgbNav, NgbNavItem, NgbNavLink, NgbNavLinkBase } from '@ng-bootstrap/ng
import { FormsModule } from '@angular/forms';
import { EditorResponsivenessService } from '../../services/editor-responsiveness.service';
import { toSignal } from '@angular/core/rxjs-interop';
-import { distinct, distinctUntilChanged, filter, map } from 'rxjs';
+import { distinctUntilChanged, filter, map } from 'rxjs';
import { EditorHeaderNameComponent } from './editor-header-name.component';
@Component({
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/components/editor-menu/editor-export-menu.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/components/editor-menu/editor-export-menu.component.ts
index 83056b69..592b934f 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/components/editor-menu/editor-export-menu.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/components/editor-menu/editor-export-menu.component.ts
@@ -1,4 +1,4 @@
-import { Component, DestroyRef, inject, output } from '@angular/core';
+import { Component, inject, output } from '@angular/core';
import { ToastService } from '../../../../core/toasts/toast.service';
import { EditorMenuService } from './editor-menu.service';
import { NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap';
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-general-form.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-general-form.component.ts
index f3fe0674..ac0e14e3 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-general-form.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-general-form.component.ts
@@ -1,5 +1,5 @@
import { Component, inject, input } from '@angular/core';
-import { ControlContainer, FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ControlContainer, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { ImportFileService } from '../../../../../../../core/import-file/import-file.service';
@Component({
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-language-pack-document-types-form.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-language-pack-document-types-form.component.ts
index 90e1f905..e21bf444 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-language-pack-document-types-form.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-language-pack-document-types-form.component.ts
@@ -1,5 +1,5 @@
import { Component, inject, input } from '@angular/core';
-import { ControlContainer, FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ControlContainer, ReactiveFormsModule } from '@angular/forms';
import { IStandardPdfGeneratorLanguagePackDto } from '../../../../../../../core/api/api.models';
@Component({
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-language-pack-form.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-language-pack-form.component.ts
index 60c183e1..ec767a9a 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-language-pack-form.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-language-pack-form.component.ts
@@ -1,5 +1,5 @@
import { Component, inject, input } from '@angular/core';
-import { ControlContainer, FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ControlContainer, ReactiveFormsModule } from '@angular/forms';
import { IStandardPdfGeneratorLanguagePackDto } from '../../../../../../../core/api/api.models';
@Component({
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-pdf-profile-form.component.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-pdf-profile-form.component.ts
index f57e43c4..c120e5d5 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-pdf-profile-form.component.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/components/editor-settings-pdf-profile-form.component.ts
@@ -1,8 +1,8 @@
-import { Component, computed, DestroyRef, inject, input, linkedSignal, Signal, TemplateRef, WritableSignal } from '@angular/core';
+import { Component, computed, inject, input, Signal, TemplateRef } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
-import { rxResource, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
+import { rxResource, toSignal } from '@angular/core/rxjs-interop';
import { GenerateApi } from '../../../../../../../core/api/generate.api';
-import { delay, distinctUntilChanged, map, startWith } from 'rxjs';
+import { distinctUntilChanged, map } from 'rxjs';
import { IStandardPdfGeneratorLanguagePackDto } from '../../../../../../../core/api/api.models';
import { EditorPdfGenerationProfileData } from '../../../../../services/editor-pdf-generation-profiles.service';
import { EditorSettingsLanguagePackDocumentTypesFormComponent } from './editor-settings-language-pack-document-types-form.component';
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/editor-settings-pdf-profile-create.tab.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/editor-settings-pdf-profile-create.tab.ts
index f43f7075..3711a1db 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/editor-settings-pdf-profile-create.tab.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/editor-settings-pdf-profile-create.tab.ts
@@ -1,7 +1,7 @@
import { Component, inject, viewChild } from '@angular/core';
import { Router, RouterLink } from '@angular/router';
import { EditorSettingsPdfProfileFormComponent } from './components/editor-settings-pdf-profile-form.component';
-import { EditorPdfGenerationProfileData, EditorPdfGenerationProfilesService } from '../../../../services/editor-pdf-generation-profiles.service';
+import { EditorPdfGenerationProfilesService } from '../../../../services/editor-pdf-generation-profiles.service';
import { ToastService } from '../../../../../../core/toasts/toast.service';
import { EditorPdfViewerService } from '../../../editor-pdf-viewer/editor-pdf-viewer.service';
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/editor-settings-pdf-profile-edit.tab.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/editor-settings-pdf-profile-edit.tab.ts
index 81240b5a..f41fbb1d 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/editor-settings-pdf-profile-edit.tab.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor-tabs/editor-settings/editor-settings-tabs/editor-settings-pdf-profiles/editor-settings-pdf-profile-edit.tab.ts
@@ -1,7 +1,7 @@
import { Component, computed, effect, inject, input, viewChild } from '@angular/core';
import { EditorSettingsPdfProfileFormComponent } from './components/editor-settings-pdf-profile-form.component';
import { Router, RouterLink } from '@angular/router';
-import { EditorPdfGenerationProfile, EditorPdfGenerationProfileData, EditorPdfGenerationProfilesService } from '../../../../services/editor-pdf-generation-profiles.service';
+import { EditorPdfGenerationProfilesService } from '../../../../services/editor-pdf-generation-profiles.service';
import { ToastService } from '../../../../../../core/toasts/toast.service';
import { EditorPdfViewerService } from '../../../editor-pdf-viewer/editor-pdf-viewer.service';
import { NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap';
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.layout.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.layout.ts
index bb73ff07..71ac2c68 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.layout.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.layout.ts
@@ -1,9 +1,7 @@
-import { Component, computed, effect, HostListener, inject, linkedSignal, Resource, signal, Signal } from '@angular/core';
+import { Component, computed, effect, inject } from '@angular/core';
import { NgOptimizedImage } from '@angular/common';
-import { EditorSettings, EditorSettingsService, PdfModel } from './services/editor-settings.service';
import { EditorMenuComponent } from './components/editor-menu/editor-menu.component';
import { FormsModule } from '@angular/forms';
-import { EditorSavedState, EditorStateService } from './services/editor-state.service';
import { API_BASE_URL } from '../../app.config';
import { ApiServerStatusComponent } from '../../core/api/components/api-server-status.component';
import { ApiConstantsService } from '../../core/api/services/api-constants.service';
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.page.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.page.ts
index d9ce5598..7a8bf06b 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.page.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.page.ts
@@ -2,7 +2,6 @@ import { Component, computed, effect, HostListener, inject, linkedSignal, Resour
import { EditorLeftPaneHeaderComponent } from './components/editor-header/editor-left-pane-header.component';
import { EditorPdfViewerComponent } from './editor-tabs/editor-pdf-viewer/editor-pdf-viewer.component';
import { EditorRightPaneHeaderComponent } from './components/editor-header/editor-right-pane-header.component';
-import { EditorWelcomePage } from './editor-welcome.page';
import { Router, RouterOutlet } from '@angular/router';
import { TwoColumnsComponent } from '../../core/two-columns/two-columns.component';
import { EditorSavedState, EditorStateService } from './services/editor-state.service';
From 9fbfeac1efa74a6361ccebbe0b35a5f9547d2af4 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 19:44:38 +0200
Subject: [PATCH 12/25] fix build
---
.../src/app/features/editor/editor.layout.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.layout.ts b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.layout.ts
index 71ac2c68..3ef93e7e 100644
--- a/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.layout.ts
+++ b/src/FacturXDotNet.WebEditor/src/app/features/editor/editor.layout.ts
@@ -66,7 +66,7 @@ export class EditorLayout {
protected apiUrl = inject(API_BASE_URL);
private apiConstantsService = inject(ApiConstantsService);
- protected unsafeEnvironment = computed(() => this.apiConstantsService.info.value()?.hosting.unsafeEnvironment ?? false);
+ protected unsafeEnvironment = computed(() => this.apiConstantsService.hostingInfo.value()?.unsafeEnvironment ?? false);
private globalOverlayService = inject(GlobalOverlayService);
private editorMenuService = inject(EditorMenuService);
From 02f2a118bf2f9c0b6b694507a931a28c2c92d9c1 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 19:45:42 +0200
Subject: [PATCH 13/25] fix CI
---
.github/workflows/reusable-build-publish-api-docker-image.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/reusable-build-publish-api-docker-image.yml b/.github/workflows/reusable-build-publish-api-docker-image.yml
index 02e0ac98..0ed59d9d 100644
--- a/.github/workflows/reusable-build-publish-api-docker-image.yml
+++ b/.github/workflows/reusable-build-publish-api-docker-image.yml
@@ -49,7 +49,7 @@ jobs:
fetch-depth: 0
- name: Install dotnet-project-licenses
- run: dotnet tool install --global CycloneDX
+ run: dotnet tool install --global CycloneDX --framework net8.0
- name: Generate licenses file
run: cd src/FacturXDotNet.API; dotnet-CycloneDX FacturXDotNet.API.csproj -o Resources --json -fn api.bom.json
From 250bc308b251dbec295e5e04b028f50d22b2fa13 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 20:01:08 +0200
Subject: [PATCH 14/25] generate correct sbom files for docs
---
.../reusable-build-docs-application.yml | 61 +++++--------------
...eusable-build-publish-api-docker-image.yml | 4 +-
2 files changed, 16 insertions(+), 49 deletions(-)
diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml
index af841d0f..8c5c90ee 100644
--- a/.github/workflows/reusable-build-docs-application.yml
+++ b/.github/workflows/reusable-build-docs-application.yml
@@ -70,8 +70,8 @@ jobs:
- name: Install dfmg
run: dotnet tool install -g DocFxMarkdownGen
- - name: Install dotnet-project-licenses
- run: dotnet tool install -g dotnet-project-licenses
+ - name: Install CycloneDX
+ run: dotnet tool install --global CycloneDX --framework net8.0
- name: Generate .NET API reference
run: |
@@ -155,53 +155,20 @@ jobs:
- name: Install dependencies
run: cd docs; npm ci
- - name: Install dependencies in editor (for license-report)
- run: cd src/FacturXDotNet.WebEditor; npm ci
+ - name: Generate Editor SBOM
+ run: cd src/FacturXDotNet.WebEditor; npm sbom --sbom-format cyclonedx > ../../docs/src/assets/editor.sbom.json
- - name: Install license-report
- run: npm i -g license-report
+ - name: Generate Docs SBOM
+ run: cd docs; npm sbom --sbom-format cyclonedx > src/assets/docs.sbom.json
- - name: Generate licenses
- run: |
- license-report --config docs/license-report-config.json --package src/FacturXDotNet.WebEditor/package.json > docs/src/assets/editor-licenses.json
-
- echo
- echo docs/src/assets/editor-licenses.json
- cat docs/src/assets/editor-licenses.json
- echo
- echo
-
- license-report --config docs/license-report-config.json --package docs/package.json > docs/src/assets/docs-licenses.json
-
- echo
- echo docs/src/assets/docs-licenses.json
- cat docs/src/assets/docs-licenses.json
- echo
- echo
-
- dotnet-project-licenses -i src/FacturXDotNet/FacturXDotNet.csproj --json --output-directory docs/src/assets --outfile library-licenses.json
-
- echo
- echo docs/src/assets/library-licenses.json
- cat docs/src/assets/library-licenses.json
- echo
- echo
-
- dotnet-project-licenses -i src/FacturXDotNet.API/FacturXDotNet.API.csproj --json --output-directory docs/src/assets --outfile api-licenses.json
-
- echo
- echo docs/src/assets/api-licenses.json
- cat docs/src/assets/api-licenses.json
- echo
- echo
-
- dotnet-project-licenses -i src/FacturXDotNet.CLI/FacturXDotNet.CLI.csproj --json --output-directory docs/src/assets --outfile cli-licenses.json
-
- echo
- echo docs/src/assets/cli-licenses.json
- cat docs/src/assets/cli-licenses.json
- echo
- echo
+ - name: Generate API SBOM
+ run: cd src/FacturXDotNet.API dotnet-CycloneDX FacturXDotNet.API.csproj -o ../../docs/src/assets --json -fn api.bom.json
+
+ - name: Generate CLI SBOM
+ run: cd src/FacturXDotNet.CLI dotnet-CycloneDX FacturXDotNet.API.csproj -o ../../docs/src/assets --json -fn cli.bom.json
+
+ - name: Generate Library SBOM
+ run: cd src/FacturXDotNet dotnet-CycloneDX FacturXDotNet.API.csproj -o ../../docs/src/assets --json -fn library.bom.json
- name: Write env.json
run: |
diff --git a/.github/workflows/reusable-build-publish-api-docker-image.yml b/.github/workflows/reusable-build-publish-api-docker-image.yml
index 0ed59d9d..eeb725fe 100644
--- a/.github/workflows/reusable-build-publish-api-docker-image.yml
+++ b/.github/workflows/reusable-build-publish-api-docker-image.yml
@@ -48,7 +48,7 @@ jobs:
with:
fetch-depth: 0
- - name: Install dotnet-project-licenses
+ - name: Install CycloneDX
run: dotnet tool install --global CycloneDX --framework net8.0
- name: Generate licenses file
@@ -83,7 +83,7 @@ jobs:
package-name: facturxdotnet-api
tag: ${{ inputs.tag }}
github-token: ${{ secrets.GITHUB_TOKEN }}
-
+
- name: Push ${{ inputs.tag }}
if: inputs.tag != ''
run: |
From e9718f32f324bcff4930633807908944e2ad1b11 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 20:06:12 +0200
Subject: [PATCH 15/25] fix workflow
---
.github/workflows/reusable-build-docs-application.yml | 9 ++-------
.../reusable-build-publish-api-docker-image.yml | 5 +++++
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml
index 8c5c90ee..5af65704 100644
--- a/.github/workflows/reusable-build-docs-application.yml
+++ b/.github/workflows/reusable-build-docs-application.yml
@@ -38,16 +38,11 @@ jobs:
with:
fetch-depth: 0
- - name: Setup .NET 9 (for lib and api)
+ - name: Setup .NET 9 (for CycloneDX, lib and api)
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
- - name: Setup .NET 7 (for dotnet-project-licenses)
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 7.0.x
-
- name: Setup .NET 6 (for dfmg)
uses: actions/setup-dotnet@v4
with:
@@ -176,7 +171,7 @@ jobs:
echo ' "buildName": "${{ inputs.build-name }}",' >> docs/src/env.json
echo ' "version": "${{ inputs.version }}",' >> docs/src/env.json
echo ' "editor": {' >> docs/src/env.json
- echo ' "url": "${{ inputs.editor-url }}"' >> docs/src/env.json
+ echo ' "url": "${{ inputs.editor-url }}"' >> docs/src/env.json
echo ' },' >> docs/src/env.json
echo ' "api": {' >> docs/src/env.json
echo ' "url": "${{ inputs.api-url }}"' >> docs/src/env.json
diff --git a/.github/workflows/reusable-build-publish-api-docker-image.yml b/.github/workflows/reusable-build-publish-api-docker-image.yml
index eeb725fe..10326c65 100644
--- a/.github/workflows/reusable-build-publish-api-docker-image.yml
+++ b/.github/workflows/reusable-build-publish-api-docker-image.yml
@@ -48,6 +48,11 @@ jobs:
with:
fetch-depth: 0
+ - name: Setup .NET 9 (for CycloneDX)
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.x
+
- name: Install CycloneDX
run: dotnet tool install --global CycloneDX --framework net8.0
From 2ef3ca8b33c85f3fa80a39d9b5bda74c6d5c01d9 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 20:07:20 +0200
Subject: [PATCH 16/25] fix workflow
---
.github/workflows/reusable-build-docs-application.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml
index 5af65704..273e8f99 100644
--- a/.github/workflows/reusable-build-docs-application.yml
+++ b/.github/workflows/reusable-build-docs-application.yml
@@ -150,6 +150,9 @@ jobs:
- name: Install dependencies
run: cd docs; npm ci
+ - name: Install dependencies in editor (for sbom generation)
+ run: cd src/FacturXDotNet.WebEditor; npm ci
+
- name: Generate Editor SBOM
run: cd src/FacturXDotNet.WebEditor; npm sbom --sbom-format cyclonedx > ../../docs/src/assets/editor.sbom.json
From 7c1b26ba6aec67e7e4b259d6b92896611f0caaab Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 20:30:59 +0200
Subject: [PATCH 17/25] use sbom in docs
---
.../reusable-build-docs-application.yml | 10 +-
docs/.gitignore | 10 +-
docs/license-report-config.json | 5 -
docs/package.json | 2 +
docs/src/dependencies.data.ts | 252 +++++++++++-------
.../.idea.FacturXDotNet/.idea/encodings.xml | 7 +
6 files changed, 179 insertions(+), 107 deletions(-)
delete mode 100644 docs/license-report-config.json
create mode 100644 src/.idea/.idea.FacturXDotNet/.idea/encodings.xml
diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml
index 273e8f99..eb82d0cb 100644
--- a/.github/workflows/reusable-build-docs-application.yml
+++ b/.github/workflows/reusable-build-docs-application.yml
@@ -154,19 +154,19 @@ jobs:
run: cd src/FacturXDotNet.WebEditor; npm ci
- name: Generate Editor SBOM
- run: cd src/FacturXDotNet.WebEditor; npm sbom --sbom-format cyclonedx > ../../docs/src/assets/editor.sbom.json
+ run: cd src/FacturXDotNet.WebEditor; npm sbom --sbom-format cyclonedx > ../../docs/src/assets/editor.bom.json
- name: Generate Docs SBOM
- run: cd docs; npm sbom --sbom-format cyclonedx > src/assets/docs.sbom.json
+ run: cd docs; npm sbom --sbom-format cyclonedx > src/assets/docs.bom.json
- name: Generate API SBOM
- run: cd src/FacturXDotNet.API dotnet-CycloneDX FacturXDotNet.API.csproj -o ../../docs/src/assets --json -fn api.bom.json
+ run: cd src/FacturXDotNet.API; dotnet-CycloneDX FacturXDotNet.API.csproj -o ../../docs/src/assets --json -fn api.bom.json
- name: Generate CLI SBOM
- run: cd src/FacturXDotNet.CLI dotnet-CycloneDX FacturXDotNet.API.csproj -o ../../docs/src/assets --json -fn cli.bom.json
+ run: cd src/FacturXDotNet.CLI; dotnet-CycloneDX FacturXDotNet.CLI.csproj -o ../../docs/src/assets --json -fn cli.bom.json
- name: Generate Library SBOM
- run: cd src/FacturXDotNet dotnet-CycloneDX FacturXDotNet.API.csproj -o ../../docs/src/assets --json -fn library.bom.json
+ run: cd src/FacturXDotNet; dotnet-CycloneDX FacturXDotNet.csproj -o ../../docs/src/assets --json -fn library.bom.json
- name: Write env.json
run: |
diff --git a/docs/.gitignore b/docs/.gitignore
index 68157175..45286a4a 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -62,8 +62,8 @@ TODOs.md
src/api-reference/**
src/cli/**
src/assets/facturxdotnet.openapi.json
-src/assets/docs-licenses.json
-src/assets/editor-licenses.json
-src/assets/api-licenses.json
-src/assets/cli-licenses.json
-src/assets/library-licenses.json
+src/assets/docs.bom.json
+src/assets/editor.bom.json
+src/assets/api.bom.json
+src/assets/cli.bom.json
+src/assets/library.bom.json
diff --git a/docs/license-report-config.json b/docs/license-report-config.json
deleted file mode 100644
index b4ccc78c..00000000
--- a/docs/license-report-config.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "output": "json",
- "only": "prod,dev,opt,peer",
- "fields": ["name", "author", "installedVersion", "licenseType", "link"]
-}
diff --git a/docs/package.json b/docs/package.json
index d26ecc87..2a38078d 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -1,4 +1,6 @@
{
+ "name": "facturxdotnet-docs",
+ "version": "0.0.0.0",
"type": "module",
"scripts": {
"dev": "vitepress dev --port 40850",
diff --git a/docs/src/dependencies.data.ts b/docs/src/dependencies.data.ts
index 6c59f222..58deef47 100644
--- a/docs/src/dependencies.data.ts
+++ b/docs/src/dependencies.data.ts
@@ -1,57 +1,75 @@
import * as fs from "node:fs";
export default {
+ watch: [
+ "src/assets/docs.bom.json",
+ "src/assets/editor.bom.json",
+ "src/assets/api.bom.json",
+ "src/assets/cli.bom.json",
+ "src/assets/library.bom.json",
+ ],
load(): Dependencies {
- return {
- docs: groupDependenciesByLicense([
- ...loadDependenciesFromLicenseReportOutput(
- "src/assets/docs-licenses.json",
+ const docsSbom = loadSbom("src/assets/docs.bom.json");
+ const editorSbom = loadSbom("src/assets/editor.bom.json");
+ const apiSbom = loadSbom("src/assets/api.bom.json");
+ const cliSbom = loadSbom("src/assets/cli.bom.json");
+ const librarySbom = loadSbom("src/assets/library.bom.json");
+
+ const result: Dependencies = {
+ docs: {
+ sbom: docsSbom,
+ licenses: groupDependenciesByLicense([
+ ...loadDependenciesFromSbom(docsSbom),
+ {
+ name: "docfx",
+ author: ".NET Foundation and Contributors",
+ version: "2.78.3",
+ license: "MIT",
+ link: "https://github.com/dotnet/docfx",
+ },
+ {
+ name: "DocFxMarkdownGen ",
+ author: "Jan0660 ",
+ version: "0.4.2",
+ license: "MIT",
+ link: "https://github.com/Jan0660/DocFxMarkdownGen",
+ },
+ ]),
+ },
+ editor: {
+ sbom: editorSbom,
+ licenses: groupDependenciesByLicense(
+ loadDependenciesFromSbom(editorSbom),
),
- {
- name: "docfx",
- author: ".NET Foundation and Contributors",
- version: "2.78.3",
- license: "MIT",
- link: "https://github.com/dotnet/docfx",
- },
- {
- name: "DocFxMarkdownGen ",
- author: "Jan0660 ",
- version: "0.4.2",
- license: "MIT",
- link: "https://github.com/Jan0660/DocFxMarkdownGen",
- },
- ]),
- editor: groupDependenciesByLicense(
- loadDependenciesFromLicenseReportOutput(
- "src/assets/editor-licenses.json",
+ },
+ api: {
+ sbom: apiSbom,
+ licenses: groupDependenciesByLicense(loadDependenciesFromSbom(apiSbom)),
+ },
+ cli: {
+ sbom: cliSbom,
+ licenses: groupDependenciesByLicense(loadDependenciesFromSbom(cliSbom)),
+ },
+ library: {
+ sbom: librarySbom,
+ licenses: groupDependenciesByLicense(
+ loadDependenciesFromSbom(librarySbom),
),
- ),
- api: groupDependenciesByLicense(
- loadDependenciesFromDotNetProjectLicensesOutput(
- "src/assets/api-licenses.json",
- ),
- ),
- cli: groupDependenciesByLicense(
- loadDependenciesFromDotNetProjectLicensesOutput(
- "src/assets/cli-licenses.json",
- ),
- ),
- library: groupDependenciesByLicense(
- loadDependenciesFromDotNetProjectLicensesOutput(
- "src/assets/library-licenses.json",
- ),
- ),
+ },
};
+
+ console.log(result);
+
+ return result;
},
};
interface Dependencies {
- docs: LicenseGroup[];
- editor: LicenseGroup[];
- api: LicenseGroup[];
- cli: LicenseGroup[];
- library: LicenseGroup[];
+ docs: { sbom: Sbom; licenses: LicenseGroup[] };
+ editor: { sbom: Sbom; licenses: LicenseGroup[] };
+ api: { sbom: Sbom; licenses: LicenseGroup[] };
+ cli: { sbom: Sbom; licenses: LicenseGroup[] };
+ library: { sbom: Sbom; licenses: LicenseGroup[] };
}
interface LicenseGroup {
@@ -88,66 +106,73 @@ function groupDependenciesByLicense(
);
}
-function loadDependenciesFromLicenseReportOutput(path: string): Dependency[] {
+function loadSbom(path: string) {
const fileContent = fs.readFileSync(path, "utf8");
- const parsed = JSON.parse(fileContent) as LicenseReportOutput;
- return parsed.map(
- (d: LicenseReportOutputElement): Dependency => ({
- name: d.name,
- author: d.author,
- version: d.installedVersion,
- license: d.licenseType,
- link: getRepositoryUrl(d.link),
- }),
- );
+ return JSON.parse(fileContent) as Sbom;
}
-type LicenseReportOutput = LicenseReportOutputElement[];
+function loadDependenciesFromSbom(sbom: Sbom): Dependency[] {
+ const thisComponentName = sbom.metadata.component["bom-ref"];
+ const thisComponentDependencies = sbom.dependencies[thisComponentName];
+ if (thisComponentDependencies === undefined) {
+ return [];
+ }
-interface LicenseReportOutputElement {
- readonly name: string;
- readonly author: string;
- readonly installedVersion: string;
- readonly licenseType: string;
- readonly link: string;
-}
+ const dependencies = sbom.components.filter(c =>
+ thisComponentDependencies.includes(c["bom-ref"]),
+ );
-function loadDependenciesFromDotNetProjectLicensesOutput(
- path: string,
-): Dependency[] {
- const fileContent = fs.readFileSync(path, "utf8");
- const parsed = JSON.parse(fileContent) as DotNetProjectLicensesOutput;
- return parsed.map(
- (d: DotNetProjectLicensesOutputElement): Dependency => ({
- name: d.PackageName,
- author: d.Authors.join(", "),
- version: d.PackageVersion,
- license: d.LicenseType,
- link: getRepositoryUrl(d.Repository?.Url),
+ return dependencies.map(
+ (component: SbomComponent): Dependency => ({
+ name: component.name,
+ author: component.author,
+ version: component.version,
+ license: getLicense(component.licenses),
+ link: getLink(component.externalReferences),
}),
);
}
-type DotNetProjectLicensesOutput = DotNetProjectLicensesOutputElement[];
-
-interface DotNetProjectLicensesOutputElement {
- readonly PackageName: string;
- readonly PackageVersion: string;
- readonly PackageUrl: string;
- readonly Copyright: string;
- readonly Authors: string[];
- readonly Description: string;
- readonly LicenseUrl: string;
- readonly LicenseType: string;
- readonly Repository: {
- readonly Type: string;
- readonly Url: string;
- readonly Commit: string;
- };
+function getLicense(licenses: SbomLicense[] | undefined): string | undefined {
+ if (licenses === undefined) {
+ return undefined;
+ }
+
+ const licenseNames = licenses.map(license => {
+ if (isSbomLicenseExpression(license)) {
+ if (
+ license.expression.startsWith("(") &&
+ license.expression.endsWith(")")
+ ) {
+ return license.expression.substring(1, license.expression.length - 2);
+ }
+
+ return license.expression;
+ }
+
+ return license.license.id;
+ });
+
+ return licenseNames.join(" OR ");
}
-const gitPlusUrlRegExp = new RegExp(/git\+(.*)\.git/);
-const gitSchemeUrlRegExp = new RegExp(/git:\/\/(.*)\.git/);
+function getLink(
+ externalReferences: SbomExternalReference[] | undefined,
+): string | undefined {
+ if (externalReferences === undefined) {
+ return undefined;
+ }
+
+ const vcsLink = externalReferences.find(r => r.type === "vcs")?.url;
+ if (vcsLink !== undefined) {
+ return getRepositoryUrl(vcsLink);
+ }
+
+ return externalReferences.find(r => r.type === "website")?.url;
+}
+
+const gitPlusUrlRegExp = new RegExp(/git\+(.*)\.git/g);
+const gitSchemeUrlRegExp = new RegExp(/git:\/\/(.*)\.git/g);
function getRepositoryUrl(url: string): string {
if (url === undefined || url === null) {
@@ -166,3 +191,46 @@ function getRepositoryUrl(url: string): string {
return url;
}
+
+export interface Sbom {
+ readonly version: number;
+ readonly metadata: {
+ readonly component: SbomComponent;
+ };
+ readonly components: SbomComponent[];
+ readonly dependencies: {
+ readonly ref: string;
+ readonly dependsOn: string[];
+ }[];
+}
+
+export interface SbomComponent {
+ readonly "bom-ref": string;
+ readonly name: string;
+ readonly version: string;
+ readonly author?: string;
+ readonly description?: string;
+ readonly licenses?: SbomLicense[];
+ readonly externalReferences: SbomExternalReference[];
+}
+
+export type SbomLicense = SbomLicenseId | SbomLicenseExpression;
+
+export interface SbomLicenseId {
+ readonly license: { readonly id: string };
+}
+
+export interface SbomLicenseExpression {
+ readonly expression: string;
+}
+
+export function isSbomLicenseExpression(
+ license: SbomLicense,
+): license is SbomLicenseExpression {
+ return Object.keys(license).includes("expression");
+}
+
+export interface SbomExternalReference {
+ readonly type: string;
+ readonly url: string;
+}
diff --git a/src/.idea/.idea.FacturXDotNet/.idea/encodings.xml b/src/.idea/.idea.FacturXDotNet/.idea/encodings.xml
new file mode 100644
index 00000000..ee5b142b
--- /dev/null
+++ b/src/.idea/.idea.FacturXDotNet/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
From 3945bfba118217efc3baafdbb3515b51a9345306 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 20:40:31 +0200
Subject: [PATCH 18/25] move sbom to public assets
---
docs/.gitignore | 10 ++---
docs/.vitepress/config.mts | 6 +--
docs/src/dependencies.data.ts | 38 +++++++++----------
docs/src/guides/about.md | 1 -
.../.idea.FacturXDotNet/.idea/encodings.xml | 4 +-
5 files changed, 29 insertions(+), 30 deletions(-)
diff --git a/docs/.gitignore b/docs/.gitignore
index 45286a4a..8c4a341b 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -62,8 +62,8 @@ TODOs.md
src/api-reference/**
src/cli/**
src/assets/facturxdotnet.openapi.json
-src/assets/docs.bom.json
-src/assets/editor.bom.json
-src/assets/api.bom.json
-src/assets/cli.bom.json
-src/assets/library.bom.json
+src/public/docs.bom.json
+src/public/editor.bom.json
+src/public/api.bom.json
+src/public/cli.bom.json
+src/public/library.bom.json
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 034ed53b..fd40865e 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -25,7 +25,7 @@ let cliItems =
collapsed: false,
collapsible: false,
})[0]?.items ?? [];
-cliItems = cliItems.map((item) => ({
+cliItems = cliItems.map(item => ({
...item,
text: item.text.toLowerCase() === "subcommands" ? "Sub Commands" : item.text,
}));
@@ -39,7 +39,7 @@ let apiReferenceItems =
collapsible: true,
})[0]?.items ?? [];
apiReferenceItems = apiReferenceItems.filter(
- (i) => i.items !== undefined && i.items.length > 0,
+ i => i.items !== undefined && i.items.length > 0,
);
const semVersion = semver.valid(env.version)
@@ -218,7 +218,7 @@ export default defineConfigWithTheme({
},
},
- transformPageData: (pageData) => {
+ transformPageData: pageData => {
const result = { ...pageData };
result.frontmatter = expandEnvInRecord(pageData.frontmatter);
return result;
diff --git a/docs/src/dependencies.data.ts b/docs/src/dependencies.data.ts
index 58deef47..aadb9614 100644
--- a/docs/src/dependencies.data.ts
+++ b/docs/src/dependencies.data.ts
@@ -9,15 +9,15 @@ export default {
"src/assets/library.bom.json",
],
load(): Dependencies {
- const docsSbom = loadSbom("src/assets/docs.bom.json");
- const editorSbom = loadSbom("src/assets/editor.bom.json");
- const apiSbom = loadSbom("src/assets/api.bom.json");
- const cliSbom = loadSbom("src/assets/cli.bom.json");
- const librarySbom = loadSbom("src/assets/library.bom.json");
+ const docsSbom = loadSbom("src/public/docs.bom.json");
+ const editorSbom = loadSbom("src/public/editor.bom.json");
+ const apiSbom = loadSbom("src/public/api.bom.json");
+ const cliSbom = loadSbom("src/public/cli.bom.json");
+ const librarySbom = loadSbom("src/public/library.bom.json");
const result: Dependencies = {
docs: {
- sbom: docsSbom,
+ sbomLink: "/docs.bom.json",
licenses: groupDependenciesByLicense([
...loadDependenciesFromSbom(docsSbom),
{
@@ -37,21 +37,21 @@ export default {
]),
},
editor: {
- sbom: editorSbom,
+ sbomLink: "/editor.bom.json",
licenses: groupDependenciesByLicense(
loadDependenciesFromSbom(editorSbom),
),
},
api: {
- sbom: apiSbom,
+ sbomLink: "/api.bom.json",
licenses: groupDependenciesByLicense(loadDependenciesFromSbom(apiSbom)),
},
cli: {
- sbom: cliSbom,
+ sbomLink: "/cli.bom.json",
licenses: groupDependenciesByLicense(loadDependenciesFromSbom(cliSbom)),
},
library: {
- sbom: librarySbom,
+ sbomLink: "/library.bom.json",
licenses: groupDependenciesByLicense(
loadDependenciesFromSbom(librarySbom),
),
@@ -65,11 +65,11 @@ export default {
};
interface Dependencies {
- docs: { sbom: Sbom; licenses: LicenseGroup[] };
- editor: { sbom: Sbom; licenses: LicenseGroup[] };
- api: { sbom: Sbom; licenses: LicenseGroup[] };
- cli: { sbom: Sbom; licenses: LicenseGroup[] };
- library: { sbom: Sbom; licenses: LicenseGroup[] };
+ docs: { sbomLink: string; licenses: LicenseGroup[] };
+ editor: { sbomLink: string; licenses: LicenseGroup[] };
+ api: { sbomLink: string; licenses: LicenseGroup[] };
+ cli: { sbomLink: string; licenses: LicenseGroup[] };
+ library: { sbomLink: string; licenses: LicenseGroup[] };
}
interface LicenseGroup {
@@ -118,7 +118,7 @@ function loadDependenciesFromSbom(sbom: Sbom): Dependency[] {
return [];
}
- const dependencies = sbom.components.filter(c =>
+ const dependencies = sbom.components.filter((c) =>
thisComponentDependencies.includes(c["bom-ref"]),
);
@@ -138,7 +138,7 @@ function getLicense(licenses: SbomLicense[] | undefined): string | undefined {
return undefined;
}
- const licenseNames = licenses.map(license => {
+ const licenseNames = licenses.map((license) => {
if (isSbomLicenseExpression(license)) {
if (
license.expression.startsWith("(") &&
@@ -163,12 +163,12 @@ function getLink(
return undefined;
}
- const vcsLink = externalReferences.find(r => r.type === "vcs")?.url;
+ const vcsLink = externalReferences.find((r) => r.type === "vcs")?.url;
if (vcsLink !== undefined) {
return getRepositoryUrl(vcsLink);
}
- return externalReferences.find(r => r.type === "website")?.url;
+ return externalReferences.find((r) => r.type === "website")?.url;
}
const gitPlusUrlRegExp = new RegExp(/git\+(.*)\.git/g);
diff --git a/docs/src/guides/about.md b/docs/src/guides/about.md
index 27edfbb9..e22ac5cf 100644
--- a/docs/src/guides/about.md
+++ b/docs/src/guides/about.md
@@ -48,7 +48,6 @@ To ensure transparency and maintainability, the dependencies used across the pro
Here’s a breakdown of the dependencies for each part of the project:
-
### Documentation website (this website)
diff --git a/src/.idea/.idea.FacturXDotNet/.idea/encodings.xml b/src/.idea/.idea.FacturXDotNet/.idea/encodings.xml
index ee5b142b..7261df15 100644
--- a/src/.idea/.idea.FacturXDotNet/.idea/encodings.xml
+++ b/src/.idea/.idea.FacturXDotNet/.idea/encodings.xml
@@ -1,7 +1,7 @@
-
-
+
+
\ No newline at end of file
From dbf0e2438a97e146d24828029f02b3e910828b16 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 20:41:46 +0200
Subject: [PATCH 19/25] minor
---
.github/workflows/reusable-build-docs-application.yml | 2 +-
.github/workflows/reusable-build-publish-api-docker-image.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml
index eb82d0cb..b454fcd4 100644
--- a/.github/workflows/reusable-build-docs-application.yml
+++ b/.github/workflows/reusable-build-docs-application.yml
@@ -66,7 +66,7 @@ jobs:
run: dotnet tool install -g DocFxMarkdownGen
- name: Install CycloneDX
- run: dotnet tool install --global CycloneDX --framework net8.0
+ run: dotnet tool install --global CycloneDX
- name: Generate .NET API reference
run: |
diff --git a/.github/workflows/reusable-build-publish-api-docker-image.yml b/.github/workflows/reusable-build-publish-api-docker-image.yml
index 10326c65..c01334eb 100644
--- a/.github/workflows/reusable-build-publish-api-docker-image.yml
+++ b/.github/workflows/reusable-build-publish-api-docker-image.yml
@@ -54,7 +54,7 @@ jobs:
dotnet-version: 9.0.x
- name: Install CycloneDX
- run: dotnet tool install --global CycloneDX --framework net8.0
+ run: dotnet tool install --global CycloneDX
- name: Generate licenses file
run: cd src/FacturXDotNet.API; dotnet-CycloneDX FacturXDotNet.API.csproj -o Resources --json -fn api.bom.json
From ee9f318817216ad15aa2649d8121dfaaca5537e9 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 22:17:45 +0200
Subject: [PATCH 20/25] use sbom in docs
---
docs/.vitepress/theme/custom.css | 5 +
docs/package-lock.json | 59 ++++++++-
docs/package.json | 4 +-
docs/src/components/Licenses.vue | 21 ----
docs/src/dependencies.data.ts | 126 +++++++++++++-------
docs/src/guides/about.md | 24 ++--
docs/src/guides/components/Dependencies.vue | 68 +++++++++++
7 files changed, 226 insertions(+), 81 deletions(-)
delete mode 100644 docs/src/components/Licenses.vue
create mode 100644 docs/src/guides/components/Dependencies.vue
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
index 110b3b18..1e802143 100644
--- a/docs/.vitepress/theme/custom.css
+++ b/docs/.vitepress/theme/custom.css
@@ -103,3 +103,8 @@ p:has(.sidebar-footer) {
--vp-sidebar-width: 472px; /* default: 272px */
}
}
+
+.dependency-description p {
+ margin-top: 0;
+ margin-bottom: 0;
+}
\ No newline at end of file
diff --git a/docs/package-lock.json b/docs/package-lock.json
index cf529657..cfc0cc6e 100644
--- a/docs/package-lock.json
+++ b/docs/package-lock.json
@@ -1,10 +1,14 @@
{
- "name": "docs",
+ "name": "facturxdotnet-docs",
+ "version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
+ "name": "facturxdotnet-docs",
+ "version": "0.0.0",
"dependencies": {
+ "markdown-it": "^14.1.0",
"semver": "^7.7.1",
"vitepress-openapi": "^0.0.3-alpha.76",
"vitepress-plugin-auto-sidebar": "^1.3.5",
@@ -2228,6 +2232,15 @@
"node": ">=18"
}
},
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "license": "MIT",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
"node_modules/lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@@ -2275,6 +2288,29 @@
"integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==",
"license": "MIT"
},
+ "node_modules/markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/markdown-it/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
"node_modules/mdast-util-to-hast": {
"version": "13.2.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
@@ -2296,6 +2332,12 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+ "license": "MIT"
+ },
"node_modules/micromark-util-character": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
@@ -2563,6 +2605,15 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@@ -2935,6 +2986,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "license": "MIT"
+ },
"node_modules/unist-util-is": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
diff --git a/docs/package.json b/docs/package.json
index 2a38078d..0f9ef1de 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -1,6 +1,6 @@
{
"name": "facturxdotnet-docs",
- "version": "0.0.0.0",
+ "version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vitepress dev --port 40850",
@@ -8,11 +8,11 @@
"preview": "vitepress preview --port 40850"
},
"devDependencies": {
- "license-report": "^6.7.2",
"vitepress": "^1.6.3",
"vitepress-plugin-nprogress": "^0.0.4"
},
"dependencies": {
+ "markdown-it": "^14.1.0",
"semver": "^7.7.1",
"vitepress-openapi": "^0.0.3-alpha.76",
"vitepress-plugin-auto-sidebar": "^1.3.5",
diff --git a/docs/src/components/Licenses.vue b/docs/src/components/Licenses.vue
deleted file mode 100644
index 63ec6833..00000000
--- a/docs/src/components/Licenses.vue
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
- {{ group.license }}
-
-
-
-
-
-
diff --git a/docs/src/dependencies.data.ts b/docs/src/dependencies.data.ts
index aadb9614..7b6448cd 100644
--- a/docs/src/dependencies.data.ts
+++ b/docs/src/dependencies.data.ts
@@ -15,61 +15,86 @@ export default {
const cliSbom = loadSbom("src/public/cli.bom.json");
const librarySbom = loadSbom("src/public/library.bom.json");
- const result: Dependencies = {
+ const docsDependencies = [
+ ...loadDependenciesFromSbom(docsSbom),
+ {
+ name: "docfx",
+ version: "2.78.3",
+ author: ".NET Foundation and Contributors",
+ description: "The docfx command line tool published as .NET tool",
+ license: "MIT",
+ link: "https://github.com/dotnet/docfx",
+ },
+ {
+ name: "DocFxMarkdownGen ",
+ version: "0.4.2",
+ author: "Jan0660 ",
+ description: "Docusaurus Markdown generator using DocFX.",
+ license: "MIT",
+ link: "https://github.com/Jan0660/DocFxMarkdownGen",
+ },
+ ];
+ const editorDependencies = loadDependenciesFromSbom(editorSbom);
+ const apiDependencies = loadDependenciesFromSbom(apiSbom);
+ const cliDependencies = loadDependenciesFromSbom(cliSbom);
+ const libraryDependencies = loadDependenciesFromSbom(librarySbom);
+
+ return {
docs: {
sbomLink: "/docs.bom.json",
- licenses: groupDependenciesByLicense([
- ...loadDependenciesFromSbom(docsSbom),
- {
- name: "docfx",
- author: ".NET Foundation and Contributors",
- version: "2.78.3",
- license: "MIT",
- link: "https://github.com/dotnet/docfx",
- },
- {
- name: "DocFxMarkdownGen ",
- author: "Jan0660 ",
- version: "0.4.2",
- license: "MIT",
- link: "https://github.com/Jan0660/DocFxMarkdownGen",
- },
- ]),
+ dependenciesCount: docsDependencies.length,
+ licenses: groupDependenciesByLicense(docsDependencies),
},
editor: {
sbomLink: "/editor.bom.json",
- licenses: groupDependenciesByLicense(
- loadDependenciesFromSbom(editorSbom),
- ),
+ dependenciesCount: editorDependencies.length,
+ licenses: groupDependenciesByLicense(editorDependencies),
},
api: {
sbomLink: "/api.bom.json",
- licenses: groupDependenciesByLicense(loadDependenciesFromSbom(apiSbom)),
+ dependenciesCount: apiDependencies.length,
+ licenses: groupDependenciesByLicense(apiDependencies),
},
cli: {
sbomLink: "/cli.bom.json",
- licenses: groupDependenciesByLicense(loadDependenciesFromSbom(cliSbom)),
+ dependenciesCount: cliDependencies.length,
+ licenses: groupDependenciesByLicense(cliDependencies),
},
library: {
sbomLink: "/library.bom.json",
- licenses: groupDependenciesByLicense(
- loadDependenciesFromSbom(librarySbom),
- ),
+ dependenciesCount: libraryDependencies.length,
+ licenses: groupDependenciesByLicense(libraryDependencies),
},
};
-
- console.log(result);
-
- return result;
},
};
interface Dependencies {
- docs: { sbomLink: string; licenses: LicenseGroup[] };
- editor: { sbomLink: string; licenses: LicenseGroup[] };
- api: { sbomLink: string; licenses: LicenseGroup[] };
- cli: { sbomLink: string; licenses: LicenseGroup[] };
- library: { sbomLink: string; licenses: LicenseGroup[] };
+ docs: {
+ sbomLink: string;
+ dependenciesCount: number;
+ licenses: LicenseGroup[];
+ };
+ editor: {
+ sbomLink: string;
+ dependenciesCount: number;
+ licenses: LicenseGroup[];
+ };
+ api: {
+ sbomLink: string;
+ dependenciesCount: number;
+ licenses: LicenseGroup[];
+ };
+ cli: {
+ sbomLink: string;
+ dependenciesCount: number;
+ licenses: LicenseGroup[];
+ };
+ library: {
+ sbomLink: string;
+ dependenciesCount: number;
+ licenses: LicenseGroup[];
+ };
}
interface LicenseGroup {
@@ -79,10 +104,11 @@ interface LicenseGroup {
interface Dependency {
readonly name: string;
- readonly author: string;
readonly version: string;
- readonly license: string;
- readonly link: string;
+ readonly author?: string;
+ readonly description?: string;
+ readonly license?: string;
+ readonly link?: string;
}
function groupDependenciesByLicense(
@@ -102,7 +128,13 @@ function groupDependenciesByLicense(
}
return Object.values(result).sort((a, b) =>
- a.license.localeCompare(b.license),
+ a.license === "" && b.license === ""
+ ? 0
+ : a.license === ""
+ ? 1
+ : b.license === ""
+ ? -1
+ : a.license.localeCompare(b.license),
);
}
@@ -113,20 +145,24 @@ function loadSbom(path: string) {
function loadDependenciesFromSbom(sbom: Sbom): Dependency[] {
const thisComponentName = sbom.metadata.component["bom-ref"];
- const thisComponentDependencies = sbom.dependencies[thisComponentName];
+ const thisComponentDependencies = sbom.dependencies.find(
+ d => d.ref === thisComponentName,
+ )?.dependsOn;
+
if (thisComponentDependencies === undefined) {
return [];
}
- const dependencies = sbom.components.filter((c) =>
+ const dependencies = sbom.components.filter(c =>
thisComponentDependencies.includes(c["bom-ref"]),
);
return dependencies.map(
(component: SbomComponent): Dependency => ({
name: component.name,
- author: component.author,
version: component.version,
+ author: component.author,
+ description: component.description,
license: getLicense(component.licenses),
link: getLink(component.externalReferences),
}),
@@ -138,7 +174,7 @@ function getLicense(licenses: SbomLicense[] | undefined): string | undefined {
return undefined;
}
- const licenseNames = licenses.map((license) => {
+ const licenseNames = licenses.map(license => {
if (isSbomLicenseExpression(license)) {
if (
license.expression.startsWith("(") &&
@@ -163,12 +199,12 @@ function getLink(
return undefined;
}
- const vcsLink = externalReferences.find((r) => r.type === "vcs")?.url;
+ const vcsLink = externalReferences.find(r => r.type === "vcs")?.url;
if (vcsLink !== undefined) {
return getRepositoryUrl(vcsLink);
}
- return externalReferences.find((r) => r.type === "website")?.url;
+ return externalReferences.find(r => r.type === "website")?.url;
}
const gitPlusUrlRegExp = new RegExp(/git\+(.*)\.git/g);
diff --git a/docs/src/guides/about.md b/docs/src/guides/about.md
index e22ac5cf..1fd06818 100644
--- a/docs/src/guides/about.md
+++ b/docs/src/guides/about.md
@@ -1,10 +1,12 @@
---
title: About
+outline: deep
---
+
+
+
+
+ Dependencies ({{ dependencies.dependenciesCount }})
+ sbom
+
+
+
+
+
+ {{ group.license }}
+
+ Unknown
+ ({{ group.dependencies.length }})
+
+
+
+
+
+
+
From b80ad685315c871bc12a80f2feeb3003b65e9442 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 22:21:08 +0200
Subject: [PATCH 21/25] update CI to set version of docs in package json
---
.github/workflows/reusable-build-docs-application.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml
index b454fcd4..2592fd6a 100644
--- a/.github/workflows/reusable-build-docs-application.yml
+++ b/.github/workflows/reusable-build-docs-application.yml
@@ -183,6 +183,9 @@ jobs:
cat docs/src/env.json
+ - name: Set version in package json
+ run: cd docs; npm run version ${{ inputs.version }}
+
- name: Build
run: cd docs; npm run build -- --base /docs/ --outDir dist/
From c766eaebf859f864db84eababe4e93e0839ed6ec Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 22:23:51 +0200
Subject: [PATCH 22/25] fix ci
---
.github/workflows/reusable-build-docs-application.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml
index 2592fd6a..167bf43b 100644
--- a/.github/workflows/reusable-build-docs-application.yml
+++ b/.github/workflows/reusable-build-docs-application.yml
@@ -184,7 +184,7 @@ jobs:
cat docs/src/env.json
- name: Set version in package json
- run: cd docs; npm run version ${{ inputs.version }}
+ run: cd docs; npm version ${{ inputs.version }}
- name: Build
run: cd docs; npm run build -- --base /docs/ --outDir dist/
From 881998fc5d6ab9c022ab0feb2300ce33669c6dc2 Mon Sep 17 00:00:00 2001
From: ismailbennani
Date: Sat, 26 Apr 2025 22:28:57 +0200
Subject: [PATCH 23/25] use href correctly in sbom download link
---
docs/src/guides/components/Dependencies.vue | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/src/guides/components/Dependencies.vue b/docs/src/guides/components/Dependencies.vue
index 3f28ed20..dd46c630 100644
--- a/docs/src/guides/components/Dependencies.vue
+++ b/docs/src/guides/components/Dependencies.vue
@@ -1,4 +1,5 @@