diff --git a/.gitignore b/.gitignore index b34e923a..3d609697 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ Thumbs.db # Nx .nx/cache +.nx/workspace-data migrations.json # Angular diff --git a/apps/mfe-for-ssr/.eslintrc.json b/apps/mfe-for-ssr/.eslintrc.json new file mode 100644 index 00000000..bcdea5e4 --- /dev/null +++ b/apps/mfe-for-ssr/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../.eslintrc.base.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/mfe-for-ssr/federation.config.js b/apps/mfe-for-ssr/federation.config.js new file mode 100644 index 00000000..c523c5f8 --- /dev/null +++ b/apps/mfe-for-ssr/federation.config.js @@ -0,0 +1,37 @@ +const { + withNativeFederation, + shareAll, +} = require('dist/libs/native-federation/src/config.js'); + +module.exports = withNativeFederation({ + name: 'mfe-for-ssr', + + exposes: { + './AppComponent': './apps/mfe-for-ssr/src/app/app.component.ts', + }, + + shared: { + ...shareAll({ + singleton: true, + strictVersion: true, + requiredVersion: 'auto', + }), + }, + + skip: [ + '@softarc/native-federation', + '@angular-architects/build_angular', + '@angular-architects/module-federation', + '@angular-architects/module-federation-runtime', + '@angular-architects/module-federation-tools', + '@angular-architects/native-federation', + '@softarc/native-federation-esbuild', + '@softarc/native-federation-runtime', + '@softarc/native-federation/build', + '@module-federation/vite', + // Add further packages you don't need at runtime + ], + + // Please read our FAQ about sharing libs: + // https://shorturl.at/jmzH0 +}); diff --git a/apps/mfe-for-ssr/jest.config.ts b/apps/mfe-for-ssr/jest.config.ts new file mode 100644 index 00000000..cd56528f --- /dev/null +++ b/apps/mfe-for-ssr/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'mfe-for-ssr', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../coverage/apps/mfe-for-ssr', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/mfe-for-ssr/project.json b/apps/mfe-for-ssr/project.json new file mode 100644 index 00000000..7081eda2 --- /dev/null +++ b/apps/mfe-for-ssr/project.json @@ -0,0 +1,106 @@ +{ + "name": "mfe-for-ssr", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/mfe-for-ssr/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-architects/native-federation:build", + "options": {}, + "configurations": { + "production": { + "target": "mfe-for-ssr:esbuild:production" + }, + "development": { + "target": "mfe-for-ssr:esbuild:development", + "dev": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-architects/native-federation:build", + "options": { + "target": "mfe-for-ssr:serve-original:development", + "rebuildDelay": 0, + "dev": true, + "port": 0 + } + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "mfe-for-ssr:build" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/mfe-for-ssr/jest.config.ts" + } + }, + "esbuild": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/mfe-for-ssr", + "index": "apps/mfe-for-ssr/src/index.html", + "browser": "apps/mfe-for-ssr/src/main.ts", + "polyfills": ["zone.js", "es-module-shims"], + "tsConfig": "apps/mfe-for-ssr/tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "apps/mfe-for-ssr/public" + } + ], + "styles": ["apps/mfe-for-ssr/src/styles.css"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve-original": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4201 + }, + "configurations": { + "production": { + "buildTarget": "mfe-for-ssr:esbuild:production" + }, + "development": { + "buildTarget": "mfe-for-ssr:esbuild:development" + } + }, + "defaultConfiguration": "development" + } + } +} diff --git a/apps/mfe-for-ssr/public/favicon.ico b/apps/mfe-for-ssr/public/favicon.ico new file mode 100644 index 00000000..317ebcb2 Binary files /dev/null and b/apps/mfe-for-ssr/public/favicon.ico differ diff --git a/apps/mfe-for-ssr/src/app/app.component.css b/apps/mfe-for-ssr/src/app/app.component.css new file mode 100644 index 00000000..e69de29b diff --git a/apps/mfe-for-ssr/src/app/app.component.html b/apps/mfe-for-ssr/src/app/app.component.html new file mode 100644 index 00000000..e144e9ff --- /dev/null +++ b/apps/mfe-for-ssr/src/app/app.component.html @@ -0,0 +1 @@ +
Mfe component
diff --git a/apps/mfe-for-ssr/src/app/app.component.spec.ts b/apps/mfe-for-ssr/src/app/app.component.spec.ts new file mode 100644 index 00000000..c4de7e2d --- /dev/null +++ b/apps/mfe-for-ssr/src/app/app.component.spec.ts @@ -0,0 +1,27 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; +import { NxWelcomeComponent } from './nx-welcome.component'; +import { RouterModule } from '@angular/router'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent, NxWelcomeComponent, RouterModule.forRoot([])], + }).compileComponents(); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome mfe-for-ssr' + ); + }); + + it(`should have as title 'mfe-for-ssr'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('mfe-for-ssr'); + }); +}); diff --git a/apps/mfe-for-ssr/src/app/app.component.ts b/apps/mfe-for-ssr/src/app/app.component.ts new file mode 100644 index 00000000..d543ded1 --- /dev/null +++ b/apps/mfe-for-ssr/src/app/app.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +@Component({ + standalone: true, + imports: [RouterModule], + selector: 'app-root-mfe', + templateUrl: './app.component.html', + styleUrl: './app.component.css', +}) +export class AppComponent { + title = 'mfe-for-ssr'; +} diff --git a/apps/mfe-for-ssr/src/app/app.config.ts b/apps/mfe-for-ssr/src/app/app.config.ts new file mode 100644 index 00000000..bc2fc3ca --- /dev/null +++ b/apps/mfe-for-ssr/src/app/app.config.ts @@ -0,0 +1,10 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { appRoutes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(appRoutes), + ], +}; diff --git a/apps/mfe-for-ssr/src/app/app.routes.ts b/apps/mfe-for-ssr/src/app/app.routes.ts new file mode 100644 index 00000000..8762dfe2 --- /dev/null +++ b/apps/mfe-for-ssr/src/app/app.routes.ts @@ -0,0 +1,3 @@ +import { Route } from '@angular/router'; + +export const appRoutes: Route[] = []; diff --git a/apps/mfe-for-ssr/src/app/nx-welcome.component.ts b/apps/mfe-for-ssr/src/app/nx-welcome.component.ts new file mode 100644 index 00000000..935bca75 --- /dev/null +++ b/apps/mfe-for-ssr/src/app/nx-welcome.component.ts @@ -0,0 +1,907 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-nx-welcome', + standalone: true, + imports: [CommonModule], + template: ` + + +
+
+ +
+

+ Hello there, + Welcome mfe-for-ssr 👋 +

+
+ +
+
+

+ + + + You're up and running +

+ What's next? +
+
+ + + +
+
+ + + +
+

Next steps

+

Here are some things you can do with Nx:

+
+ + + + + Add UI library + +
# Generate UI lib
+nx g @nx/angular:lib ui
+# Add a component
+nx g @nx/angular:component ui/src/lib/button
+
+
+ + + + + View project details + +
nx show project mfe-for-ssr --web
+
+
+ + + + + View interactive project graph + +
nx graph
+
+
+ + + + + Run affected commands + +
# see what's been affected by changes
+nx affected:graph
+# run tests for current changes
+nx affected:test
+# run e2e tests for current changes
+nx affected:e2e
+
+
+

+ Carefully crafted with + + + +

+
+
+ `, + styles: [], + encapsulation: ViewEncapsulation.None, +}) +export class NxWelcomeComponent {} diff --git a/apps/mfe-for-ssr/src/bootstrap.ts b/apps/mfe-for-ssr/src/bootstrap.ts new file mode 100644 index 00000000..514c89a0 --- /dev/null +++ b/apps/mfe-for-ssr/src/bootstrap.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err) +); diff --git a/apps/mfe-for-ssr/src/index.html b/apps/mfe-for-ssr/src/index.html new file mode 100644 index 00000000..71490c09 --- /dev/null +++ b/apps/mfe-for-ssr/src/index.html @@ -0,0 +1,13 @@ + + + + + mfe-for-ssr + + + + + + + + diff --git a/apps/mfe-for-ssr/src/main.ts b/apps/mfe-for-ssr/src/main.ts new file mode 100644 index 00000000..f5a7609c --- /dev/null +++ b/apps/mfe-for-ssr/src/main.ts @@ -0,0 +1,6 @@ +import { initFederation } from '@angular-architects/native-federation'; + +initFederation() + .catch((err) => console.error(err)) + .then((_) => import('./bootstrap')) + .catch((err) => console.error(err)); diff --git a/apps/mfe-for-ssr/src/styles.css b/apps/mfe-for-ssr/src/styles.css new file mode 100644 index 00000000..90d4ee00 --- /dev/null +++ b/apps/mfe-for-ssr/src/styles.css @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/mfe-for-ssr/src/test-setup.ts b/apps/mfe-for-ssr/src/test-setup.ts new file mode 100644 index 00000000..ab1eeeb3 --- /dev/null +++ b/apps/mfe-for-ssr/src/test-setup.ts @@ -0,0 +1,8 @@ +// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment +globalThis.ngJest = { + testEnvironmentOptions: { + errorOnUnknownElements: true, + errorOnUnknownProperties: true, + }, +}; +import 'jest-preset-angular/setup-jest'; diff --git a/apps/mfe-for-ssr/tsconfig.app.json b/apps/mfe-for-ssr/tsconfig.app.json new file mode 100644 index 00000000..fff4a41d --- /dev/null +++ b/apps/mfe-for-ssr/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/mfe-for-ssr/tsconfig.editor.json b/apps/mfe-for-ssr/tsconfig.editor.json new file mode 100644 index 00000000..a8ac182c --- /dev/null +++ b/apps/mfe-for-ssr/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/mfe-for-ssr/tsconfig.federation.json b/apps/mfe-for-ssr/tsconfig.federation.json new file mode 100644 index 00000000..f0faebe8 --- /dev/null +++ b/apps/mfe-for-ssr/tsconfig.federation.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src*.spec.ts"] +} diff --git a/apps/mfe-for-ssr/tsconfig.json b/apps/mfe-for-ssr/tsconfig.json new file mode 100644 index 00000000..c147dd21 --- /dev/null +++ b/apps/mfe-for-ssr/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "es2022", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/mfe-for-ssr/tsconfig.spec.json b/apps/mfe-for-ssr/tsconfig.spec.json new file mode 100644 index 00000000..53fbfcdc --- /dev/null +++ b/apps/mfe-for-ssr/tsconfig.spec.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "target": "es2016", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/apps/mfe2/tsconfig.federation.json b/apps/mfe2/tsconfig.federation.json new file mode 100644 index 00000000..5835c519 --- /dev/null +++ b/apps/mfe2/tsconfig.federation.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["***.spec.ts", "jest.config.ts"] +} diff --git a/apps/playground/tsconfig.federation.json b/apps/playground/tsconfig.federation.json new file mode 100644 index 00000000..feaf147e --- /dev/null +++ b/apps/playground/tsconfig.federation.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts"] +} diff --git a/apps/ssr-host/.eslintrc.json b/apps/ssr-host/.eslintrc.json new file mode 100644 index 00000000..bcdea5e4 --- /dev/null +++ b/apps/ssr-host/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../.eslintrc.base.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/apps/ssr-host/federation.config.js b/apps/ssr-host/federation.config.js new file mode 100644 index 00000000..50b56ca9 --- /dev/null +++ b/apps/ssr-host/federation.config.js @@ -0,0 +1,31 @@ +const { + withNativeFederation, + shareAll, +} = require('dist/libs/native-federation/src/config.js'); + +module.exports = withNativeFederation({ + shared: { + ...shareAll({ + singleton: true, + strictVersion: true, + requiredVersion: 'auto', + }), + }, + + skip: [ + '@softarc/native-federation', + '@angular-architects/build_angular', + '@angular-architects/module-federation', + '@angular-architects/module-federation-runtime', + '@angular-architects/module-federation-tools', + '@angular-architects/native-federation', + '@softarc/native-federation-esbuild', + '@softarc/native-federation-runtime', + '@softarc/native-federation/build', + '@module-federation/vite', + // Add further packages you don't need at runtime + ], + + // Please read our FAQ about sharing libs: + // https://shorturl.at/jmzH0 +}); diff --git a/apps/ssr-host/jest.config.ts b/apps/ssr-host/jest.config.ts new file mode 100644 index 00000000..fa9df5c2 --- /dev/null +++ b/apps/ssr-host/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'ssr-host', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../coverage/apps/ssr-host', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/ssr-host/project.json b/apps/ssr-host/project.json new file mode 100644 index 00000000..9bd91bd4 --- /dev/null +++ b/apps/ssr-host/project.json @@ -0,0 +1,115 @@ +{ + "name": "ssr-host", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/ssr-host/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-architects/native-federation:build", + "options": { + "customLoader": "apps/ssr-host/src/custom-loader.ts" + }, + "configurations": { + "production": { + "target": "ssr-host:esbuild:development" + }, + "development": { + "target": "ssr-host:esbuild:development", + "dev": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-architects/native-federation:build", + "options": { + "target": "ssr-host:serve-original:development", + "customLoader": "apps/ssr-host/src/custom-loader.ts", + "rebuildDelay": 0, + "dev": true, + "port": 0 + } + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "ssr-host:build" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/ssr-host/jest.config.ts" + } + }, + "esbuild": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/ssr-host", + "index": "apps/ssr-host/src/index.html", + "browser": "apps/ssr-host/src/main.ts", + "polyfills": ["zone.js", "es-module-shims"], + "tsConfig": "apps/ssr-host/tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "apps/ssr-host/public" + }, + "apps/ssr-host/src/assets" + ], + "styles": ["apps/ssr-host/src/styles.css"], + "scripts": [], + "server": "apps/ssr-host/src/main.server.ts", + "prerender": false, + "ssr": { + "entry": "apps/ssr-host/server.ts" + } + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve-original": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "ssr-host:esbuild:production" + }, + "development": { + "buildTarget": "ssr-host:esbuild:development" + } + }, + "defaultConfiguration": "development", + "options": { + "port": 4200 + } + } + } +} diff --git a/apps/ssr-host/public/favicon.ico b/apps/ssr-host/public/favicon.ico new file mode 100644 index 00000000..317ebcb2 Binary files /dev/null and b/apps/ssr-host/public/favicon.ico differ diff --git a/apps/ssr-host/server.ts b/apps/ssr-host/server.ts new file mode 100644 index 00000000..c60dd3a3 --- /dev/null +++ b/apps/ssr-host/server.ts @@ -0,0 +1,60 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { CommonEngine } from '@angular/ssr'; +import express from 'express'; +import { fileURLToPath } from 'node:url'; +import { dirname, join, resolve } from 'node:path'; +import bootstrap from './src/main.server'; + +// The Express app is exported so that it can be used by serverless Functions. +export function app(): express.Express { + const server = express(); + const serverDistFolder = dirname(fileURLToPath(import.meta.url)); + const browserDistFolder = resolve(serverDistFolder, '../browser'); + const indexHtml = join(serverDistFolder, 'index.server.html'); + + const commonEngine = new CommonEngine(); + + server.set('view engine', 'html'); + server.set('views', browserDistFolder); + + // Example Express Rest API endpoints + // server.get('/api/**', (req, res) => { }); + // Serve static files from /browser + server.get( + '**', + express.static(browserDistFolder, { + maxAge: '1y', + index: 'index.html', + }) + ); + + // All regular routes use the Angular engine + server.get('**', (req, res, next) => { + const { protocol, originalUrl, baseUrl, headers } = req; + + commonEngine + .render({ + bootstrap, + documentFilePath: indexHtml, + url: `${protocol}://${headers.host}${originalUrl}`, + publicPath: browserDistFolder, + providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], + }) + .then((html) => res.send(html)) + .catch((err) => next(err)); + }); + + return server; +} + +function run(): void { + const port = process.env['PORT'] || 4000; + + // Start up the Node server + const server = app(); + server.listen(port, () => { + console.log(`Node Express server listening on http://localhost:${port}`); + }); +} + +run(); diff --git a/apps/ssr-host/src/app/app.component.css b/apps/ssr-host/src/app/app.component.css new file mode 100644 index 00000000..e69de29b diff --git a/apps/ssr-host/src/app/app.component.html b/apps/ssr-host/src/app/app.component.html new file mode 100644 index 00000000..0680b43f --- /dev/null +++ b/apps/ssr-host/src/app/app.component.html @@ -0,0 +1 @@ + diff --git a/apps/ssr-host/src/app/app.component.spec.ts b/apps/ssr-host/src/app/app.component.spec.ts new file mode 100644 index 00000000..d2b6ce6a --- /dev/null +++ b/apps/ssr-host/src/app/app.component.spec.ts @@ -0,0 +1,27 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; +import { NxWelcomeComponent } from './nx-welcome.component'; +import { RouterModule } from '@angular/router'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent, NxWelcomeComponent, RouterModule.forRoot([])], + }).compileComponents(); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Welcome ssr-host' + ); + }); + + it(`should have as title 'ssr-host'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('ssr-host'); + }); +}); diff --git a/apps/ssr-host/src/app/app.component.ts b/apps/ssr-host/src/app/app.component.ts new file mode 100644 index 00000000..f6230ae9 --- /dev/null +++ b/apps/ssr-host/src/app/app.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { NxWelcomeComponent } from './nx-welcome.component'; + +@Component({ + standalone: true, + imports: [NxWelcomeComponent, RouterModule], + selector: 'app-root', + templateUrl: './app.component.html', + styleUrl: './app.component.css', +}) +export class AppComponent { + title = 'ssr-host'; +} diff --git a/apps/ssr-host/src/app/app.config.server.ts b/apps/ssr-host/src/app/app.config.server.ts new file mode 100644 index 00000000..1980cfe1 --- /dev/null +++ b/apps/ssr-host/src/app/app.config.server.ts @@ -0,0 +1,9 @@ +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering } from '@angular/platform-server'; +import { appConfig } from './app.config'; + +const serverConfig: ApplicationConfig = { + providers: [provideServerRendering()], +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/apps/ssr-host/src/app/app.config.ts b/apps/ssr-host/src/app/app.config.ts new file mode 100644 index 00000000..8f91ae95 --- /dev/null +++ b/apps/ssr-host/src/app/app.config.ts @@ -0,0 +1,12 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { appRoutes } from './app.routes'; +import { provideClientHydration } from '@angular/platform-browser'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideClientHydration(), + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(appRoutes), + ], +}; diff --git a/apps/ssr-host/src/app/app.routes.ts b/apps/ssr-host/src/app/app.routes.ts new file mode 100644 index 00000000..6557c22f --- /dev/null +++ b/apps/ssr-host/src/app/app.routes.ts @@ -0,0 +1,27 @@ +import { Route } from '@angular/router'; +import { loadRemoteModule } from '@angular-architects/native-federation'; +import { isPlatformServer } from '@angular/common'; +import { inject, PLATFORM_ID } from '@angular/core'; + +export const appRoutes: Route[] = [ + { + path: '', + // canMatch: [() => !isPlatformServer(inject(PLATFORM_ID))], + loadComponent: () => + loadRemoteModule('mfe-for-ssr', './AppComponent').then( + (r) => r.AppComponent + ), + }, + // { + // path: '', + // canMatch: [() => isPlatformServer(inject(PLATFORM_ID))], + // loadComponent: () => { + // // loadRemoteModule('project-app-ui', './Test').then((r) => r.TestComponent), + // + // // @ts-ignore + // return import('./nx-welcome.component').then( + // (r) => r.NxWelcomeComponent + // ); + // }, + // }, +]; diff --git a/apps/ssr-host/src/app/nx-welcome.component.ts b/apps/ssr-host/src/app/nx-welcome.component.ts new file mode 100644 index 00000000..d8018cca --- /dev/null +++ b/apps/ssr-host/src/app/nx-welcome.component.ts @@ -0,0 +1,907 @@ +import { Component, ViewEncapsulation } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-nx-welcome', + standalone: true, + imports: [CommonModule], + template: ` + + +
+
+ +
+

