@crownpeak/dqm-react-component-dev-mcp 1.2.2 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/.glfrc.json +1 -1
- package/data/CHANGELOG.md +14 -0
- package/data/LICENSE +1 -1
- package/data/package.json +4 -3
- package/data/scripts/npm-set-version.mjs +41 -0
- package/data/website/LICENSE +22 -0
- package/data/website/README.md +95 -0
- package/data/website/RELEASE-NOTES.md +42 -0
- package/data/website/components.template.json +30 -0
- package/data/website/eslint.config.mjs +16 -0
- package/data/website/next.config.ts +25 -0
- package/data/website/package.json +58 -0
- package/data/website/postcss.config.mjs +7 -0
- package/data/website/public/.nojekyll +0 -0
- package/data/website/public/dqm-react-component/mockServiceWorker.js +349 -0
- package/data/website/public/mockServiceWorker.js +349 -0
- package/data/website/public/robots.txt +7 -0
- package/data/website/public/site.webmanifest +1 -0
- package/data/website/public/sitemap.xml +9 -0
- package/data/website/tsconfig.json +41 -0
- package/package.json +2 -2
package/data/.glfrc.json
CHANGED
package/data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.4] - 2026-01-08
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Marketing Website**: New Next.js marketing/documentation website deployed to GitHub Pages
|
|
12
|
+
- Hero section with animated feature highlights
|
|
13
|
+
- Interactive demo section with live sidebar preview
|
|
14
|
+
- Integration section with code examples and copy functionality
|
|
15
|
+
- Multi-language support (English, German, Spanish) using react-i18next
|
|
16
|
+
- Framer Motion animations throughout
|
|
17
|
+
- Built with Tailwind CSS and shadcn/ui components
|
|
18
|
+
- WCAG AA accessibility compliant (proper aria-labels, 4.5:1+ contrast ratios)
|
|
19
|
+
- Static export for GitHub Pages hosting
|
|
20
|
+
- **AI Agent Documentation**: Added comprehensive Website section to `.github/copilot-instructions.md`
|
|
21
|
+
|
|
8
22
|
## [1.2.2] - 2026-01-08
|
|
9
23
|
|
|
10
24
|
### Fixed
|
package/data/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2025 Crownpeak Technology GmbH
|
|
3
|
+
Copyright (c) 2025-2026 Crownpeak Technology GmbH
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/data/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crownpeak/dqm-react-component",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A React component for Crownpeak Digital Quality Management (DQM) integration",
|
|
6
6
|
"type": "module",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"test:e2e:debug": "playwright test --debug",
|
|
85
85
|
"wiki:build": "./scripts/wiki-build.sh",
|
|
86
86
|
"wiki:deploy": "./scripts/wiki-deploy.sh",
|
|
87
|
-
"setVersion": "npm
|
|
87
|
+
"setVersion": "node ./scripts/npm-set-version.mjs",
|
|
88
88
|
"licenses:generate": "generate-license-file --config .glfrc.json",
|
|
89
89
|
"licenses:check": "license-checker --onlyAllow 'MIT;ISC;Apache-2.0;BSD-2-Clause;BSD-3-Clause;0BSD;Unlicense;CC0-1.0;CC-BY-3.0;CC-BY-4.0;Python-2.0;BlueOak-1.0.0;Zlib;WTFPL' --production",
|
|
90
90
|
"licenses:check:all": "npm run licenses:check && npm run licenses:check --prefix mcp-server",
|
|
@@ -92,7 +92,8 @@
|
|
|
92
92
|
"prepare": "husky"
|
|
93
93
|
},
|
|
94
94
|
"workspaces": [
|
|
95
|
-
"mcp-server"
|
|
95
|
+
"mcp-server",
|
|
96
|
+
"website"
|
|
96
97
|
],
|
|
97
98
|
"peerDependencies": {
|
|
98
99
|
"@mui/icons-material": ">=5.0.0",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const rootDir = join(__dirname, '..');
|
|
9
|
+
|
|
10
|
+
// Get version from command line argument
|
|
11
|
+
const newVersion = process.argv[2];
|
|
12
|
+
|
|
13
|
+
if (!newVersion) {
|
|
14
|
+
console.error('Usage: npm run setVersion <version>');
|
|
15
|
+
console.error('Example: npm run setVersion 1.2.3');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Validate version format
|
|
20
|
+
const versionRegex = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
|
|
21
|
+
if (!versionRegex.test(newVersion)) {
|
|
22
|
+
console.error(`Invalid version format: ${newVersion}`);
|
|
23
|
+
console.error('Expected format: x.y.z or x.y.z-prerelease');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Main execution
|
|
28
|
+
console.log(`Setting version to ${newVersion} in all packages...\n`);
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Update root package.json
|
|
32
|
+
execSync(`npm version ${newVersion} --no-git-tag-version`, { cwd: rootDir, stdio: 'inherit' });
|
|
33
|
+
|
|
34
|
+
// Update all workspace packages
|
|
35
|
+
execSync(`npm version ${newVersion} --workspaces --no-git-tag-version`, { cwd: rootDir, stdio: 'inherit' });
|
|
36
|
+
|
|
37
|
+
console.log('\nVersion update complete!');
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(`Failed to update version: ${error.message}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Crownpeak Technology GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Crownpeak DQM React Component - Demo Website
|
|
2
|
+
|
|
3
|
+
Eine interaktive Demo-Website für das [@crownpeak/dqm-react-component](https://www.npmjs.com/package/@crownpeak/dqm-react-component) Paket.
|
|
4
|
+
|
|
5
|
+
## 🚀 Features
|
|
6
|
+
|
|
7
|
+
- **Interaktive Demo** - Erleben Sie das DQM React Component mit Mock-Daten
|
|
8
|
+
- **Mehrsprachig** - Verfügbar in Deutsch, Englisch und Spanisch
|
|
9
|
+
- **Statischer Export** - Läuft ohne Server, nur HTML/CSS/JS
|
|
10
|
+
- **GitHub Pages Ready** - Automatisches Deployment via GitHub Actions
|
|
11
|
+
|
|
12
|
+
## 🛠️ Entwicklung
|
|
13
|
+
|
|
14
|
+
### Voraussetzungen
|
|
15
|
+
|
|
16
|
+
- Node.js 20+
|
|
17
|
+
- Yarn 1.22+
|
|
18
|
+
|
|
19
|
+
### Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yarn install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Entwicklungsserver starten
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn dev
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Die Website ist dann unter [http://localhost:3000](http://localhost:3000) erreichbar.
|
|
32
|
+
|
|
33
|
+
### Produktions-Build
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
yarn build
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Der statische Export wird im `out/`-Ordner erstellt.
|
|
40
|
+
|
|
41
|
+
## 📦 Deployment
|
|
42
|
+
|
|
43
|
+
### GitHub Pages (automatisch)
|
|
44
|
+
|
|
45
|
+
1. Repository auf GitHub erstellen
|
|
46
|
+
2. Code pushen
|
|
47
|
+
3. In den Repository-Einstellungen:
|
|
48
|
+
- **Settings** → **Pages** → **Source**: "GitHub Actions"
|
|
49
|
+
|
|
50
|
+
Der Workflow in `.github/workflows/deploy.yml` übernimmt den Rest.
|
|
51
|
+
|
|
52
|
+
### Manuelles Deployment
|
|
53
|
+
|
|
54
|
+
Der `out/`-Ordner kann auf jedem statischen Hosting-Service deployed werden:
|
|
55
|
+
|
|
56
|
+
- Netlify
|
|
57
|
+
- Vercel
|
|
58
|
+
- AWS S3
|
|
59
|
+
- Cloudflare Pages
|
|
60
|
+
- etc.
|
|
61
|
+
|
|
62
|
+
## 🔧 Konfiguration
|
|
63
|
+
|
|
64
|
+
### Base Path (für Subdirectory-Deployment)
|
|
65
|
+
|
|
66
|
+
Falls die Website nicht im Root einer Domain gehostet wird, setze die Umgebungsvariable:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
NEXT_PUBLIC_BASE_PATH=/mein-pfad yarn build
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 📁 Projektstruktur
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
├── src/
|
|
76
|
+
│ ├── app/ # Next.js App Router
|
|
77
|
+
│ ├── components/ # React Komponenten
|
|
78
|
+
│ ├── i18n/ # Internationalisierung
|
|
79
|
+
│ ├── lib/ # Utility-Funktionen
|
|
80
|
+
│ ├── locales/ # Übersetzungen (de, en, es)
|
|
81
|
+
│ ├── mocks/ # MSW Mock-Handler
|
|
82
|
+
│ └── store/ # Redux Store
|
|
83
|
+
├── public/ # Statische Assets
|
|
84
|
+
└── out/ # Build-Output (nach `yarn build`)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 📄 Lizenz
|
|
88
|
+
|
|
89
|
+
© 2024 Crownpeak Technology GmbH. Alle Rechte vorbehalten.
|
|
90
|
+
|
|
91
|
+
## 🔗 Links
|
|
92
|
+
|
|
93
|
+
- [DQM React Component auf npm](https://www.npmjs.com/package/@crownpeak/dqm-react-component)
|
|
94
|
+
- [GitHub Repository](https://github.com/Crownpeak/dqm-react-component)
|
|
95
|
+
- [Crownpeak Website](https://www.crownpeak.com)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Crownpeak DQM Website - Release Notes
|
|
2
|
+
|
|
3
|
+
## v1.2.4 - 2026-01-08
|
|
4
|
+
|
|
5
|
+
### 🚀 Initial Release
|
|
6
|
+
|
|
7
|
+
The Crownpeak DQM React Component now has an official marketing and documentation website, hosted on GitHub Pages.
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **Hero Section**: Animated landing page with feature highlights and call-to-action
|
|
12
|
+
- **Features Section**: Grid layout showcasing component capabilities
|
|
13
|
+
- **Demo Section**: Interactive sidebar demonstration with live preview
|
|
14
|
+
- **Integration Section**: Code examples with syntax highlighting and one-click copy
|
|
15
|
+
- **Footer**: Links to documentation, GitHub repository, and NPM package
|
|
16
|
+
|
|
17
|
+
#### 🌍 Internationalization
|
|
18
|
+
|
|
19
|
+
- Multi-language support: English (default), German, Spanish
|
|
20
|
+
- Language switcher in navigation
|
|
21
|
+
- Browser language auto-detection
|
|
22
|
+
- URL parameter override (`?lang=de`)
|
|
23
|
+
|
|
24
|
+
#### ♿ Accessibility (WCAG AA)
|
|
25
|
+
|
|
26
|
+
- Proper `aria-label` attributes on all icon-only interactive elements
|
|
27
|
+
- Color contrast ratio of 6.5:1+ on dark backgrounds
|
|
28
|
+
- Semantic HTML structure
|
|
29
|
+
- Keyboard navigation support
|
|
30
|
+
|
|
31
|
+
#### 🛠 Tech Stack
|
|
32
|
+
|
|
33
|
+
- **Framework**: Next.js 14 with App Router
|
|
34
|
+
- **Styling**: Tailwind CSS
|
|
35
|
+
- **Components**: shadcn/ui
|
|
36
|
+
- **Animations**: Framer Motion
|
|
37
|
+
- **i18n**: react-i18next
|
|
38
|
+
- **Deployment**: Static export to GitHub Pages
|
|
39
|
+
|
|
40
|
+
#### 📦 Deployment
|
|
41
|
+
|
|
42
|
+
Automatic deployment via GitHub Actions on push to `main` branch. Manual trigger also available via workflow dispatch.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "default",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/app/globals.css",
|
|
9
|
+
"baseColor": "slate",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "@/components",
|
|
15
|
+
"utils": "@/lib/utils",
|
|
16
|
+
"ui": "@/components/ui",
|
|
17
|
+
"hooks": "@/hooks",
|
|
18
|
+
"lib": "@/lib"
|
|
19
|
+
},
|
|
20
|
+
"iconLibrary": "lucide",
|
|
21
|
+
"registries": {
|
|
22
|
+
"@aceternity": "https://ui.aceternity.com/registry/{name}.json",
|
|
23
|
+
"@shadcnblocks": {
|
|
24
|
+
"url": "https://www.shadcnblocks.com/r/{name}",
|
|
25
|
+
"headers": {
|
|
26
|
+
"Authorization": "Bearer YOUR_API_KEY_HERE"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
2
|
+
import nextVitals from "eslint-config-next/core-web-vitals";
|
|
3
|
+
import nextTs from "eslint-config-next/typescript";
|
|
4
|
+
|
|
5
|
+
const eslintConfig = defineConfig([
|
|
6
|
+
...nextVitals,
|
|
7
|
+
...nextTs,
|
|
8
|
+
globalIgnores([
|
|
9
|
+
".next/**",
|
|
10
|
+
"out/**",
|
|
11
|
+
"build/**",
|
|
12
|
+
"next-env.d.ts",
|
|
13
|
+
]),
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
|
|
3
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
4
|
+
|
|
5
|
+
const nextConfig: NextConfig = {
|
|
6
|
+
// Static export for GitHub Pages
|
|
7
|
+
output: 'export',
|
|
8
|
+
|
|
9
|
+
// Base path for GitHub Pages (repository name)
|
|
10
|
+
// Wird automatisch von GitHub Actions gesetzt, oder manuell konfiguriert
|
|
11
|
+
basePath: isProd ? process.env.NEXT_PUBLIC_BASE_PATH || '' : '',
|
|
12
|
+
|
|
13
|
+
// Asset prefix for proper asset loading on GitHub Pages
|
|
14
|
+
assetPrefix: isProd ? process.env.NEXT_PUBLIC_BASE_PATH || '' : '',
|
|
15
|
+
|
|
16
|
+
// Disable image optimization for static export
|
|
17
|
+
images: {
|
|
18
|
+
unoptimized: true,
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Trailing slash for better compatibility with static hosting
|
|
22
|
+
trailingSlash: true,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default nextConfig;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crownpeak/dqm-react-component-website",
|
|
3
|
+
"version": "1.2.4",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "eslint"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@crownpeak/dqm-react-component": "^1.2.2",
|
|
13
|
+
"@emotion/react": "^11.14.0",
|
|
14
|
+
"@emotion/styled": "^11.14.1",
|
|
15
|
+
"@mui/icons-material": "^7.3.7",
|
|
16
|
+
"@mui/material": "^7.3.7",
|
|
17
|
+
"@radix-ui/react-accordion": "^1.2.12",
|
|
18
|
+
"@radix-ui/react-avatar": "^1.1.11",
|
|
19
|
+
"@radix-ui/react-progress": "^1.1.8",
|
|
20
|
+
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
21
|
+
"@radix-ui/react-separator": "^1.1.8",
|
|
22
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
23
|
+
"@radix-ui/react-tabs": "^1.1.13",
|
|
24
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
25
|
+
"@reduxjs/toolkit": "^2.11.2",
|
|
26
|
+
"class-variance-authority": "^0.7.1",
|
|
27
|
+
"clsx": "^2.1.1",
|
|
28
|
+
"framer-motion": "^12.24.10",
|
|
29
|
+
"i18next": "^25.7.4",
|
|
30
|
+
"i18next-browser-languagedetector": "^8.2.0",
|
|
31
|
+
"lucide-react": "^0.562.0",
|
|
32
|
+
"next": "16.1.1",
|
|
33
|
+
"react": "19.2.3",
|
|
34
|
+
"react-dom": "19.2.3",
|
|
35
|
+
"react-i18next": "^16.5.1",
|
|
36
|
+
"react-redux": "^9.2.0",
|
|
37
|
+
"tailwind-merge": "^3.4.0",
|
|
38
|
+
"vanilla-cookieconsent": "^3.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@tailwindcss/postcss": "^4",
|
|
42
|
+
"@types/node": "^20",
|
|
43
|
+
"@types/react": "^19",
|
|
44
|
+
"@types/react-dom": "^19",
|
|
45
|
+
"eslint": "^9",
|
|
46
|
+
"eslint-config-next": "16.1.1",
|
|
47
|
+
"husky": "^9.1.7",
|
|
48
|
+
"msw": "^2.12.7",
|
|
49
|
+
"tailwindcss": "^4",
|
|
50
|
+
"typescript": "^5"
|
|
51
|
+
},
|
|
52
|
+
"packageManager": "yarn@1.22.22",
|
|
53
|
+
"msw": {
|
|
54
|
+
"workerDirectory": [
|
|
55
|
+
"public"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/* tslint:disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mock Service Worker.
|
|
6
|
+
* @see https://github.com/mswjs/msw
|
|
7
|
+
* - Please do NOT modify this file.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const PACKAGE_VERSION = '2.12.7'
|
|
11
|
+
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
|
12
|
+
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
|
13
|
+
const activeClientIds = new Set()
|
|
14
|
+
|
|
15
|
+
addEventListener('install', function () {
|
|
16
|
+
self.skipWaiting()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
addEventListener('activate', function (event) {
|
|
20
|
+
event.waitUntil(self.clients.claim())
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
addEventListener('message', async function (event) {
|
|
24
|
+
const clientId = Reflect.get(event.source || {}, 'id')
|
|
25
|
+
|
|
26
|
+
if (!clientId || !self.clients) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const client = await self.clients.get(clientId)
|
|
31
|
+
|
|
32
|
+
if (!client) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const allClients = await self.clients.matchAll({
|
|
37
|
+
type: 'window',
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
switch (event.data) {
|
|
41
|
+
case 'KEEPALIVE_REQUEST': {
|
|
42
|
+
sendToClient(client, {
|
|
43
|
+
type: 'KEEPALIVE_RESPONSE',
|
|
44
|
+
})
|
|
45
|
+
break
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
case 'INTEGRITY_CHECK_REQUEST': {
|
|
49
|
+
sendToClient(client, {
|
|
50
|
+
type: 'INTEGRITY_CHECK_RESPONSE',
|
|
51
|
+
payload: {
|
|
52
|
+
packageVersion: PACKAGE_VERSION,
|
|
53
|
+
checksum: INTEGRITY_CHECKSUM,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'MOCK_ACTIVATE': {
|
|
60
|
+
activeClientIds.add(clientId)
|
|
61
|
+
|
|
62
|
+
sendToClient(client, {
|
|
63
|
+
type: 'MOCKING_ENABLED',
|
|
64
|
+
payload: {
|
|
65
|
+
client: {
|
|
66
|
+
id: client.id,
|
|
67
|
+
frameType: client.frameType,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
case 'CLIENT_CLOSED': {
|
|
75
|
+
activeClientIds.delete(clientId)
|
|
76
|
+
|
|
77
|
+
const remainingClients = allClients.filter((client) => {
|
|
78
|
+
return client.id !== clientId
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Unregister itself when there are no more clients
|
|
82
|
+
if (remainingClients.length === 0) {
|
|
83
|
+
self.registration.unregister()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
break
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
addEventListener('fetch', function (event) {
|
|
92
|
+
const requestInterceptedAt = Date.now()
|
|
93
|
+
|
|
94
|
+
// Bypass navigation requests.
|
|
95
|
+
if (event.request.mode === 'navigate') {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Opening the DevTools triggers the "only-if-cached" request
|
|
100
|
+
// that cannot be handled by the worker. Bypass such requests.
|
|
101
|
+
if (
|
|
102
|
+
event.request.cache === 'only-if-cached' &&
|
|
103
|
+
event.request.mode !== 'same-origin'
|
|
104
|
+
) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Bypass all requests when there are no active clients.
|
|
109
|
+
// Prevents the self-unregistered worked from handling requests
|
|
110
|
+
// after it's been terminated (still remains active until the next reload).
|
|
111
|
+
if (activeClientIds.size === 0) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const requestId = crypto.randomUUID()
|
|
116
|
+
event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {FetchEvent} event
|
|
121
|
+
* @param {string} requestId
|
|
122
|
+
* @param {number} requestInterceptedAt
|
|
123
|
+
*/
|
|
124
|
+
async function handleRequest(event, requestId, requestInterceptedAt) {
|
|
125
|
+
const client = await resolveMainClient(event)
|
|
126
|
+
const requestCloneForEvents = event.request.clone()
|
|
127
|
+
const response = await getResponse(
|
|
128
|
+
event,
|
|
129
|
+
client,
|
|
130
|
+
requestId,
|
|
131
|
+
requestInterceptedAt,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
// Send back the response clone for the "response:*" life-cycle events.
|
|
135
|
+
// Ensure MSW is active and ready to handle the message, otherwise
|
|
136
|
+
// this message will pend indefinitely.
|
|
137
|
+
if (client && activeClientIds.has(client.id)) {
|
|
138
|
+
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
|
139
|
+
|
|
140
|
+
// Clone the response so both the client and the library could consume it.
|
|
141
|
+
const responseClone = response.clone()
|
|
142
|
+
|
|
143
|
+
sendToClient(
|
|
144
|
+
client,
|
|
145
|
+
{
|
|
146
|
+
type: 'RESPONSE',
|
|
147
|
+
payload: {
|
|
148
|
+
isMockedResponse: IS_MOCKED_RESPONSE in response,
|
|
149
|
+
request: {
|
|
150
|
+
id: requestId,
|
|
151
|
+
...serializedRequest,
|
|
152
|
+
},
|
|
153
|
+
response: {
|
|
154
|
+
type: responseClone.type,
|
|
155
|
+
status: responseClone.status,
|
|
156
|
+
statusText: responseClone.statusText,
|
|
157
|
+
headers: Object.fromEntries(responseClone.headers.entries()),
|
|
158
|
+
body: responseClone.body,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return response
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Resolve the main client for the given event.
|
|
171
|
+
* Client that issues a request doesn't necessarily equal the client
|
|
172
|
+
* that registered the worker. It's with the latter the worker should
|
|
173
|
+
* communicate with during the response resolving phase.
|
|
174
|
+
* @param {FetchEvent} event
|
|
175
|
+
* @returns {Promise<Client | undefined>}
|
|
176
|
+
*/
|
|
177
|
+
async function resolveMainClient(event) {
|
|
178
|
+
const client = await self.clients.get(event.clientId)
|
|
179
|
+
|
|
180
|
+
if (activeClientIds.has(event.clientId)) {
|
|
181
|
+
return client
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (client?.frameType === 'top-level') {
|
|
185
|
+
return client
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const allClients = await self.clients.matchAll({
|
|
189
|
+
type: 'window',
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
return allClients
|
|
193
|
+
.filter((client) => {
|
|
194
|
+
// Get only those clients that are currently visible.
|
|
195
|
+
return client.visibilityState === 'visible'
|
|
196
|
+
})
|
|
197
|
+
.find((client) => {
|
|
198
|
+
// Find the client ID that's recorded in the
|
|
199
|
+
// set of clients that have registered the worker.
|
|
200
|
+
return activeClientIds.has(client.id)
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @param {FetchEvent} event
|
|
206
|
+
* @param {Client | undefined} client
|
|
207
|
+
* @param {string} requestId
|
|
208
|
+
* @param {number} requestInterceptedAt
|
|
209
|
+
* @returns {Promise<Response>}
|
|
210
|
+
*/
|
|
211
|
+
async function getResponse(event, client, requestId, requestInterceptedAt) {
|
|
212
|
+
// Clone the request because it might've been already used
|
|
213
|
+
// (i.e. its body has been read and sent to the client).
|
|
214
|
+
const requestClone = event.request.clone()
|
|
215
|
+
|
|
216
|
+
function passthrough() {
|
|
217
|
+
// Cast the request headers to a new Headers instance
|
|
218
|
+
// so the headers can be manipulated with.
|
|
219
|
+
const headers = new Headers(requestClone.headers)
|
|
220
|
+
|
|
221
|
+
// Remove the "accept" header value that marked this request as passthrough.
|
|
222
|
+
// This prevents request alteration and also keeps it compliant with the
|
|
223
|
+
// user-defined CORS policies.
|
|
224
|
+
const acceptHeader = headers.get('accept')
|
|
225
|
+
if (acceptHeader) {
|
|
226
|
+
const values = acceptHeader.split(',').map((value) => value.trim())
|
|
227
|
+
const filteredValues = values.filter(
|
|
228
|
+
(value) => value !== 'msw/passthrough',
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if (filteredValues.length > 0) {
|
|
232
|
+
headers.set('accept', filteredValues.join(', '))
|
|
233
|
+
} else {
|
|
234
|
+
headers.delete('accept')
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return fetch(requestClone, { headers })
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Bypass mocking when the client is not active.
|
|
242
|
+
if (!client) {
|
|
243
|
+
return passthrough()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Bypass initial page load requests (i.e. static assets).
|
|
247
|
+
// The absence of the immediate/parent client in the map of the active clients
|
|
248
|
+
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
|
|
249
|
+
// and is not ready to handle requests.
|
|
250
|
+
if (!activeClientIds.has(client.id)) {
|
|
251
|
+
return passthrough()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Notify the client that a request has been intercepted.
|
|
255
|
+
const serializedRequest = await serializeRequest(event.request)
|
|
256
|
+
const clientMessage = await sendToClient(
|
|
257
|
+
client,
|
|
258
|
+
{
|
|
259
|
+
type: 'REQUEST',
|
|
260
|
+
payload: {
|
|
261
|
+
id: requestId,
|
|
262
|
+
interceptedAt: requestInterceptedAt,
|
|
263
|
+
...serializedRequest,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
[serializedRequest.body],
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
switch (clientMessage.type) {
|
|
270
|
+
case 'MOCK_RESPONSE': {
|
|
271
|
+
return respondWithMock(clientMessage.data)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
case 'PASSTHROUGH': {
|
|
275
|
+
return passthrough()
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return passthrough()
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param {Client} client
|
|
284
|
+
* @param {any} message
|
|
285
|
+
* @param {Array<Transferable>} transferrables
|
|
286
|
+
* @returns {Promise<any>}
|
|
287
|
+
*/
|
|
288
|
+
function sendToClient(client, message, transferrables = []) {
|
|
289
|
+
return new Promise((resolve, reject) => {
|
|
290
|
+
const channel = new MessageChannel()
|
|
291
|
+
|
|
292
|
+
channel.port1.onmessage = (event) => {
|
|
293
|
+
if (event.data && event.data.error) {
|
|
294
|
+
return reject(event.data.error)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
resolve(event.data)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
client.postMessage(message, [
|
|
301
|
+
channel.port2,
|
|
302
|
+
...transferrables.filter(Boolean),
|
|
303
|
+
])
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @param {Response} response
|
|
309
|
+
* @returns {Response}
|
|
310
|
+
*/
|
|
311
|
+
function respondWithMock(response) {
|
|
312
|
+
// Setting response status code to 0 is a no-op.
|
|
313
|
+
// However, when responding with a "Response.error()", the produced Response
|
|
314
|
+
// instance will have status code set to 0. Since it's not possible to create
|
|
315
|
+
// a Response instance with status code 0, handle that use-case separately.
|
|
316
|
+
if (response.status === 0) {
|
|
317
|
+
return Response.error()
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const mockedResponse = new Response(response.body, response)
|
|
321
|
+
|
|
322
|
+
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
|
|
323
|
+
value: true,
|
|
324
|
+
enumerable: true,
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
return mockedResponse
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @param {Request} request
|
|
332
|
+
*/
|
|
333
|
+
async function serializeRequest(request) {
|
|
334
|
+
return {
|
|
335
|
+
url: request.url,
|
|
336
|
+
mode: request.mode,
|
|
337
|
+
method: request.method,
|
|
338
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
339
|
+
cache: request.cache,
|
|
340
|
+
credentials: request.credentials,
|
|
341
|
+
destination: request.destination,
|
|
342
|
+
integrity: request.integrity,
|
|
343
|
+
redirect: request.redirect,
|
|
344
|
+
referrer: request.referrer,
|
|
345
|
+
referrerPolicy: request.referrerPolicy,
|
|
346
|
+
body: await request.arrayBuffer(),
|
|
347
|
+
keepalive: request.keepalive,
|
|
348
|
+
}
|
|
349
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/* tslint:disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mock Service Worker.
|
|
6
|
+
* @see https://github.com/mswjs/msw
|
|
7
|
+
* - Please do NOT modify this file.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const PACKAGE_VERSION = '2.12.7'
|
|
11
|
+
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
|
12
|
+
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
|
13
|
+
const activeClientIds = new Set()
|
|
14
|
+
|
|
15
|
+
addEventListener('install', function () {
|
|
16
|
+
self.skipWaiting()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
addEventListener('activate', function (event) {
|
|
20
|
+
event.waitUntil(self.clients.claim())
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
addEventListener('message', async function (event) {
|
|
24
|
+
const clientId = Reflect.get(event.source || {}, 'id')
|
|
25
|
+
|
|
26
|
+
if (!clientId || !self.clients) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const client = await self.clients.get(clientId)
|
|
31
|
+
|
|
32
|
+
if (!client) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const allClients = await self.clients.matchAll({
|
|
37
|
+
type: 'window',
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
switch (event.data) {
|
|
41
|
+
case 'KEEPALIVE_REQUEST': {
|
|
42
|
+
sendToClient(client, {
|
|
43
|
+
type: 'KEEPALIVE_RESPONSE',
|
|
44
|
+
})
|
|
45
|
+
break
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
case 'INTEGRITY_CHECK_REQUEST': {
|
|
49
|
+
sendToClient(client, {
|
|
50
|
+
type: 'INTEGRITY_CHECK_RESPONSE',
|
|
51
|
+
payload: {
|
|
52
|
+
packageVersion: PACKAGE_VERSION,
|
|
53
|
+
checksum: INTEGRITY_CHECKSUM,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'MOCK_ACTIVATE': {
|
|
60
|
+
activeClientIds.add(clientId)
|
|
61
|
+
|
|
62
|
+
sendToClient(client, {
|
|
63
|
+
type: 'MOCKING_ENABLED',
|
|
64
|
+
payload: {
|
|
65
|
+
client: {
|
|
66
|
+
id: client.id,
|
|
67
|
+
frameType: client.frameType,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
case 'CLIENT_CLOSED': {
|
|
75
|
+
activeClientIds.delete(clientId)
|
|
76
|
+
|
|
77
|
+
const remainingClients = allClients.filter((client) => {
|
|
78
|
+
return client.id !== clientId
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Unregister itself when there are no more clients
|
|
82
|
+
if (remainingClients.length === 0) {
|
|
83
|
+
self.registration.unregister()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
break
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
addEventListener('fetch', function (event) {
|
|
92
|
+
const requestInterceptedAt = Date.now()
|
|
93
|
+
|
|
94
|
+
// Bypass navigation requests.
|
|
95
|
+
if (event.request.mode === 'navigate') {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Opening the DevTools triggers the "only-if-cached" request
|
|
100
|
+
// that cannot be handled by the worker. Bypass such requests.
|
|
101
|
+
if (
|
|
102
|
+
event.request.cache === 'only-if-cached' &&
|
|
103
|
+
event.request.mode !== 'same-origin'
|
|
104
|
+
) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Bypass all requests when there are no active clients.
|
|
109
|
+
// Prevents the self-unregistered worked from handling requests
|
|
110
|
+
// after it's been terminated (still remains active until the next reload).
|
|
111
|
+
if (activeClientIds.size === 0) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const requestId = crypto.randomUUID()
|
|
116
|
+
event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {FetchEvent} event
|
|
121
|
+
* @param {string} requestId
|
|
122
|
+
* @param {number} requestInterceptedAt
|
|
123
|
+
*/
|
|
124
|
+
async function handleRequest(event, requestId, requestInterceptedAt) {
|
|
125
|
+
const client = await resolveMainClient(event)
|
|
126
|
+
const requestCloneForEvents = event.request.clone()
|
|
127
|
+
const response = await getResponse(
|
|
128
|
+
event,
|
|
129
|
+
client,
|
|
130
|
+
requestId,
|
|
131
|
+
requestInterceptedAt,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
// Send back the response clone for the "response:*" life-cycle events.
|
|
135
|
+
// Ensure MSW is active and ready to handle the message, otherwise
|
|
136
|
+
// this message will pend indefinitely.
|
|
137
|
+
if (client && activeClientIds.has(client.id)) {
|
|
138
|
+
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
|
139
|
+
|
|
140
|
+
// Clone the response so both the client and the library could consume it.
|
|
141
|
+
const responseClone = response.clone()
|
|
142
|
+
|
|
143
|
+
sendToClient(
|
|
144
|
+
client,
|
|
145
|
+
{
|
|
146
|
+
type: 'RESPONSE',
|
|
147
|
+
payload: {
|
|
148
|
+
isMockedResponse: IS_MOCKED_RESPONSE in response,
|
|
149
|
+
request: {
|
|
150
|
+
id: requestId,
|
|
151
|
+
...serializedRequest,
|
|
152
|
+
},
|
|
153
|
+
response: {
|
|
154
|
+
type: responseClone.type,
|
|
155
|
+
status: responseClone.status,
|
|
156
|
+
statusText: responseClone.statusText,
|
|
157
|
+
headers: Object.fromEntries(responseClone.headers.entries()),
|
|
158
|
+
body: responseClone.body,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return response
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Resolve the main client for the given event.
|
|
171
|
+
* Client that issues a request doesn't necessarily equal the client
|
|
172
|
+
* that registered the worker. It's with the latter the worker should
|
|
173
|
+
* communicate with during the response resolving phase.
|
|
174
|
+
* @param {FetchEvent} event
|
|
175
|
+
* @returns {Promise<Client | undefined>}
|
|
176
|
+
*/
|
|
177
|
+
async function resolveMainClient(event) {
|
|
178
|
+
const client = await self.clients.get(event.clientId)
|
|
179
|
+
|
|
180
|
+
if (activeClientIds.has(event.clientId)) {
|
|
181
|
+
return client
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (client?.frameType === 'top-level') {
|
|
185
|
+
return client
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const allClients = await self.clients.matchAll({
|
|
189
|
+
type: 'window',
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
return allClients
|
|
193
|
+
.filter((client) => {
|
|
194
|
+
// Get only those clients that are currently visible.
|
|
195
|
+
return client.visibilityState === 'visible'
|
|
196
|
+
})
|
|
197
|
+
.find((client) => {
|
|
198
|
+
// Find the client ID that's recorded in the
|
|
199
|
+
// set of clients that have registered the worker.
|
|
200
|
+
return activeClientIds.has(client.id)
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @param {FetchEvent} event
|
|
206
|
+
* @param {Client | undefined} client
|
|
207
|
+
* @param {string} requestId
|
|
208
|
+
* @param {number} requestInterceptedAt
|
|
209
|
+
* @returns {Promise<Response>}
|
|
210
|
+
*/
|
|
211
|
+
async function getResponse(event, client, requestId, requestInterceptedAt) {
|
|
212
|
+
// Clone the request because it might've been already used
|
|
213
|
+
// (i.e. its body has been read and sent to the client).
|
|
214
|
+
const requestClone = event.request.clone()
|
|
215
|
+
|
|
216
|
+
function passthrough() {
|
|
217
|
+
// Cast the request headers to a new Headers instance
|
|
218
|
+
// so the headers can be manipulated with.
|
|
219
|
+
const headers = new Headers(requestClone.headers)
|
|
220
|
+
|
|
221
|
+
// Remove the "accept" header value that marked this request as passthrough.
|
|
222
|
+
// This prevents request alteration and also keeps it compliant with the
|
|
223
|
+
// user-defined CORS policies.
|
|
224
|
+
const acceptHeader = headers.get('accept')
|
|
225
|
+
if (acceptHeader) {
|
|
226
|
+
const values = acceptHeader.split(',').map((value) => value.trim())
|
|
227
|
+
const filteredValues = values.filter(
|
|
228
|
+
(value) => value !== 'msw/passthrough',
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if (filteredValues.length > 0) {
|
|
232
|
+
headers.set('accept', filteredValues.join(', '))
|
|
233
|
+
} else {
|
|
234
|
+
headers.delete('accept')
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return fetch(requestClone, { headers })
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Bypass mocking when the client is not active.
|
|
242
|
+
if (!client) {
|
|
243
|
+
return passthrough()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Bypass initial page load requests (i.e. static assets).
|
|
247
|
+
// The absence of the immediate/parent client in the map of the active clients
|
|
248
|
+
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
|
|
249
|
+
// and is not ready to handle requests.
|
|
250
|
+
if (!activeClientIds.has(client.id)) {
|
|
251
|
+
return passthrough()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Notify the client that a request has been intercepted.
|
|
255
|
+
const serializedRequest = await serializeRequest(event.request)
|
|
256
|
+
const clientMessage = await sendToClient(
|
|
257
|
+
client,
|
|
258
|
+
{
|
|
259
|
+
type: 'REQUEST',
|
|
260
|
+
payload: {
|
|
261
|
+
id: requestId,
|
|
262
|
+
interceptedAt: requestInterceptedAt,
|
|
263
|
+
...serializedRequest,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
[serializedRequest.body],
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
switch (clientMessage.type) {
|
|
270
|
+
case 'MOCK_RESPONSE': {
|
|
271
|
+
return respondWithMock(clientMessage.data)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
case 'PASSTHROUGH': {
|
|
275
|
+
return passthrough()
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return passthrough()
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param {Client} client
|
|
284
|
+
* @param {any} message
|
|
285
|
+
* @param {Array<Transferable>} transferrables
|
|
286
|
+
* @returns {Promise<any>}
|
|
287
|
+
*/
|
|
288
|
+
function sendToClient(client, message, transferrables = []) {
|
|
289
|
+
return new Promise((resolve, reject) => {
|
|
290
|
+
const channel = new MessageChannel()
|
|
291
|
+
|
|
292
|
+
channel.port1.onmessage = (event) => {
|
|
293
|
+
if (event.data && event.data.error) {
|
|
294
|
+
return reject(event.data.error)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
resolve(event.data)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
client.postMessage(message, [
|
|
301
|
+
channel.port2,
|
|
302
|
+
...transferrables.filter(Boolean),
|
|
303
|
+
])
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @param {Response} response
|
|
309
|
+
* @returns {Response}
|
|
310
|
+
*/
|
|
311
|
+
function respondWithMock(response) {
|
|
312
|
+
// Setting response status code to 0 is a no-op.
|
|
313
|
+
// However, when responding with a "Response.error()", the produced Response
|
|
314
|
+
// instance will have status code set to 0. Since it's not possible to create
|
|
315
|
+
// a Response instance with status code 0, handle that use-case separately.
|
|
316
|
+
if (response.status === 0) {
|
|
317
|
+
return Response.error()
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const mockedResponse = new Response(response.body, response)
|
|
321
|
+
|
|
322
|
+
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
|
|
323
|
+
value: true,
|
|
324
|
+
enumerable: true,
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
return mockedResponse
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @param {Request} request
|
|
332
|
+
*/
|
|
333
|
+
async function serializeRequest(request) {
|
|
334
|
+
return {
|
|
335
|
+
url: request.url,
|
|
336
|
+
mode: request.mode,
|
|
337
|
+
method: request.method,
|
|
338
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
339
|
+
cache: request.cache,
|
|
340
|
+
credentials: request.credentials,
|
|
341
|
+
destination: request.destination,
|
|
342
|
+
integrity: request.integrity,
|
|
343
|
+
redirect: request.redirect,
|
|
344
|
+
referrer: request.referrer,
|
|
345
|
+
referrerPolicy: request.referrerPolicy,
|
|
346
|
+
body: await request.arrayBuffer(),
|
|
347
|
+
keepalive: request.keepalive,
|
|
348
|
+
}
|
|
349
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
3
|
+
<url>
|
|
4
|
+
<loc>https://crownpeak.github.io/dqm-react-component/</loc>
|
|
5
|
+
<lastmod>2026-01-08</lastmod>
|
|
6
|
+
<changefreq>weekly</changefreq>
|
|
7
|
+
<priority>1.0</priority>
|
|
8
|
+
</url>
|
|
9
|
+
</urlset>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": [
|
|
5
|
+
"dom",
|
|
6
|
+
"dom.iterable",
|
|
7
|
+
"esnext"
|
|
8
|
+
],
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"module": "esnext",
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"jsx": "react-jsx",
|
|
19
|
+
"incremental": true,
|
|
20
|
+
"plugins": [
|
|
21
|
+
{
|
|
22
|
+
"name": "next"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"paths": {
|
|
26
|
+
"@/*": [
|
|
27
|
+
"./src/*"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"include": [
|
|
32
|
+
"next-env.d.ts",
|
|
33
|
+
"**/*.ts",
|
|
34
|
+
"**/*.tsx",
|
|
35
|
+
".next/types/**/*.ts",
|
|
36
|
+
".next/dev/types/**/*.ts"
|
|
37
|
+
],
|
|
38
|
+
"exclude": [
|
|
39
|
+
"node_modules"
|
|
40
|
+
]
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crownpeak/dqm-react-component-dev-mcp",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "MCP Server for Crownpeak DQM React Component documentation - powered by Probe",
|
|
5
5
|
"author": "Crownpeak Technology GmbH",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@probelabs/probe": "^0.6.0-rc128",
|
|
25
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
26
26
|
"axios": "^1.6.0",
|
|
27
27
|
"fs-extra": "^11.2.0",
|
|
28
28
|
"glob": "^10.3.0",
|