+ Hello there, + Welcome ssr-host 👋 +

+
+ +
+
+

+ + + + You're up and running +

+ What's next? +
+
+ + + +
+
+ + + +
+

Next steps

+

Here are some things you can do with Nx:

+
+ + + + + Add UI library + +
# Generate UI lib
+nx g @nx/angular:lib ui
+# Add a component
+nx g @nx/angular:component ui/src/lib/button
+
+
+ + + + + View project details + +
nx show project ssr-host --web
+
+
+ + + + + View interactive project graph + +
nx graph
+
+
+ + + + + Run affected commands + +
# see what's been affected by changes
+nx affected:graph
+# run tests for current changes
+nx affected:test
+# run e2e tests for current changes
+nx affected:e2e
+
+
+

+ Carefully crafted with + + + +

+
+
+ `, + styles: [], + encapsulation: ViewEncapsulation.None, +}) +export class NxWelcomeComponent {} diff --git a/apps/ssr-host/src/assets/federation.manifest.json b/apps/ssr-host/src/assets/federation.manifest.json new file mode 100644 index 00000000..0d890fa2 --- /dev/null +++ b/apps/ssr-host/src/assets/federation.manifest.json @@ -0,0 +1,3 @@ +{ + "mfe-for-ssr": "http://localhost:4201/remoteEntry.json" +} diff --git a/apps/ssr-host/src/bootstrap.ts b/apps/ssr-host/src/bootstrap.ts new file mode 100644 index 00000000..514c89a0 --- /dev/null +++ b/apps/ssr-host/src/bootstrap.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err) +); diff --git a/apps/ssr-host/src/custom-loader.ts b/apps/ssr-host/src/custom-loader.ts new file mode 100644 index 00000000..a359a910 --- /dev/null +++ b/apps/ssr-host/src/custom-loader.ts @@ -0,0 +1,119 @@ +import { + getModuleNameByUrl, + hasRemoteSsr, + loadModule, + setManifestObjectForSsr, +} from '@angular-architects/native-federation'; +import { ImportMap } from '@jspm/import-map'; +import { readFileSync } from 'fs'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +import manifest from './assets/federation.manifest.json'; + +setManifestObjectForSsr(manifest); + +export type Context = { + parentURL?: string; +}; + +export type NextResolve = ( + specifier: string, + context: Context, + nextResolve: NextResolve +) => Promise; +export type DefaultLoad = ( + specifier: string, + context: Context, + defaultLoad: DefaultLoad +) => Promise; + +const toImportJson = join( + dirname(fileURLToPath(import.meta.url)), + 'importmap.json' +); +const importmapJson = JSON.parse(readFileSync(toImportJson, 'utf8')); + +const importMap = new ImportMap({ map: {}, mapUrl: import.meta.url }); + +const resolveModulePath = ( + specifier: string, + cacheMapPath: string +): string | null => { + try { + return importMap.resolve(specifier, cacheMapPath); + } catch { + return null; + } +}; + +export const checkIfNodeProtocol = (modulePath: string) => { + const { protocol = '' } = new URL(modulePath); + return protocol === 'node:'; +}; + +export const checkIfFileProtocol = (modulePath: string) => { + const { protocol = '' } = new URL(modulePath); + return protocol === 'file:'; +}; + +export async function resolve( + specifier: string, + context: Context, + nextResolve: NextResolve +) { + let { parentURL } = context; + + // if (specifier.indexOf('@angular/platform-browser') > -1) { + // specifier = '@angular/platform-browser' + // parentURL = import.meta.url + // run = true; + // } + // + // if (!run) return nextResolve(specifier, context, nextResolve); + + if (!parentURL) return nextResolve(specifier, context, nextResolve); + if (hasRemoteSsr(parentURL)) { + parentURL = import.meta.url; + context.parentURL = parentURL; + } + + const modulePath = resolveModulePath(specifier, parentURL); + + if (!modulePath) return nextResolve(specifier, context, nextResolve); + if (checkIfNodeProtocol(modulePath)) + return nextResolve(modulePath, context, nextResolve); + if (checkIfFileProtocol(modulePath)) + return nextResolve(modulePath, context, nextResolve); + + // console.log('resolve', specifier, importMap.resolve(specifier, parentURL)); + // console.log('resolve',specifier); + if (hasRemoteSsr(specifier)) { + return { + url: specifier, + shortCircuit: true, + }; + } + + return nextResolve(specifier, context, nextResolve); +} + +export async function load( + url: string, + context: Context, + defaultLoad: DefaultLoad +) { + if (hasRemoteSsr(url)) { + const [response, moduleName] = await Promise.all([ + await loadModule(url), + await getModuleNameByUrl(url), + ]); + + return { + format: 'module', + source: response, + shortCircuit: true, + }; + } + return defaultLoad(url, context, defaultLoad); +} diff --git a/apps/ssr-host/src/index.html b/apps/ssr-host/src/index.html new file mode 100644 index 00000000..0d516be3 --- /dev/null +++ b/apps/ssr-host/src/index.html @@ -0,0 +1,13 @@ + + + + + ssr-host + + + + + + + + diff --git a/apps/ssr-host/src/main.server.ts b/apps/ssr-host/src/main.server.ts new file mode 100644 index 00000000..b213183b --- /dev/null +++ b/apps/ssr-host/src/main.server.ts @@ -0,0 +1,12 @@ +// import {register} from 'node:module' +import { setManifestObjectForSsr } from '@angular-architects/native-federation'; +import manifest from './assets/federation.manifest.json'; +setManifestObjectForSsr(manifest); + +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { config } from './app/app.config.server'; + +const bootstrap = () => bootstrapApplication(AppComponent, config); + +export default bootstrap; diff --git a/apps/ssr-host/src/main.ts b/apps/ssr-host/src/main.ts new file mode 100644 index 00000000..dd2160a0 --- /dev/null +++ b/apps/ssr-host/src/main.ts @@ -0,0 +1,6 @@ +import { initFederation } from '@angular-architects/native-federation'; + +initFederation('assets/federation.manifest.json') + .catch((err) => console.error(err)) + .then((_) => import('./bootstrap')) + .catch((err) => console.error(err)); diff --git a/apps/ssr-host/src/styles.css b/apps/ssr-host/src/styles.css new file mode 100644 index 00000000..90d4ee00 --- /dev/null +++ b/apps/ssr-host/src/styles.css @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/ssr-host/src/test-setup.ts b/apps/ssr-host/src/test-setup.ts new file mode 100644 index 00000000..ab1eeeb3 --- /dev/null +++ b/apps/ssr-host/src/test-setup.ts @@ -0,0 +1,8 @@ +// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment +globalThis.ngJest = { + testEnvironmentOptions: { + errorOnUnknownElements: true, + errorOnUnknownProperties: true, + }, +}; +import 'jest-preset-angular/setup-jest'; diff --git a/apps/ssr-host/tsconfig.app.json b/apps/ssr-host/tsconfig.app.json new file mode 100644 index 00000000..e716475c --- /dev/null +++ b/apps/ssr-host/tsconfig.app.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "src/main.ts", + "src/main.server.ts", + "server.ts", + "src/custom-loader.ts" + ], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/ssr-host/tsconfig.editor.json b/apps/ssr-host/tsconfig.editor.json new file mode 100644 index 00000000..a8ac182c --- /dev/null +++ b/apps/ssr-host/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/ssr-host/tsconfig.federation.json b/apps/ssr-host/tsconfig.federation.json new file mode 100644 index 00000000..cc23fab6 --- /dev/null +++ b/apps/ssr-host/tsconfig.federation.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "src/main.ts", + "src/main.server.ts", + "server.ts", + "src/custom-loader.ts" + ], + "include": ["src/**/*.d.ts", "src/custom-loader.ts"], + "exclude": ["jest.config.ts", "src*.spec.ts"] +} diff --git a/apps/ssr-host/tsconfig.json b/apps/ssr-host/tsconfig.json new file mode 100644 index 00000000..2e54778c --- /dev/null +++ b/apps/ssr-host/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es2022", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/ssr-host/tsconfig.spec.json b/apps/ssr-host/tsconfig.spec.json new file mode 100644 index 00000000..53fbfcdc --- /dev/null +++ b/apps/ssr-host/tsconfig.spec.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "target": "es2016", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/libs/native-federation-core/src/build.ts b/libs/native-federation-core/src/build.ts index d22d8a46..c30cdf99 100644 --- a/libs/native-federation-core/src/build.ts +++ b/libs/native-federation-core/src/build.ts @@ -14,7 +14,10 @@ export { EntryPoint, } from './lib/core/build-adapter'; export { withNativeFederation } from './lib/config/with-native-federation'; -export { buildForFederation } from './lib/core/build-for-federation'; +export { + buildForFederation, + buildForCustomLoader, +} from './lib/core/build-for-federation'; export { bundleExposedAndMappings } from './lib/core/bundle-exposed-and-mappings'; export { diff --git a/libs/native-federation-core/src/lib/core/build-for-federation.ts b/libs/native-federation-core/src/lib/core/build-for-federation.ts index eaf3c327..2177db98 100644 --- a/libs/native-federation-core/src/lib/core/build-for-federation.ts +++ b/libs/native-federation-core/src/lib/core/build-for-federation.ts @@ -10,7 +10,9 @@ import { describeExposed, describeSharedMappings, } from './bundle-exposed-and-mappings'; - +import { bundle } from '../utils/build-utils'; +import * as promisesFs from 'fs/promises'; +import * as path from 'path'; export interface BuildParams { skipMappingsAndExposed: boolean; } @@ -19,6 +21,48 @@ export const defaultBuildParams: BuildParams = { skipMappingsAndExposed: false, }; +export async function buildForCustomLoader( + config: NormalizedFederationConfig, + fedOptions: FederationOptions, + buildParams = defaultBuildParams +) { + if (!fedOptions.customLoader) throw new Error('No custom loader provided'); + + const nameCustomLoader = path.parse(fedOptions.customLoader).name; + + await bundle({ + entryPoints: [ + { + fileName: path.join(fedOptions.workspaceRoot, fedOptions.customLoader), + outName: nameCustomLoader, + }, + ], + tsConfigPath: fedOptions.tsConfig, + external: ['fs', 'path', 'url'], + outdir: path.join(fedOptions.workspaceRoot, fedOptions.outputPathServer), + mappedPaths: config.sharedMappings, + dev: fedOptions.dev, + kind: 'shared-package', + hash: false, + esm: true, + }); + await new Promise((resolve) => { + setTimeout(() => resolve(void 0), 2000); + }); + await promisesFs.rename( + path.join( + fedOptions.workspaceRoot, + fedOptions.outputPathServer, + nameCustomLoader + ) + '.js', + path.join( + fedOptions.workspaceRoot, + fedOptions.outputPathServer, + nameCustomLoader + ) + '.mjs' + ); +} + export async function buildForFederation( config: NormalizedFederationConfig, fedOptions: FederationOptions, diff --git a/libs/native-federation-core/src/lib/core/bundle-shared.ts b/libs/native-federation-core/src/lib/core/bundle-shared.ts index 2ade2423..a9f3b070 100644 --- a/libs/native-federation-core/src/lib/core/bundle-shared.ts +++ b/libs/native-federation-core/src/lib/core/bundle-shared.ts @@ -53,9 +53,19 @@ export async function bundleShared( fedOptions.outputPath ); + const fullOutputPathServer = path.join( + fedOptions.workspaceRoot, + fedOptions.outputPathServer + ); + const exptedResults = allEntryPoints.map((ep) => path.join(fullOutputPath, ep.outName) ); + + const exptedResultsServer = allEntryPoints.map((ep) => + path.join(fullOutputPathServer, ep.outName) + ); + const entryPoints = allEntryPoints.filter( (ep) => !fs.existsSync(path.join(cachePath, ep.outName)) ); @@ -87,6 +97,14 @@ export async function bundleShared( copyFileIfExists(cachedFile, fileName); copySrcMapIfExists(cachedFile, fileName); } + + for (const fileName of exptedResultsServer) { + const outFileName = path.basename(fileName); + const cachedFile = path.join(cachePath, outFileName); + + copyFileIfExists(cachedFile, fileName); + copySrcMapIfExists(cachedFile, fileName); + } } catch (e) { logger.error('Error bundling shared npm package '); if (e instanceof Error) { diff --git a/libs/native-federation-core/src/lib/core/federation-options.ts b/libs/native-federation-core/src/lib/core/federation-options.ts index 7c5cb51b..b40adf6f 100644 --- a/libs/native-federation-core/src/lib/core/federation-options.ts +++ b/libs/native-federation-core/src/lib/core/federation-options.ts @@ -1,10 +1,13 @@ export interface FederationOptions { workspaceRoot: string; outputPath: string; + outputPathServer: string; federationConfig: string; tsConfig?: string; verbose?: boolean; dev?: boolean; watch?: boolean; packageJson?: string; + isSrr: boolean; + customLoader?: string; } diff --git a/libs/native-federation-core/src/lib/core/write-federation-info.ts b/libs/native-federation-core/src/lib/core/write-federation-info.ts index 12bc5520..2aaa7de8 100644 --- a/libs/native-federation-core/src/lib/core/write-federation-info.ts +++ b/libs/native-federation-core/src/lib/core/write-federation-info.ts @@ -12,5 +12,11 @@ export function writeFederationInfo( fedOptions.outputPath, 'remoteEntry.json' ); + const metaDataPathServer = path.join( + fedOptions.workspaceRoot, + fedOptions.outputPathServer, + 'remoteEntry.json' + ); fs.writeFileSync(metaDataPath, JSON.stringify(federationInfo, null, 2)); + fs.writeFileSync(metaDataPathServer, JSON.stringify(federationInfo, null, 2)); } diff --git a/libs/native-federation-core/src/lib/core/write-import-map.ts b/libs/native-federation-core/src/lib/core/write-import-map.ts index 5fa39f11..7bc859d8 100644 --- a/libs/native-federation-core/src/lib/core/write-import-map.ts +++ b/libs/native-federation-core/src/lib/core/write-import-map.ts @@ -20,5 +20,11 @@ export function writeImportMap( fedOption.outputPath, 'importmap.json' ); + const importMapPathServer = path.join( + fedOption.workspaceRoot, + fedOption.outputPathServer, + 'importmap.json' + ); fs.writeFileSync(importMapPath, JSON.stringify(importMap, null, 2)); + fs.writeFileSync(importMapPathServer, JSON.stringify(importMap, null, 2)); } diff --git a/libs/native-federation-runtime/src/index.ts b/libs/native-federation-runtime/src/index.ts index 0d25076e..686ead47 100644 --- a/libs/native-federation-runtime/src/index.ts +++ b/libs/native-federation-runtime/src/index.ts @@ -1,3 +1,4 @@ export * from './lib/init-federation'; +export * from './lib/init-federation-ssr'; export * from './lib/load-remote-module'; export * from './lib/model/federation-info'; diff --git a/libs/native-federation-runtime/src/lib/init-federation-ssr.ts b/libs/native-federation-runtime/src/lib/init-federation-ssr.ts new file mode 100644 index 00000000..f0d53a9e --- /dev/null +++ b/libs/native-federation-runtime/src/lib/init-federation-ssr.ts @@ -0,0 +1,78 @@ +import { joinPaths, getDirectory } from './utils/path-utils'; + +let manifestObject: Record = {}; +const remoteEntryJsonMap: Record> = {}; +const saveModuleByUrl: Record = {}; +export function setManifestObjectForSsr(manifest: Record) { + manifestObject = manifest; +} + +export function hasRemoteSsr(name: string): boolean { + if (!name.startsWith('http')) return false; + + const remoteEntryUrl = getRemoteEntry(name); + const hasRemoteEntryUrl = Object.values(manifestObject).some( + (i) => i === remoteEntryUrl + ); + if (!hasRemoteEntryUrl) throw new Error('unknown remote ' + name); + + return true; +} + +export async function loadModule(url: string) { + if (saveModuleByUrl[url]) return saveModuleByUrl[url]; + + const result = await fetch(url).then((r) => r.text()); + + saveModuleByUrl[url] = result; + return result; +} + +const getRemoteEntry = (url: string) => + new URL('remoteEntry.json', new URL(url).origin).toString(); + +export async function getModuleNameByUrl(url: string) { + const remoteEntryUrl = getRemoteEntry(url); + const result = Object.entries(manifestObject).find( + ([key, val]) => manifestObject[key] === remoteEntryUrl + ); + if (!result) throw new Error('unknown remote ' + url); + const moduleName = result[0]; + return `${moduleName}${new URL(url).pathname}`; +} + +import { LoadRemoteModuleOptions } from './load-remote-module'; + +export async function loadRemoteModuleSsr( + options: LoadRemoteModuleOptions +): Promise { + const { remoteName, exposedModule } = options; + if (!remoteName || !manifestObject[remoteName]) + throw new Error('unknown remote ' + remoteName); + const remoteEntryJson = await getRemoteEntryJson(remoteName); + if (!remoteEntryJson['exposes'] || !Array.isArray(remoteEntryJson['exposes'])) + throw new Error( + 'remoteEntry for "' + remoteName + '" doesn\'t have "exposes"' + ); + + const exposed = remoteEntryJson['exposes'].find( + (i) => i.key === exposedModule + ); + const resultUrl = joinPaths( + getDirectory(manifestObject[remoteName]), + exposed.outFileName + ); + + return import(/* @vite-ignore */ resultUrl); +} + +async function getRemoteEntryJson( + remoteName: string +): Promise> { + if (remoteEntryJsonMap[remoteName]) return remoteEntryJsonMap[remoteName]; + const remoteEntryJson = await fetch(manifestObject[remoteName]).then((r) => + r.json() + ); + remoteEntryJsonMap[remoteName] = remoteEntryJson; + return remoteEntryJson; +} diff --git a/libs/native-federation-runtime/src/lib/load-remote-module.ts b/libs/native-federation-runtime/src/lib/load-remote-module.ts index 333eecaf..23ecbe61 100644 --- a/libs/native-federation-runtime/src/lib/load-remote-module.ts +++ b/libs/native-federation-runtime/src/lib/load-remote-module.ts @@ -29,6 +29,12 @@ export async function loadRemoteModule( ): Promise { const options = normalizeOptions(optionsOrRemoteName, exposedModule); + // @ts-ignore + if (typeof process !== 'undefined' && process.version) { + return await import('./init-federation-ssr').then((r) => + r.loadRemoteModuleSsr(options) + ); + } await ensureRemoteInitialized(options); const remoteName = getRemoteNameByOptions(options); diff --git a/libs/native-federation/src/builders/build/builder.ts b/libs/native-federation/src/builders/build/builder.ts index 4154d22a..fd1f3f95 100644 --- a/libs/native-federation/src/builders/build/builder.ts +++ b/libs/native-federation/src/builders/build/builder.ts @@ -1,6 +1,7 @@ import * as path from 'path'; import * as fs from 'fs'; import * as mrmime from 'mrmime'; +import * as url from 'url'; import { buildApplication, ApplicationBuilderOptions } from '@angular/build'; import { @@ -27,7 +28,10 @@ import { } from '../../utils/angular-esbuild-adapter'; import { getExternals } from '@softarc/native-federation/build'; import { loadFederationConfig } from '@softarc/native-federation/build'; -import { buildForFederation } from '@softarc/native-federation/build'; +import { + buildForFederation, + buildForCustomLoader, +} from '@softarc/native-federation/build'; import { targetFromTargetString } from '@angular-devkit/architect'; import { NfBuilderSchema } from './schema'; @@ -39,7 +43,7 @@ import { } from '../../utils/dev-server'; import { RebuildHubs } from '../../utils/rebuild-events'; import { updateIndexHtml, updateScriptTags } from '../../utils/updateIndexHtml'; -import { existsSync, mkdirSync, rmSync } from 'fs'; +import { existsSync, mkdirSync, rmSync, promises as promisesFs } from 'fs'; import { EsBuildResult, MemResults, @@ -50,6 +54,7 @@ import { createSharedMappingsPlugin } from '../../utils/shared-mappings-plugin'; // import { NextHandleFunction } from 'vite'; import { PluginBuild } from 'esbuild'; import { FederationInfo } from '@softarc/native-federation-runtime'; +import { register } from 'node:module'; function _buildApplication(options, context, pluginsOrExtensions) { let extensions; @@ -156,19 +161,74 @@ export async function* runBuilder( outputOptions.browser ); + const serverOutputPath = path.join(outputOptions.base, outputOptions.server); + const fedOptions: FederationOptions = { workspaceRoot: context.workspaceRoot, outputPath: browserOutputPath, + outputPathServer: serverOutputPath, federationConfig: infereConfigPath(options.tsConfig), tsConfig: options.tsConfig, verbose: options.verbose, watch: false, // options.watch, dev: !!nfOptions.dev, + isSrr: !!options.ssr, + customLoader: nfOptions.customLoader, }; + if (fedOptions.isSrr && !nfOptions.customLoader) { + throw new Error('Should be set custom-loader'); + } + const config = await loadFederationConfig(fedOptions); const externals = getExternals(config); + const plugins = [ + { + name: 'entryPointsInterceptor', + setup(build) { + if (build.initialOptions.platform === 'browser') return; + + build.initialOptions.external = externals.filter((e) => e !== 'tslib'); + + build.initialOptions.minifyIdentifiers = false; + + build.initialOptions.entryPoints = { + ...build.initialOptions.entryPoints, + ['custom-loader']: nfOptions.customLoader, + }; + // build.onEnd(async (result) => { + // + // const pathToCustomLoader = + // fedOptions.dev + // ? path.join(context.workspaceRoot, `.angular/vite-root`, context.target.project) + // : path.join(context.workspaceRoot, fedOptions.outputPathServer); + // + // const pathToResultCustomLoader = path.join(context.workspaceRoot, fedOptions.outputPathServer); + // const relativePathToResultCustomLoader = path.relative(pathToCustomLoader, pathToResultCustomLoader); + // const nameCustomLoader = nfOptions.customLoader.split('/').at(-1).split('.').at(0); + // const fullNameCustomLoader = `${nameCustomLoader}.mjs`; + // + // const replaceStr = relativePathToResultCustomLoader + // ? path.join(relativePathToResultCustomLoader, fullNameCustomLoader) + // : `.${path.sep}${fullNameCustomLoader}`; + // let mainServer = result.outputFiles.find((files) => files.path.endsWith('main.server.mjs')); + // + // const regex = /import\s+{?\s*main_server_default\s*}?\s+from\s+["'][^"']*\/([^"']+)["'];/; + // const match = mainServer.text.match(regex); + // + // if (match) { + // const modulePath = match[1]; + // mainServer = result.outputFiles.find((files) => files.path.endsWith(modulePath)); + // } + // + // const resultContent = new TextDecoder().decode(mainServer.contents) + // .replace(`./${nameCustomLoader}`, replaceStr); + // mainServer.contents = new TextEncoder().encode(resultContent); + // + // }); + }, + }, createSharedMappingsPlugin(config.sharedMappings), { name: 'externals', @@ -191,7 +251,7 @@ export async function* runBuilder( fedOptions.outputPath, url ); - + const exists = fs.existsSync(fileName); if (url !== '/' && url !== '' && exists) { @@ -239,6 +299,26 @@ export async function* runBuilder( } catch (e) { process.exit(1); } + if (fedOptions.isSrr && nfOptions.dev) { + //Need register before run dev server + await buildForCustomLoader(config, fedOptions); + const nameCustomLoader = nfOptions.customLoader + .split('/') + .at(-1) + .split('.') + .at(0); + const fullNameCustomLoader = `${nameCustomLoader}.mjs`; + const parentPath = url + .pathToFileURL( + path.join( + context.workspaceRoot, + fedOptions.outputPathServer, + fullNameCustomLoader + ) + ) + .toString(); + register('./' + fullNameCustomLoader, parentPath); + } options.deleteOutputPath = false; @@ -246,7 +326,6 @@ export async function* runBuilder( // TODO: Clarify if buildApplication is needed `executeDevServerBuilder` seems to choose the correct DevServer const appBuilderName = '@angular-devkit/build-angular:application'; - const builderRun = nfOptions.dev ? serveWithVite( normOuterOptions, @@ -306,6 +385,10 @@ export async function* runBuilder( // logger.info('Rebuilding federation artefacts ...'); // await Promise.all([rebuildEvents.rebuild.emit()]); await buildForFederation(config, fedOptions, externals); + if (fedOptions.isSrr && nfOptions.dev) { + await buildForCustomLoader(config, fedOptions); + } + logger.info('Done!'); if (runServer) { diff --git a/libs/native-federation/src/builders/build/schema.d.ts b/libs/native-federation/src/builders/build/schema.d.ts index 3fae81c9..5d0ee0a4 100644 --- a/libs/native-federation/src/builders/build/schema.d.ts +++ b/libs/native-federation/src/builders/build/schema.d.ts @@ -12,4 +12,5 @@ export interface NfBuilderSchema extends JsonObject { skipHtmlTransform: boolean; esmsInitOptions: ESMSInitOptions; baseHref?: string; + customLoader: string; } // eslint-disable-line diff --git a/libs/native-federation/src/builders/build/schema.json b/libs/native-federation/src/builders/build/schema.json index 811c6b21..561e6afc 100644 --- a/libs/native-federation/src/builders/build/schema.json +++ b/libs/native-federation/src/builders/build/schema.json @@ -52,6 +52,9 @@ "default": { "shimMode": true } + }, + "customLoader": { + "type": "string" } } } diff --git a/libs/native-federation/src/utils/angular-esbuild-adapter.ts b/libs/native-federation/src/utils/angular-esbuild-adapter.ts index db148c52..db058f91 100644 --- a/libs/native-federation/src/utils/angular-esbuild-adapter.ts +++ b/libs/native-federation/src/utils/angular-esbuild-adapter.ts @@ -306,7 +306,6 @@ async function runEsbuild( const ctx = await esbuild.context(config); const result = await ctx.rebuild(); - const memOnly = dev && kind === 'mapping-or-exposed' && !!_memResultHandler; const writtenFiles = writeResult(result, outdir, memOnly); @@ -393,7 +392,6 @@ function writeResult( memOnly: boolean ) { const writtenFiles: string[] = []; - if (memOnly) { _memResultHandler(result.outputFiles, outdir); } diff --git a/libs/native-federation/src/utils/updateIndexHtml.ts b/libs/native-federation/src/utils/updateIndexHtml.ts index d65c31c1..4ce83f1f 100644 --- a/libs/native-federation/src/utils/updateIndexHtml.ts +++ b/libs/native-federation/src/utils/updateIndexHtml.ts @@ -8,7 +8,10 @@ export function updateIndexHtml( nfOptions: NfBuilderSchema ) { const outputPath = path.join(fedOptions.workspaceRoot, fedOptions.outputPath); - const indexPath = path.join(outputPath, 'index.html'); + + const indexName = fedOptions.isSrr ? 'index.csr.html' : 'index.html'; + + const indexPath = path.join(outputPath, indexName); const mainName = fs .readdirSync(outputPath) .find((f) => f.startsWith('main') && f.endsWith('.js')); diff --git a/package-lock.json b/package-lock.json index 16bfc7df..16afcbcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,13 @@ "@angular/forms": "18.1.3", "@angular/platform-browser": "18.1.3", "@angular/platform-browser-dynamic": "18.1.3", + "@angular/platform-server": "~18.1.0", "@angular/router": "18.1.3", + "@angular/ssr": "18.1.3", + "@jspm/import-map": "^1.1.0", "@module-federation/vite": "^0.2.6", "es-module-shims": "^1.5.12", + "express": "~4.18.2", "rxjs": "^7.0.0", "tslib": "^2.0.0", "zone.js": "0.14.2" @@ -55,6 +59,7 @@ "@swc/helpers": "0.5.12", "@types/browser-sync": "^2.29.0", "@types/cross-spawn": "^6.0.2", + "@types/express": "4.17.14", "@types/jest": "29.5.11", "@types/node": "^18.16.9", "@types/npmlog": "^4.1.4", @@ -2136,6 +2141,25 @@ "@angular/platform-browser": "18.1.3" } }, + "node_modules/@angular/platform-server": { + "version": "18.1.5", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-18.1.5.tgz", + "integrity": "sha512-d48uTFOyDWJWQp/p3cJAmdOJTPpnY2fZKMKc+HIV6vIMcxlCRse+BhMaPOWT44po+XEmrSPNmY3go0BQpneRKg==", + "dependencies": { + "tslib": "^2.3.0", + "xhr2": "^0.2.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/animations": "18.1.5", + "@angular/common": "18.1.5", + "@angular/compiler": "18.1.5", + "@angular/core": "18.1.5", + "@angular/platform-browser": "18.1.5" + } + }, "node_modules/@angular/router": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.1.3.tgz", @@ -2154,6 +2178,19 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/ssr": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-18.1.3.tgz", + "integrity": "sha512-QJWCdKd2R67tzJozkf5koRfxVQUwbOdq03s21V06xiVKGZWQu/m6GnZmz15pORVpDrKEslMx7p/dR3kmBvzzqA==", + "dependencies": { + "critters": "0.0.24", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^18.0.0", + "@angular/core": "^18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -6622,6 +6659,11 @@ "tslib": "2" } }, + "node_modules/@jspm/import-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jspm/import-map/-/import-map-1.1.0.tgz", + "integrity": "sha512-vmk583YnMi4fmqeXbWIBiyzFu+vqVZ5VCoaa6H4xeSQy5E6JAWtmcq72OAMFTeSTqw7xxHQIJFq2OlHKdUWitQ==" + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -9949,6 +9991,26 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, + "node_modules/@softarc/native-federation": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@softarc/native-federation/-/native-federation-2.0.10.tgz", + "integrity": "sha512-SCjAd8F2nO7VPr9TEadpbqQWI8RHd+9HAYbdjJxKXNtmi9Nz5EnIZshTRgnQlMth59FrKlQ2fyoyRWcmYKNe0g==", + "peer": true, + "dependencies": { + "@softarc/native-federation-runtime": "2.0.10", + "json5": "^2.2.0", + "npmlog": "^6.0.2" + } + }, + "node_modules/@softarc/native-federation-runtime": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@softarc/native-federation-runtime/-/native-federation-runtime-2.0.10.tgz", + "integrity": "sha512-8CETrqtLkYL1i6bwmqNe/1GwNVQYfayf3b2PSHJ4/n1QR2Dk84RhbpnAPgGVG7XqzRhDYwV2xsJyhVJdWkxQ3A==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + } + }, "node_modules/@swc-node/core": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/@swc-node/core/-/core-1.13.3.tgz", @@ -10270,6 +10332,16 @@ "tslib": "^2.4.0" } }, + "node_modules/@swc/types": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", + "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "dev": true, + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -10531,13 +10603,13 @@ "dev": true }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", "dev": true, "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", + "@types/express-serve-static-core": "^4.17.18", "@types/qs": "*", "@types/serve-static": "*" } @@ -11776,7 +11848,6 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -11991,7 +12062,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -12052,8 +12122,7 @@ "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" }, "node_modules/arch": { "version": "2.2.0", @@ -12079,7 +12148,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "dev": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -12112,8 +12180,7 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/array-union": { "version": "2.1.0", @@ -12890,7 +12957,6 @@ "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dev": true, "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -12914,7 +12980,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "dependencies": { "ms": "2.0.0" } @@ -12923,7 +12988,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -12932,14 +12996,12 @@ "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/body-parser/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, "dependencies": { "ee-first": "1.1.1" }, @@ -12951,7 +13013,6 @@ "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, "dependencies": { "side-channel": "^1.0.4" }, @@ -12966,7 +13027,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -12990,8 +13050,7 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -13395,7 +13454,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -13523,7 +13581,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -13925,7 +13982,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, "bin": { "color-support": "bin.js" } @@ -14156,14 +14212,12 @@ "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, "dependencies": { "safe-buffer": "5.2.1" }, @@ -14175,7 +14229,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -14198,8 +14251,7 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/cookies": { "version": "0.9.1", @@ -14472,7 +14524,6 @@ "version": "0.0.24", "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.24.tgz", "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==", - "dev": true, "license": "Apache-2.0", "dependencies": { "chalk": "^4.1.0", @@ -14488,7 +14539,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -14504,7 +14554,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -14521,7 +14570,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -14534,14 +14582,12 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/critters/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -14724,7 +14770,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -14753,7 +14798,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, "engines": { "node": ">= 6" }, @@ -15352,7 +15396,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -15386,14 +15429,12 @@ "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -15531,7 +15572,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -15545,7 +15585,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, "funding": [ { "type": "github", @@ -15570,7 +15609,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -15585,7 +15623,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -15770,8 +15807,7 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { "version": "3.1.9", @@ -15810,8 +15846,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -15826,7 +15861,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -15978,7 +16012,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -16059,7 +16092,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -16071,7 +16103,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -16150,8 +16181,7 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -16619,7 +16649,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -16813,7 +16842,6 @@ "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dev": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -16861,7 +16889,6 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -16870,7 +16897,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "dependencies": { "ms": "2.0.0" } @@ -16879,7 +16905,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -16889,7 +16914,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -16906,14 +16930,12 @@ "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, "dependencies": { "ee-first": "1.1.1" }, @@ -16925,7 +16947,6 @@ "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, "dependencies": { "side-channel": "^1.0.4" }, @@ -16940,7 +16961,6 @@ "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -16963,14 +16983,12 @@ "node_modules/express/node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/express/node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dev": true, "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -16985,7 +17003,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -17700,7 +17717,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -17722,7 +17738,6 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -17823,7 +17838,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -17832,7 +17846,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "dev": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -17882,7 +17895,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -18103,7 +18115,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -18194,7 +18205,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -18203,7 +18213,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -18215,7 +18224,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -18227,7 +18235,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -18254,14 +18261,12 @@ "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -18393,7 +18398,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -18476,7 +18480,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -18492,7 +18495,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -18724,7 +18726,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -18894,8 +18895,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "4.1.3", @@ -18939,7 +18939,6 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, "engines": { "node": ">= 0.10" } @@ -19029,7 +19028,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -19674,6 +19672,23 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/jest-circus/node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/jest-circus/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -19708,6 +19723,24 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-circus/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest-circus/node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -21284,7 +21317,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -22341,7 +22373,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -22361,8 +22392,7 @@ "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -22383,7 +22413,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -22417,7 +22446,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, "bin": { "mime": "cli.js" }, @@ -22429,7 +22457,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -22438,7 +22465,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -22876,7 +22902,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -22939,7 +22964,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -23921,7 +23945,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "dev": true, "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -23936,7 +23959,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -24132,7 +24154,6 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -24677,7 +24698,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -24743,8 +24763,7 @@ "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "node_modules/path-type": { "version": "4.0.0", @@ -24784,7 +24803,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -25008,7 +25026,6 @@ "version": "8.4.38", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -25537,7 +25554,6 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", - "dev": true, "license": "MIT" }, "node_modules/postcss-merge-longhand": { @@ -26365,7 +26381,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -26513,7 +26528,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -26552,7 +26566,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -27054,7 +27067,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -27082,8 +27094,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { "version": "1.77.6", @@ -27456,14 +27467,12 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -27479,8 +27488,7 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/shallow-clone": { "version": "3.0.1", @@ -27528,7 +27536,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -27545,8 +27552,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sigstore": { "version": "2.3.1", @@ -27815,7 +27821,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -28123,7 +28128,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -28145,7 +28149,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -28174,7 +28177,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -28776,7 +28778,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, "engines": { "node": ">=0.6" } @@ -29257,7 +29258,6 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -29452,7 +29452,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -29537,14 +29536,12 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, "engines": { "node": ">= 0.4.0" } @@ -29619,7 +29616,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -30838,6 +30834,18 @@ } } }, + "node_modules/webpack-dev-server/node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, "node_modules/webpack-dev-server/node_modules/connect-history-api-fallback": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", @@ -31218,7 +31226,6 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -31383,6 +31390,14 @@ } } }, + "node_modules/xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz", + "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", diff --git a/package.json b/package.json index 3bf4dff0..76b8a59f 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,13 @@ "@angular/forms": "18.1.3", "@angular/platform-browser": "18.1.3", "@angular/platform-browser-dynamic": "18.1.3", + "@angular/platform-server": "~18.1.0", "@angular/router": "18.1.3", + "@angular/ssr": "18.1.3", + "@jspm/import-map": "^1.1.0", "@module-federation/vite": "^0.2.6", "es-module-shims": "^1.5.12", + "express": "~4.18.2", "rxjs": "^7.0.0", "tslib": "^2.0.0", "zone.js": "0.14.2" @@ -81,6 +85,7 @@ "@swc/helpers": "0.5.12", "@types/browser-sync": "^2.29.0", "@types/cross-spawn": "^6.0.2", + "@types/express": "4.17.14", "@types/jest": "29.5.11", "@types/node": "^18.16.9", "@types/npmlog": "^4.1.4",