From cba6c66ccca33f884efc6e2f3534883501e848a0 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Thu, 22 Jan 2026 15:27:30 +0100 Subject: [PATCH 01/55] chore: add auto-claude entries to .gitignore --- .gitignore | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a484ab2..11afad6 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,15 @@ supabase/.temp/ .history/ # Source images (originals before optimization) -source-images/ \ No newline at end of file +source-images/ + +# Auto Claude data directory +.auto-claude/ + +# Auto Claude generated files +.auto-claude-security.json +.auto-claude-status +.claude_settings.json +.worktrees/ +.security-key +logs/security/ From 29ad019884efa89cbb505929e91cff606c7acfd4 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 02:36:05 +0100 Subject: [PATCH 02/55] auto-claude: subtask-1-1 - Create useLocalStorage hook with TypeScript --- src/hooks/useLocalStorage.ts | 94 ++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/hooks/useLocalStorage.ts diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts new file mode 100644 index 0000000..7a35919 --- /dev/null +++ b/src/hooks/useLocalStorage.ts @@ -0,0 +1,94 @@ +import { useState, useEffect, useCallback } from 'react'; +import { handleError } from '../utils/errorHandling'; + +type SetValue = (value: T | ((val: T) => T)) => void; + +/** + * Custom hook for managing state in localStorage with automatic serialization + * + * @template T - The type of the stored value + * @param {string} key - The localStorage key to use + * @param {T | (() => T)} initialValue - The initial value or a function that returns the initial value + * @returns {[T, SetValue, () => void]} A tuple containing: + * - The current stored value + * - A function to update the stored value + * - A function to remove the value from storage + * + * @example + * ```tsx + * const [theme, setTheme, removeTheme] = useLocalStorage('theme', 'light'); + * setTheme('dark'); // Updates both state and localStorage + * removeTheme(); // Removes from localStorage and resets to initial value + * ``` + * + * @remarks + * - SSR-safe: Returns initial value during server-side rendering + * - Automatically serializes/deserializes JSON + * - Handles localStorage quota exceeded errors + * - Handles invalid JSON gracefully + */ +export function useLocalStorage( + key: string, + initialValue: T | (() => T) +): [T, SetValue, () => void] { + // Get initial value - SSR safe + const getInitialValue = useCallback((): T => { + // Check if we're in a browser environment + if (typeof window === 'undefined') { + return initialValue instanceof Function ? initialValue() : initialValue; + } + + try { + const item = window.localStorage.getItem(key); + if (item) { + return JSON.parse(item) as T; + } + } catch (error) { + handleError(error, `LocalStorage Read (key: ${key})`); + } + + return initialValue instanceof Function ? initialValue() : initialValue; + }, [key, initialValue]); + + const [storedValue, setStoredValue] = useState(getInitialValue); + + // Update localStorage whenever storedValue changes + useEffect(() => { + if (typeof window === 'undefined') { + return; + } + + try { + window.localStorage.setItem(key, JSON.stringify(storedValue)); + } catch (error) { + handleError(error, `LocalStorage Write (key: ${key})`); + } + }, [key, storedValue]); + + // Set value function that supports both direct values and updater functions + const setValue: SetValue = useCallback((value) => { + try { + setStoredValue((prevValue) => { + const newValue = value instanceof Function ? value(prevValue) : value; + return newValue; + }); + } catch (error) { + handleError(error, `LocalStorage Update (key: ${key})`); + } + }, [key]); + + // Remove value from localStorage and reset to initial value + const removeValue = useCallback(() => { + try { + if (typeof window !== 'undefined') { + window.localStorage.removeItem(key); + } + const resetValue = initialValue instanceof Function ? initialValue() : initialValue; + setStoredValue(resetValue); + } catch (error) { + handleError(error, `LocalStorage Remove (key: ${key})`); + } + }, [key, initialValue]); + + return [storedValue, setValue, removeValue]; +} From 2f8c79612ddc299b6acbb3230ffe7b2d3e521108 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 02:38:22 +0100 Subject: [PATCH 03/55] auto-claude: subtask-1-2 - Add JSDoc documentation to hook --- src/hooks/useLocalStorage.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts index 7a35919..ee1029f 100644 --- a/src/hooks/useLocalStorage.ts +++ b/src/hooks/useLocalStorage.ts @@ -1,6 +1,11 @@ import { useState, useEffect, useCallback } from 'react'; import { handleError } from '../utils/errorHandling'; +/** + * Function type for updating the stored value + * @template T - The type of the stored value + * @param value - Either a new value of type T or an updater function that takes the current value and returns a new value + */ type SetValue = (value: T | ((val: T) => T)) => void; /** From 34555de0e5b4b7bacf23638418fc32cd060c7825 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 04:01:47 +0100 Subject: [PATCH 04/55] auto-claude: subtask-2-1 - Create basic unit tests for useLocalStorage --- src/hooks/useLocalStorage.test.ts | 235 ++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 src/hooks/useLocalStorage.test.ts diff --git a/src/hooks/useLocalStorage.test.ts b/src/hooks/useLocalStorage.test.ts new file mode 100644 index 0000000..08c6f9b --- /dev/null +++ b/src/hooks/useLocalStorage.test.ts @@ -0,0 +1,235 @@ +import { renderHook, act } from '@testing-library/react'; +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { useLocalStorage } from './useLocalStorage'; + +// Mock the error handling utility +vi.mock('../utils/errorHandling', () => ({ + handleError: vi.fn() +})); + +// Mock localStorage +const localStorageMock = (() => { + let store: Record = {}; + + return { + getItem: (key: string) => store[key] || null, + setItem: (key: string, value: string) => { + store[key] = value.toString(); + }, + removeItem: (key: string) => { + delete store[key]; + }, + clear: () => { + store = {}; + } + }; +})(); + +describe('useLocalStorage', () => { + beforeEach(() => { + // Setup localStorage mock + Object.defineProperty(window, 'localStorage', { + value: localStorageMock, + writable: true + }); + localStorageMock.clear(); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should return initial value when localStorage is empty', () => { + const { result } = renderHook(() => useLocalStorage('test-key', 'initial-value')); + const [value] = result.current; + + expect(value).toBe('initial-value'); + }); + + it('should return initial value from function when localStorage is empty', () => { + const initializer = vi.fn(() => 'computed-value'); + const { result } = renderHook(() => useLocalStorage('test-key', initializer)); + const [value] = result.current; + + expect(value).toBe('computed-value'); + expect(initializer).toHaveBeenCalled(); + }); + + it('should set value in localStorage', () => { + const { result } = renderHook(() => useLocalStorage('test-key', 'initial')); + + act(() => { + const [, setValue] = result.current; + setValue('new-value'); + }); + + const [value] = result.current; + expect(value).toBe('new-value'); + expect(JSON.parse(localStorage.getItem('test-key')!)).toBe('new-value'); + }); + + it('should handle updater function in setValue', () => { + const { result } = renderHook(() => useLocalStorage('test-key', 10)); + + act(() => { + const [, setValue] = result.current; + setValue((prev) => prev + 5); + }); + + const [value] = result.current; + expect(value).toBe(15); + }); + + it('should read existing value from localStorage', () => { + localStorage.setItem('test-key', JSON.stringify('existing-value')); + + const { result } = renderHook(() => useLocalStorage('test-key', 'initial-value')); + const [value] = result.current; + + expect(value).toBe('existing-value'); + }); + + it('should handle complex objects', () => { + const complexObject = { name: 'John', age: 30, hobbies: ['reading', 'coding'] }; + const { result } = renderHook(() => useLocalStorage('test-key', complexObject)); + + act(() => { + const [, setValue] = result.current; + setValue({ ...complexObject, age: 31 }); + }); + + const [value] = result.current; + expect(value).toEqual({ name: 'John', age: 31, hobbies: ['reading', 'coding'] }); + }); + + it('should remove value from localStorage', () => { + localStorage.setItem('test-key', JSON.stringify('existing-value')); + + const { result } = renderHook(() => useLocalStorage('test-key', 'initial-value')); + + act(() => { + const [, , removeValue] = result.current; + removeValue(); + }); + + const [value] = result.current; + expect(value).toBe('initial-value'); + expect(localStorage.getItem('test-key')).toBeNull(); + }); + + it('should handle invalid JSON in localStorage', () => { + localStorage.setItem('test-key', 'invalid-json{'); + + const { result } = renderHook(() => useLocalStorage('test-key', 'fallback-value')); + const [value] = result.current; + + expect(value).toBe('fallback-value'); + }); + + it('should be SSR-safe (no window)', () => { + const originalWindow = global.window; + // @ts-ignore - Temporarily remove window for SSR test + delete global.window; + + const { result } = renderHook(() => useLocalStorage('test-key', 'ssr-value')); + const [value] = result.current; + + expect(value).toBe('ssr-value'); + + // Restore window + global.window = originalWindow; + }); + + it('should handle localStorage quota exceeded', () => { + const { handleError } = require('../utils/errorHandling'); + + // Mock setItem to throw quota exceeded error + const originalSetItem = localStorage.setItem; + localStorage.setItem = vi.fn(() => { + throw new DOMException('QuotaExceededError'); + }); + + const { result } = renderHook(() => useLocalStorage('test-key', 'initial')); + + act(() => { + const [, setValue] = result.current; + setValue('new-value'); + }); + + expect(handleError).toHaveBeenCalled(); + + // Restore original setItem + localStorage.setItem = originalSetItem; + }); + + it('should serialize and deserialize arrays', () => { + const initialArray = [1, 2, 3, 4, 5]; + const { result } = renderHook(() => useLocalStorage('test-key', initialArray)); + + act(() => { + const [, setValue] = result.current; + setValue([...initialArray, 6]); + }); + + const [value] = result.current; + expect(value).toEqual([1, 2, 3, 4, 5, 6]); + expect(JSON.parse(localStorage.getItem('test-key')!)).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it('should handle boolean values', () => { + const { result } = renderHook(() => useLocalStorage('test-key', false)); + + act(() => { + const [, setValue] = result.current; + setValue(true); + }); + + const [value] = result.current; + expect(value).toBe(true); + expect(JSON.parse(localStorage.getItem('test-key')!)).toBe(true); + }); + + it('should handle number values', () => { + const { result } = renderHook(() => useLocalStorage('test-key', 0)); + + act(() => { + const [, setValue] = result.current; + setValue(42); + }); + + const [value] = result.current; + expect(value).toBe(42); + expect(JSON.parse(localStorage.getItem('test-key')!)).toBe(42); + }); + + it('should handle null values', () => { + const { result } = renderHook(() => useLocalStorage('test-key', null)); + + const [value] = result.current; + expect(value).toBe(null); + }); + + it('should update localStorage when key changes', () => { + const { result, rerender } = renderHook( + ({ key, value }) => useLocalStorage(key, value), + { initialProps: { key: 'key1', value: 'value1' } } + ); + + act(() => { + const [, setValue] = result.current; + setValue('updated-value1'); + }); + + expect(localStorage.getItem('key1')).toBe(JSON.stringify('updated-value1')); + + // Change the key + rerender({ key: 'key2', value: 'value2' }); + + act(() => { + const [, setValue] = result.current; + setValue('updated-value2'); + }); + + expect(localStorage.getItem('key2')).toBe(JSON.stringify('updated-value2')); + }); +}); From ffa56a7b5009725e0a60c30a6ccd5f157f8ce6fd Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 04:07:29 +0100 Subject: [PATCH 05/55] auto-claude: subtask-2-2 - Manual browser verification Created comprehensive test page at src/app/test-localstorage/page.tsx to verify useLocalStorage hook functionality in the browser. Test Coverage: - String values (basic get/set) - Complex objects (nested properties) - Arrays (add/remove items) - Numbers (increment/decrement) - Remove functionality - State persistence across page refreshes - Real-time localStorage updates visible in DevTools Verification Guide: A detailed manual verification guide has been created at .auto-claude/specs/004-add-uselocalstorage-custom-hook/MANUAL_VERIFICATION.md with step-by-step instructions for browser testing. Manual Verification Checklist: - [ ] No hydration errors in console - [ ] localStorage updates visible in DevTools - [ ] State persists across page refresh - [ ] No TypeScript errors (verified with npx tsc --noEmit) TypeScript compilation verified with no errors. Co-Authored-By: Claude Sonnet 4.5 --- src/app/test-localstorage/page.tsx | 186 +++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/app/test-localstorage/page.tsx diff --git a/src/app/test-localstorage/page.tsx b/src/app/test-localstorage/page.tsx new file mode 100644 index 0000000..4e6470c --- /dev/null +++ b/src/app/test-localstorage/page.tsx @@ -0,0 +1,186 @@ +'use client'; + +import { useLocalStorage } from '@/hooks/useLocalStorage'; +import { useState } from 'react'; + +interface UserSettings { + theme: 'light' | 'dark'; + notifications: boolean; + language: string; +} + +export default function TestLocalStoragePage() { + // Test 1: Simple string value + const [name, setName, removeName] = useLocalStorage('test-name', 'Guest'); + + // Test 2: Complex object + const [settings, setSettings, removeSettings] = useLocalStorage( + 'test-settings', + { + theme: 'light', + notifications: true, + language: 'en', + } + ); + + // Test 3: Array + const [items, setItems, removeItems] = useLocalStorage('test-items', []); + + // Test 4: Number + const [count, setCount, removeCount] = useLocalStorage('test-count', 0); + + const [newItem, setNewItem] = useState(''); + + return ( +
+

useLocalStorage Hook Test Page

+

+ Open DevTools (F12) → Application → Local Storage to see values update in real-time. + Refresh the page to verify persistence. +

+ + {/* Test 1: String */} +
+

Test 1: String Value

+

Current Name: {name}

+ setName(e.target.value)} + placeholder="Enter your name" + style={{ padding: '0.5rem', marginRight: '0.5rem' }} + /> + +

+ localStorage key: test-name +

+
+ + {/* Test 2: Complex Object */} +
+

Test 2: Complex Object

+
+

Theme: {settings.theme}

+ +
+
+ +
+
+ + +
+ +

+ localStorage key: test-settings +

+
+ + {/* Test 3: Array */} +
+

Test 3: Array Value

+

Items ({items.length}):

+
    + {items.map((item, index) => ( +
  • + {item}{' '} + +
  • + ))} +
+
+ setNewItem(e.target.value)} + onKeyPress={(e) => { + if (e.key === 'Enter' && newItem.trim()) { + setItems((prev) => [...prev, newItem.trim()]); + setNewItem(''); + } + }} + placeholder="Add new item" + style={{ padding: '0.5rem', marginRight: '0.5rem' }} + /> + + +
+

+ localStorage key: test-items +

+
+ + {/* Test 4: Number */} +
+

Test 4: Number Value

+

Count: {count}

+ + + +

+ localStorage key: test-count +

+
+ + {/* Instructions */} +
+

Verification Checklist:

+
    +
  • ✓ Open DevTools → Application → Local Storage → http://localhost:3000
  • +
  • ✓ Interact with the controls above and watch localStorage update in real-time
  • +
  • ✓ Refresh the page (F5) - all values should persist
  • +
  • ✓ Check Console for hydration errors (there should be none)
  • +
  • ✓ Check Console for any errors (there should be none)
  • +
  • ✓ Verify TypeScript has no errors in your editor
  • +
+
+
+ ); +} From f0490db54435a6b5623700c08ad4c5a2ab439bbf Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:28:59 +0100 Subject: [PATCH 06/55] auto-claude: subtask-1-1 - Install testing dependencies (@testing-library/react, @testing-library/jest-dom, jsdom) Co-Authored-By: Claude Sonnet 4.5 --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 6f3ec94..80f7713 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ }, "devDependencies": { "@tailwindcss/forms": "^0.5.7", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^14.0.0", "@types/mdx": "^2.0.13", "@types/node": "^22.0.0", "@types/react": "^19.0.0", @@ -45,6 +47,7 @@ "autoprefixer": "^10.4.18", "eslint": "^9.9.1", "eslint-config-next": "^15.1.0", + "jsdom": "^23.2.0", "postcss": "^8.4.35", "sharp": "^0.34.3", "tailwindcss": "^3.4.1", From 3047c8f7f28aae141917da033fbd9a5b68687389 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:29:02 +0100 Subject: [PATCH 07/55] auto-claude: subtask-1-1 - Create schemas directory and extract PersonJsonLd --- .auto-claude-security.json | 227 ++++++++++++++++++++ .auto-claude-status | 25 +++ .claude_settings.json | 39 ++++ .gitignore | 5 +- context.json | 74 +++++++ project_index.json | 27 +++ src/components/seo/schemas/PersonJsonLd.tsx | 80 +++++++ 7 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 .auto-claude-security.json create mode 100644 .auto-claude-status create mode 100644 .claude_settings.json create mode 100644 context.json create mode 100644 project_index.json create mode 100644 src/components/seo/schemas/PersonJsonLd.tsx diff --git a/.auto-claude-security.json b/.auto-claude-security.json new file mode 100644 index 0000000..eed60a2 --- /dev/null +++ b/.auto-claude-security.json @@ -0,0 +1,227 @@ +{ + "base_commands": [ + ".", + "[", + "[[", + "ag", + "awk", + "basename", + "bash", + "bc", + "break", + "cat", + "cd", + "chmod", + "clear", + "cmp", + "column", + "comm", + "command", + "continue", + "cp", + "curl", + "cut", + "date", + "df", + "diff", + "dig", + "dirname", + "du", + "echo", + "egrep", + "env", + "eval", + "exec", + "exit", + "expand", + "export", + "expr", + "false", + "fd", + "fgrep", + "file", + "find", + "fmt", + "fold", + "gawk", + "gh", + "git", + "grep", + "gunzip", + "gzip", + "head", + "help", + "host", + "iconv", + "id", + "jobs", + "join", + "jq", + "kill", + "killall", + "less", + "let", + "ln", + "ls", + "lsof", + "man", + "mkdir", + "mktemp", + "more", + "mv", + "nl", + "paste", + "pgrep", + "ping", + "pkill", + "popd", + "printenv", + "printf", + "ps", + "pushd", + "pwd", + "read", + "readlink", + "realpath", + "reset", + "return", + "rev", + "rg", + "rm", + "rmdir", + "sed", + "seq", + "set", + "sh", + "shuf", + "sleep", + "sort", + "source", + "split", + "stat", + "tail", + "tar", + "tee", + "test", + "time", + "timeout", + "touch", + "tr", + "tree", + "true", + "type", + "uname", + "unexpand", + "uniq", + "unset", + "unzip", + "watch", + "wc", + "wget", + "whereis", + "which", + "whoami", + "xargs", + "yes", + "yq", + "zip", + "zsh" + ], + "stack_commands": [ + "ar", + "clang", + "clang++", + "cmake", + "composer", + "dive", + "docker", + "docker-buildx", + "docker-compose", + "dockerfile", + "eslint", + "g++", + "gcc", + "ipython", + "jupyter", + "ld", + "make", + "meson", + "next", + "ninja", + "nm", + "node", + "notebook", + "npm", + "npx", + "objdump", + "pdb", + "php", + "pip", + "pip3", + "pipx", + "pnpm", + "pnpx", + "pudb", + "python", + "python3", + "react-scripts", + "strip", + "ts-node", + "tsc", + "tsx", + "vitest" + ], + "script_commands": [ + "bun", + "npm", + "pnpm", + "yarn" + ], + "custom_commands": [], + "detected_stack": { + "languages": [ + "python", + "javascript", + "typescript", + "php", + "c" + ], + "package_managers": [ + "pnpm" + ], + "frameworks": [ + "nextjs", + "react", + "vitest", + "eslint" + ], + "databases": [], + "infrastructure": [ + "docker" + ], + "cloud_providers": [], + "code_quality_tools": [], + "version_managers": [] + }, + "custom_scripts": { + "npm_scripts": [ + "dev", + "build", + "start", + "lint", + "build:images", + "generate:images", + "generate:images:dry", + "test", + "test:coverage" + ], + "make_targets": [], + "poetry_scripts": [], + "cargo_aliases": [], + "shell_scripts": [] + }, + "project_dir": "C:\\Users\\damja\\WebstormProjects\\Portfolio", + "created_at": "2026-01-22T15:28:38.237190", + "project_hash": "c4ad399e16be367eb4e6b076fe1d9ee3", + "inherited_from": "C:\\Users\\damja\\WebstormProjects\\Portfolio" +} \ No newline at end of file diff --git a/.auto-claude-status b/.auto-claude-status new file mode 100644 index 0000000..d3c20c7 --- /dev/null +++ b/.auto-claude-status @@ -0,0 +1,25 @@ +{ + "active": true, + "spec": "032-split-large-jsonld-tsx-component-759-lines-into-se", + "state": "planning", + "subtasks": { + "completed": 0, + "total": 0, + "in_progress": 1, + "failed": 0 + }, + "phase": { + "current": "Add New Schema Files", + "id": null, + "total": 14 + }, + "workers": { + "active": 0, + "max": 1 + }, + "session": { + "number": 2, + "started_at": "2026-01-25T06:23:19.101802" + }, + "last_update": "2026-01-25T06:28:11.697923" +} \ No newline at end of file diff --git a/.claude_settings.json b/.claude_settings.json new file mode 100644 index 0000000..a6f4d43 --- /dev/null +++ b/.claude_settings.json @@ -0,0 +1,39 @@ +{ + "sandbox": { + "enabled": true, + "autoAllowBashIfSandboxed": true + }, + "permissions": { + "defaultMode": "acceptEdits", + "allow": [ + "Read(./**)", + "Write(./**)", + "Edit(./**)", + "Glob(./**)", + "Grep(./**)", + "Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\032-split-large-jsonld-tsx-component-759-lines-into-se/**)", + "Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\032-split-large-jsonld-tsx-component-759-lines-into-se/**)", + "Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\032-split-large-jsonld-tsx-component-759-lines-into-se/**)", + "Glob(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\032-split-large-jsonld-tsx-component-759-lines-into-se/**)", + "Grep(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\032-split-large-jsonld-tsx-component-759-lines-into-se/**)", + "Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\032-split-large-jsonld-tsx-component-759-lines-into-se\\.auto-claude\\specs\\032-split-large-jsonld-tsx-component-759-lines-into-se/**)", + "Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\032-split-large-jsonld-tsx-component-759-lines-into-se\\.auto-claude\\specs\\032-split-large-jsonld-tsx-component-759-lines-into-se/**)", + "Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\032-split-large-jsonld-tsx-component-759-lines-into-se\\.auto-claude\\specs\\032-split-large-jsonld-tsx-component-759-lines-into-se/**)", + "Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Glob(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Grep(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Bash(*)", + "WebFetch(*)", + "WebSearch(*)", + "mcp__context7__resolve-library-id(*)", + "mcp__context7__get-library-docs(*)", + "mcp__graphiti-memory__search_nodes(*)", + "mcp__graphiti-memory__search_facts(*)", + "mcp__graphiti-memory__add_episode(*)", + "mcp__graphiti-memory__get_episodes(*)", + "mcp__graphiti-memory__get_entity_edge(*)" + ] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a484ab2..fa65296 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,7 @@ supabase/.temp/ .history/ # Source images (originals before optimization) -source-images/ \ No newline at end of file +source-images/ + +# Auto Claude data directory +.auto-claude/ diff --git a/context.json b/context.json new file mode 100644 index 0000000..a4607ec --- /dev/null +++ b/context.json @@ -0,0 +1,74 @@ +{ + "files_to_modify": { + "frontend": [ + "src/components/seo/JsonLd.tsx", + "src/components/seo/index.ts" + ] + }, + "files_to_create": { + "frontend": [ + "src/components/seo/schemas/PersonJsonLd.tsx", + "src/components/seo/schemas/ProfessionalServiceJsonLd.tsx", + "src/components/seo/schemas/BreadcrumbJsonLd.tsx", + "src/components/seo/schemas/WebPageJsonLd.tsx", + "src/components/seo/schemas/FAQJsonLd.tsx", + "src/components/seo/schemas/ServiceJsonLd.tsx", + "src/components/seo/schemas/OrganizationJsonLd.tsx", + "src/components/seo/schemas/ArticleJsonLd.tsx", + "src/components/seo/schemas/HowToJsonLd.tsx", + "src/components/seo/schemas/ProfilePageJsonLd.tsx", + "src/components/seo/schemas/VideoJsonLd.tsx", + "src/components/seo/schemas/SoftwareAppJsonLd.tsx", + "src/components/seo/schemas/WebSiteJsonLd.tsx", + "src/components/seo/schemas/index.ts" + ] + }, + "files_to_reference": [ + "src/components/about/index.ts", + "src/components/layout/index.ts", + "src/components/portfolio/index.ts" + ], + "patterns": { + "component_organization": "Each component type gets its own directory with multiple component files and a barrel export index.ts", + "barrel_export": "index.ts re-exports all components from the directory using named exports", + "import_paths": "Imports use path alias @/components/... for cleaner imports", + "typescript": "All components use TypeScript with proper interface definitions" + }, + "existing_implementations": { + "description": "Found 14 JSON-LD schema components in a single 758-line file at src/components/seo/JsonLd.tsx", + "relevant_files": [ + "src/components/seo/JsonLd.tsx", + "src/components/seo/index.ts", + "src/app/[locale]/layout.tsx (imports PersonJsonLd, ProfessionalServiceJsonLd, OrganizationJsonLd, WebSiteJsonLd)", + "src/app/[locale]/about/page.tsx (imports ProfilePageJsonLd, BreadcrumbJsonLd)", + "src/app/[locale]/blog/[slug]/page.tsx (imports BreadcrumbJsonLd, ArticleJsonLd)", + "src/app/[locale]/leistungen/page.tsx (imports BreadcrumbJsonLd, FAQJsonLd)" + ], + "components_to_split": [ + "PersonJsonLd", + "ProfessionalServiceJsonLd", + "BreadcrumbJsonLd", + "WebPageJsonLd", + "FAQJsonLd", + "ServiceJsonLd", + "OrganizationJsonLd", + "ArticleJsonLd", + "HowToJsonLd", + "ProfilePageJsonLd", + "VideoJsonLd", + "SoftwareAppJsonLd", + "WebSiteJsonLd" + ], + "usage_locations": [ + "src/app/[locale]/layout.tsx", + "src/app/[locale]/about/page.tsx", + "src/app/[locale]/blog/page.tsx", + "src/app/[locale]/blog/[slug]/page.tsx", + "src/app/[locale]/contact/page.tsx", + "src/app/[locale]/leistungen/page.tsx", + "src/app/[locale]/leistungen/[slug]/page.tsx", + "src/app/[locale]/leistungen/standort/[city]/page.tsx", + "src/app/[locale]/portfolio/[slug]/page.tsx" + ] + } +} diff --git a/project_index.json b/project_index.json new file mode 100644 index 0000000..fe6dec7 --- /dev/null +++ b/project_index.json @@ -0,0 +1,27 @@ +{ + "project_type": "single", + "services": { + "frontend": { + "path": ".", + "tech_stack": ["typescript", "react", "nextjs", "tailwindcss"], + "framework": "Next.js 15 (App Router)", + "port": 3000, + "dev_command": "npm run dev", + "build_command": "npm run build", + "test_command": "npm test" + } + }, + "infrastructure": { + "docker": false, + "database": null, + "ci_cd": false + }, + "conventions": { + "linter": "eslint", + "formatter": "prettier", + "testing": "vitest", + "component_pattern": "Each component directory contains multiple .tsx files with a barrel export index.ts", + "import_pattern": "Path aliases using @/ for src directory", + "export_pattern": "index.ts re-exports components using export { ComponentName } or export { default as ComponentName }" + } +} diff --git a/src/components/seo/schemas/PersonJsonLd.tsx b/src/components/seo/schemas/PersonJsonLd.tsx new file mode 100644 index 0000000..9cd9aba --- /dev/null +++ b/src/components/seo/schemas/PersonJsonLd.tsx @@ -0,0 +1,80 @@ +import { type Locale } from '@/i18n/config'; + +interface JsonLdProps { + locale: Locale; +} + +export function PersonJsonLd({ locale }: JsonLdProps) { + const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com'; + + const schema = { + '@context': 'https://schema.org', + '@type': 'Person', + '@id': `${baseUrl}/#person`, + name: 'Damjan Savić', + givenName: 'Damjan', + familyName: 'Savić', + jobTitle: locale === 'de' + ? 'AI & Automation Specialist | Fullstack Entwickler' + : locale === 'sr' + ? 'AI & Automation Specialist | Fullstack Developer' + : 'AI & Automation Specialist | Fullstack Developer', + description: locale === 'de' + ? 'Entwicklung von KI-Agenten und Automatisierungslösungen. Von Voice-AI-Plattformen bis zu autonomen Web-Agenten.' + : locale === 'sr' + ? 'Razvoj AI agenata i rešenja za automatizaciju. Od voice AI platformi do autonomnih web agenata.' + : 'Remote AI developer building AI agents and automation solutions for clients in USA, UK and Europe. From voice AI platforms to autonomous web agents.', + url: baseUrl, + image: `${baseUrl}/images/og-image.jpg`, + email: 'info@damjan-savic.com', + telephone: '+491756950979', + address: { + '@type': 'PostalAddress', + addressLocality: locale === 'sr' ? 'Keln' : locale === 'en' ? 'Cologne' : 'Köln', + addressRegion: 'NRW', + addressCountry: 'DE', + }, + sameAs: [ + 'https://www.linkedin.com/in/damjansavic/', + 'https://github.com/damjansavic', + ], + knowsAbout: [ + 'Artificial Intelligence', + 'Machine Learning', + 'Process Automation', + 'Voice AI', + 'Web Development', + 'Python', + 'TypeScript', + 'React', + 'Next.js', + 'n8n', + 'SaaS Development', + ], + alumniOf: [ + { + '@type': 'EducationalOrganization', + name: 'FOM Hochschule für Ökonomie & Management', + }, + ], + hasCredential: [ + { + '@type': 'EducationalOccupationalCredential', + name: 'M.A. Software Development', + credentialCategory: 'degree', + }, + { + '@type': 'EducationalOccupationalCredential', + name: 'B.Sc. Wirtschaftsinformatik', + credentialCategory: 'degree', + }, + ], + }; + + return ( + + + +``` + +```typescript +// popup.ts +document.addEventListener('DOMContentLoaded', async () => { + // Stats laden + await updateStats(); + + // Event Listeners + document.getElementById('extract-btn')?.addEventListener('click', extractCurrent); + document.getElementById('batch-extract-btn')?.addEventListener('click', batchExtract); +}); + +async function updateStats() { + const { extractedCount = 0, syncedCount = 0 } = await chrome.storage.local.get([ + 'extractedCount', + 'syncedCount' + ]); + + document.getElementById('extracted-count')!.textContent = extractedCount.toString(); + document.getElementById('synced-count')!.textContent = syncedCount.toString(); +} + +async function extractCurrent() { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + + if (!tab.id) return; + + // Message an Content Script + const response = await chrome.tabs.sendMessage(tab.id, { type: 'EXTRACT' }); + + if (response.success) { + setStatus('success', 'Extrahiert!'); + await updateStats(); + } else { + setStatus('error', 'Fehler'); + } +} + +async function batchExtract() { + const tabs = await chrome.tabs.query({ url: 'https://*.example.com/*' }); + + setStatus('loading', `Extrahiere ${tabs.length} Tabs...`); + + for (const tab of tabs) { + if (tab.id) { + await chrome.tabs.sendMessage(tab.id, { type: 'EXTRACT' }); + } + } + + setStatus('success', `${tabs.length} Seiten extrahiert`); + await updateStats(); +} + +function setStatus(type: 'success' | 'error' | 'loading', text: string) { + const indicator = document.getElementById('status-indicator')!; + const statusText = document.getElementById('status-text')!; + + indicator.className = `indicator ${type}`; + statusText.textContent = text; +} +``` + +--- + +## Storage API + +```typescript +// Sync Storage (zwischen Geräten synchronisiert, 100KB Limit) +await chrome.storage.sync.set({ + settings: { + autoExtract: true, + theme: 'dark' + } +}); + +const { settings } = await chrome.storage.sync.get('settings'); + +// Local Storage (nur lokal, 5MB Limit) +await chrome.storage.local.set({ + extractedData: largeDataArray +}); + +// Storage Change Listener +chrome.storage.onChanged.addListener((changes, areaName) => { + for (const [key, { oldValue, newValue }] of Object.entries(changes)) { + console.log(`${areaName}.${key} changed from`, oldValue, 'to', newValue); + } +}); +``` + +--- + +## Cross-Browser Kompatibilität + +```typescript +// Browser Detection +const isChrome = typeof chrome !== 'undefined' && chrome.runtime; +const isFirefox = typeof browser !== 'undefined'; + +// Unified API +const browserAPI = isFirefox ? browser : chrome; + +// Polyfill für Firefox Promise-based API +function promisify(chromeMethod: (...args: any[]) => void): (...args: any[]) => Promise { + return (...args) => new Promise((resolve, reject) => { + chromeMethod(...args, (result: T) => { + if (chrome.runtime.lastError) { + reject(chrome.runtime.lastError); + } else { + resolve(result); + } + }); + }); +} + +// webextension-polyfill verwenden +import browser from 'webextension-polyfill'; + +// Unified API für alle Browser +const tabs = await browser.tabs.query({ active: true }); +``` + +--- + +## Fazit + +Browser Extensions bieten: + +1. **Tiefe Integration**: Direkter DOM-Zugriff +2. **Automation**: Wiederkehrende Tasks automatisieren +3. **Produktivität**: Workflows optimieren +4. **Cross-Platform**: Chrome, Firefox, Edge + +Mächtige Tools für Power-User und Entwickler. + +--- + +## Bildprompts + +1. "Browser with extension icon showing automation, productivity concept" +2. "Extension popup interacting with webpage, data extraction" +3. "Multiple browser windows synchronized, cross-browser extension" + +--- + +## Quellen + +- [Chrome Extension Documentation](https://developer.chrome.com/docs/extensions/) +- [Manifest V3 Migration](https://developer.chrome.com/docs/extensions/mv3/intro/) +- [Firefox WebExtensions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions) +- [webextension-polyfill](https://github.com/nicknisi/webextension-polyfill) diff --git a/blog-posts/58-api-integration-patterns.md b/blog-posts/58-api-integration-patterns.md new file mode 100644 index 0000000..66cdb82 --- /dev/null +++ b/blog-posts/58-api-integration-patterns.md @@ -0,0 +1,608 @@ +# API Integration Patterns für Automation + +**Meta-Description:** Robuste API Integrationen. Retry Logic, Rate Limiting, Webhooks und Error Handling für zuverlässige Automatisierung. + +**Keywords:** API Integration, Retry Logic, Rate Limiting, Webhooks, Error Handling, REST API, HTTP Client + +--- + +## Einführung + +Zuverlässige API-Integrationen sind das Fundament jeder Automation. **Retry Logic, Rate Limiting, Circuit Breaker** – diese Patterns machen den Unterschied zwischen fragilen und robusten Systemen. + +--- + +## API Client Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ROBUST API CLIENT │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Request Flow: │ +│ │ +│ Application │ +│ ↓ │ +│ Rate Limiter (Throttle Requests) │ +│ ↓ │ +│ Retry Handler (Exponential Backoff) │ +│ ↓ │ +│ Circuit Breaker (Fail Fast) │ +│ ↓ │ +│ HTTP Client (Axios/Fetch) │ +│ ↓ │ +│ Response Handler (Parse/Validate) │ +│ ↓ │ +│ Application │ +│ │ +│ Cross-Cutting Concerns: │ +│ ├── Logging │ +│ ├── Metrics │ +│ └── Caching │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Base API Client + +```typescript +import axios, { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios'; + +interface ApiClientConfig { + baseURL: string; + timeout?: number; + headers?: Record; + retries?: number; + retryDelay?: number; + rateLimit?: { + maxRequests: number; + windowMs: number; + }; +} + +class ApiClient { + private client: AxiosInstance; + private config: Required; + private requestTimestamps: number[] = []; + + constructor(config: ApiClientConfig) { + this.config = { + timeout: 30000, + headers: {}, + retries: 3, + retryDelay: 1000, + rateLimit: { maxRequests: 100, windowMs: 60000 }, + ...config + }; + + this.client = axios.create({ + baseURL: this.config.baseURL, + timeout: this.config.timeout, + headers: this.config.headers + }); + + this.setupInterceptors(); + } + + private setupInterceptors() { + // Request Interceptor + this.client.interceptors.request.use( + async (config) => { + await this.waitForRateLimit(); + this.recordRequest(); + + console.log(`[API] ${config.method?.toUpperCase()} ${config.url}`); + return config; + }, + (error) => Promise.reject(error) + ); + + // Response Interceptor + this.client.interceptors.response.use( + (response) => { + console.log(`[API] Response: ${response.status}`); + return response; + }, + async (error: AxiosError) => { + const config = error.config as AxiosRequestConfig & { _retryCount?: number }; + + if (!config || !this.shouldRetry(error, config._retryCount || 0)) { + return Promise.reject(this.formatError(error)); + } + + config._retryCount = (config._retryCount || 0) + 1; + + const delay = this.calculateDelay(config._retryCount); + console.log(`[API] Retry ${config._retryCount}/${this.config.retries} after ${delay}ms`); + + await this.sleep(delay); + return this.client.request(config); + } + ); + } + + private shouldRetry(error: AxiosError, retryCount: number): boolean { + if (retryCount >= this.config.retries) return false; + + // Retry bei Netzwerkfehlern + if (!error.response) return true; + + // Retry bei bestimmten Status Codes + const retryableStatuses = [408, 429, 500, 502, 503, 504]; + return retryableStatuses.includes(error.response.status); + } + + private calculateDelay(retryCount: number): number { + // Exponential Backoff mit Jitter + const baseDelay = this.config.retryDelay; + const exponentialDelay = baseDelay * Math.pow(2, retryCount - 1); + const jitter = Math.random() * 1000; + return exponentialDelay + jitter; + } + + private async waitForRateLimit(): Promise { + const now = Date.now(); + const windowStart = now - this.config.rateLimit.windowMs; + + // Alte Timestamps entfernen + this.requestTimestamps = this.requestTimestamps.filter(t => t > windowStart); + + if (this.requestTimestamps.length >= this.config.rateLimit.maxRequests) { + const oldestRequest = this.requestTimestamps[0]; + const waitTime = oldestRequest + this.config.rateLimit.windowMs - now; + + if (waitTime > 0) { + console.log(`[API] Rate limit reached, waiting ${waitTime}ms`); + await this.sleep(waitTime); + } + } + } + + private recordRequest() { + this.requestTimestamps.push(Date.now()); + } + + private formatError(error: AxiosError): Error { + if (error.response) { + return new Error( + `API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}` + ); + } + if (error.request) { + return new Error(`Network Error: ${error.message}`); + } + return error; + } + + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + // Public Methods + async get(url: string, config?: AxiosRequestConfig): Promise { + const response = await this.client.get(url, config); + return response.data; + } + + async post(url: string, data?: any, config?: AxiosRequestConfig): Promise { + const response = await this.client.post(url, data, config); + return response.data; + } + + async put(url: string, data?: any, config?: AxiosRequestConfig): Promise { + const response = await this.client.put(url, data, config); + return response.data; + } + + async delete(url: string, config?: AxiosRequestConfig): Promise { + const response = await this.client.delete(url, config); + return response.data; + } +} +``` + +--- + +## Circuit Breaker Pattern + +```typescript +enum CircuitState { + CLOSED = 'CLOSED', // Normal Operation + OPEN = 'OPEN', // Failing, block requests + HALF_OPEN = 'HALF_OPEN' // Testing recovery +} + +interface CircuitBreakerConfig { + failureThreshold: number; + successThreshold: number; + timeout: number; +} + +class CircuitBreaker { + private state: CircuitState = CircuitState.CLOSED; + private failureCount = 0; + private successCount = 0; + private lastFailureTime?: number; + private config: CircuitBreakerConfig; + + constructor(config: Partial = {}) { + this.config = { + failureThreshold: 5, + successThreshold: 2, + timeout: 30000, + ...config + }; + } + + async execute(fn: () => Promise): Promise { + if (this.state === CircuitState.OPEN) { + if (this.shouldAttemptReset()) { + this.state = CircuitState.HALF_OPEN; + } else { + throw new Error('Circuit breaker is OPEN'); + } + } + + try { + const result = await fn(); + this.onSuccess(); + return result; + } catch (error) { + this.onFailure(); + throw error; + } + } + + private shouldAttemptReset(): boolean { + return ( + this.lastFailureTime !== undefined && + Date.now() - this.lastFailureTime >= this.config.timeout + ); + } + + private onSuccess() { + if (this.state === CircuitState.HALF_OPEN) { + this.successCount++; + + if (this.successCount >= this.config.successThreshold) { + this.reset(); + } + } + + this.failureCount = 0; + } + + private onFailure() { + this.failureCount++; + this.lastFailureTime = Date.now(); + + if (this.state === CircuitState.HALF_OPEN) { + this.state = CircuitState.OPEN; + this.successCount = 0; + } else if (this.failureCount >= this.config.failureThreshold) { + this.state = CircuitState.OPEN; + } + } + + private reset() { + this.state = CircuitState.CLOSED; + this.failureCount = 0; + this.successCount = 0; + this.lastFailureTime = undefined; + } + + getState(): CircuitState { + return this.state; + } +} + +// Verwendung +const circuitBreaker = new CircuitBreaker({ failureThreshold: 3 }); + +async function callExternalApi() { + return circuitBreaker.execute(async () => { + return apiClient.get('/data'); + }); +} +``` + +--- + +## Webhook Handler + +```typescript +import { createHmac, timingSafeEqual } from 'crypto'; + +interface WebhookConfig { + secret: string; + tolerance: number; // Sekunden +} + +class WebhookHandler { + private config: WebhookConfig; + private handlers: Map Promise> = new Map(); + + constructor(config: WebhookConfig) { + this.config = config; + } + + register(event: string, handler: (data: any) => Promise) { + this.handlers.set(event, handler); + } + + async handle( + payload: string, + signature: string, + timestamp: string + ): Promise { + // 1. Signature validieren + if (!this.verifySignature(payload, signature, timestamp)) { + throw new Error('Invalid webhook signature'); + } + + // 2. Timestamp validieren (Replay Attack Prevention) + if (!this.verifyTimestamp(timestamp)) { + throw new Error('Webhook timestamp too old'); + } + + // 3. Event verarbeiten + const event = JSON.parse(payload); + const handler = this.handlers.get(event.type); + + if (handler) { + await handler(event.data); + } else { + console.warn(`No handler for event type: ${event.type}`); + } + } + + private verifySignature( + payload: string, + signature: string, + timestamp: string + ): boolean { + const signedPayload = `${timestamp}.${payload}`; + const expectedSignature = createHmac('sha256', this.config.secret) + .update(signedPayload) + .digest('hex'); + + const sigBuffer = Buffer.from(signature); + const expectedBuffer = Buffer.from(`sha256=${expectedSignature}`); + + return sigBuffer.length === expectedBuffer.length && + timingSafeEqual(sigBuffer, expectedBuffer); + } + + private verifyTimestamp(timestamp: string): boolean { + const webhookTime = parseInt(timestamp, 10) * 1000; + const now = Date.now(); + const tolerance = this.config.tolerance * 1000; + + return Math.abs(now - webhookTime) <= tolerance; + } +} + +// Express Route +import express from 'express'; + +const app = express(); +const webhookHandler = new WebhookHandler({ + secret: process.env.WEBHOOK_SECRET!, + tolerance: 300 // 5 Minuten +}); + +// Webhook Events registrieren +webhookHandler.register('order.created', async (data) => { + await processNewOrder(data); +}); + +webhookHandler.register('payment.completed', async (data) => { + await completePayment(data); +}); + +// Webhook Endpoint +app.post('/webhooks', + express.raw({ type: 'application/json' }), + async (req, res) => { + const signature = req.headers['x-webhook-signature'] as string; + const timestamp = req.headers['x-webhook-timestamp'] as string; + + try { + await webhookHandler.handle( + req.body.toString(), + signature, + timestamp + ); + + res.status(200).send('OK'); + } catch (error) { + console.error('Webhook error:', error); + res.status(400).send('Invalid webhook'); + } + } +); +``` + +--- + +## Pagination Handler + +```typescript +interface PaginatedResponse { + data: T[]; + pagination: { + page: number; + pageSize: number; + total: number; + hasMore: boolean; + }; +} + +async function* paginatedFetch( + apiClient: ApiClient, + endpoint: string, + pageSize: number = 100 +): AsyncGenerator { + let page = 1; + let hasMore = true; + + while (hasMore) { + const response = await apiClient.get>( + `${endpoint}?page=${page}&pageSize=${pageSize}` + ); + + yield response.data; + + hasMore = response.pagination.hasMore; + page++; + } +} + +// Cursor-based Pagination +async function* cursorPaginatedFetch( + apiClient: ApiClient, + endpoint: string, + limit: number = 100 +): AsyncGenerator { + let cursor: string | null = null; + + while (true) { + const url = cursor + ? `${endpoint}?cursor=${cursor}&limit=${limit}` + : `${endpoint}?limit=${limit}`; + + const response = await apiClient.get<{ + data: T[]; + nextCursor: string | null; + }>(url); + + yield response.data; + + if (!response.nextCursor) break; + cursor = response.nextCursor; + } +} + +// Verwendung +async function fetchAllProducts() { + const allProducts: Product[] = []; + + for await (const products of paginatedFetch(apiClient, '/products')) { + allProducts.push(...products); + console.log(`Fetched ${allProducts.length} products so far`); + } + + return allProducts; +} +``` + +--- + +## Response Caching + +```typescript +interface CacheEntry { + data: T; + expiresAt: number; + etag?: string; +} + +class ResponseCache { + private cache: Map> = new Map(); + private defaultTTL: number; + + constructor(defaultTTL: number = 60000) { + this.defaultTTL = defaultTTL; + } + + set(key: string, data: T, ttl?: number, etag?: string) { + this.cache.set(key, { + data, + expiresAt: Date.now() + (ttl || this.defaultTTL), + etag + }); + } + + get(key: string): CacheEntry | null { + const entry = this.cache.get(key); + + if (!entry) return null; + + if (Date.now() > entry.expiresAt) { + this.cache.delete(key); + return null; + } + + return entry; + } + + invalidate(pattern: string | RegExp) { + for (const key of this.cache.keys()) { + if (typeof pattern === 'string' ? key.includes(pattern) : pattern.test(key)) { + this.cache.delete(key); + } + } + } +} + +// Mit ETag Support +async function fetchWithCache( + apiClient: ApiClient, + url: string, + cache: ResponseCache +): Promise { + const cacheKey = url; + const cached = cache.get(cacheKey); + + const headers: Record = {}; + + if (cached?.etag) { + headers['If-None-Match'] = cached.etag; + } + + try { + const response = await apiClient.get(url, { headers }); + const etag = (response as any).headers?.etag; + + cache.set(cacheKey, response, undefined, etag); + return response; + } catch (error: any) { + if (error.response?.status === 304 && cached) { + return cached.data; + } + throw error; + } +} +``` + +--- + +## Fazit + +Robuste API Integrationen brauchen: + +1. **Retry Logic**: Exponential Backoff mit Jitter +2. **Rate Limiting**: Client-seitige Throttling +3. **Circuit Breaker**: Fail Fast bei Ausfällen +4. **Caching**: Reduziert Last und Latenz + +Production-ready von Anfang an. + +--- + +## Bildprompts + +1. "API calls with retry arrows showing resilience, error recovery" +2. "Circuit breaker switch between services, fail fast concept" +3. "Rate limiter queue with request tickets, throttling visualization" + +--- + +## Quellen + +- [Axios Documentation](https://axios-http.com/) +- [Circuit Breaker Pattern](https://martinfowler.com/bliki/CircuitBreaker.html) +- [API Rate Limiting Best Practices](https://cloud.google.com/architecture/rate-limiting-strategies) +- [Webhook Security](https://webhooks.fyi/security/hmac) diff --git a/blog-posts/59-email-automation.md b/blog-posts/59-email-automation.md new file mode 100644 index 0000000..6efde19 --- /dev/null +++ b/blog-posts/59-email-automation.md @@ -0,0 +1,593 @@ +# Email Automation mit Resend & React Email + +**Meta-Description:** Moderne Email Automation. React Email Templates, Resend API, Transactional Emails und Email Sequenzen. + +**Keywords:** Email Automation, Resend, React Email, Transactional Email, Email Templates, Nodemailer, SMTP + +--- + +## Einführung + +Email bleibt der wichtigste Kommunikationskanal. Mit **React Email** und **Resend** werden Email-Templates zu First-Class React Components – testbar, typsicher und versioniert. + +--- + +## Email Stack 2026 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ MODERN EMAIL STACK │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Template Layer: │ +│ ├── React Email (JSX → HTML) │ +│ ├── MJML Alternative │ +│ └── Tailwind Email Support │ +│ │ +│ Delivery Layer: │ +│ ├── Resend (Developer-First) │ +│ ├── SendGrid │ +│ ├── Postmark │ +│ └── AWS SES │ +│ │ +│ Features: │ +│ ├── Transactional (Welcome, Reset, etc.) │ +│ ├── Marketing (Newsletters) │ +│ ├── Sequences (Drip Campaigns) │ +│ └── Webhooks (Delivery Status) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## React Email Setup + +```bash +npm install @react-email/components react-email resend +``` + +```typescript +// emails/welcome.tsx +import { + Html, + Head, + Preview, + Body, + Container, + Section, + Text, + Button, + Img, + Hr, + Link +} from '@react-email/components'; + +interface WelcomeEmailProps { + userName: string; + verificationUrl: string; +} + +export default function WelcomeEmail({ + userName, + verificationUrl +}: WelcomeEmailProps) { + return ( + + + Willkommen bei unserer Plattform, {userName}! + + + + Logo + +
+ Willkommen, {userName}! 👋 + + + Vielen Dank für Ihre Registrierung. Bitte bestätigen Sie Ihre + E-Mail-Adresse, um alle Funktionen nutzen zu können. + + + + + + Oder kopieren Sie diesen Link in Ihren Browser: + + + {verificationUrl} +
+ +
+ +
+ + © 2026 Unser Unternehmen. Alle Rechte vorbehalten. + + + Abmelden + +
+
+ + + ); +} + +// Styles +const main = { + backgroundColor: '#f6f9fc', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' +}; + +const container = { + backgroundColor: '#ffffff', + margin: '0 auto', + padding: '20px 0', + maxWidth: '600px' +}; + +const logo = { + margin: '0 auto 20px', + display: 'block' +}; + +const content = { + padding: '0 40px' +}; + +const heading = { + fontSize: '24px', + fontWeight: 'bold', + color: '#1a1a1a', + marginBottom: '20px' +}; + +const paragraph = { + fontSize: '16px', + lineHeight: '26px', + color: '#484848' +}; + +const button = { + backgroundColor: '#5046e5', + borderRadius: '8px', + color: '#ffffff', + fontSize: '16px', + fontWeight: 'bold', + textDecoration: 'none', + textAlign: 'center' as const, + display: 'block', + padding: '14px 24px', + margin: '20px 0' +}; + +const link = { + color: '#5046e5', + fontSize: '14px', + wordBreak: 'break-all' as const +}; + +const hr = { + borderColor: '#e6ebf1', + margin: '20px 0' +}; + +const footer = { + padding: '0 40px' +}; + +const footerText = { + fontSize: '12px', + color: '#9ca299' +}; + +const footerLink = { + color: '#9ca299', + fontSize: '12px' +}; +``` + +--- + +## Resend Integration + +```typescript +// lib/email.ts +import { Resend } from 'resend'; +import WelcomeEmail from '@/emails/welcome'; +import PasswordResetEmail from '@/emails/password-reset'; +import OrderConfirmationEmail from '@/emails/order-confirmation'; + +const resend = new Resend(process.env.RESEND_API_KEY); + +// Single Email senden +export async function sendWelcomeEmail( + to: string, + userName: string, + verificationUrl: string +) { + const { data, error } = await resend.emails.send({ + from: 'Unser Team ', + to, + subject: `Willkommen, ${userName}!`, + react: WelcomeEmail({ userName, verificationUrl }) + }); + + if (error) { + console.error('Email send error:', error); + throw error; + } + + return data; +} + +// Password Reset +export async function sendPasswordResetEmail( + to: string, + resetUrl: string +) { + return resend.emails.send({ + from: 'Sicherheit ', + to, + subject: 'Passwort zurücksetzen', + react: PasswordResetEmail({ resetUrl }) + }); +} + +// Order Confirmation mit Anhang +export async function sendOrderConfirmation( + to: string, + order: Order, + invoicePdf: Buffer +) { + return resend.emails.send({ + from: 'Shop ', + to, + subject: `Bestellbestätigung #${order.id}`, + react: OrderConfirmationEmail({ order }), + attachments: [ + { + filename: `Rechnung-${order.id}.pdf`, + content: invoicePdf + } + ] + }); +} + +// Batch Send +export async function sendBulkEmails( + recipients: Array<{ email: string; data: any }> +) { + const emails = recipients.map(r => ({ + from: 'Newsletter ', + to: r.email, + subject: 'Unser Newsletter', + react: NewsletterEmail(r.data) + })); + + return resend.batch.send(emails); +} +``` + +--- + +## Email Sequenzen (Drip Campaigns) + +```typescript +import { Queue, Worker } from 'bullmq'; +import IORedis from 'ioredis'; + +const connection = new IORedis(process.env.REDIS_URL!); +const emailQueue = new Queue('email-sequences', { connection }); + +// Sequence Definition +interface EmailSequence { + id: string; + name: string; + steps: Array<{ + delay: number; // Millisekunden + template: string; + subject: string; + }>; +} + +const onboardingSequence: EmailSequence = { + id: 'onboarding', + name: 'Onboarding Sequence', + steps: [ + { + delay: 0, + template: 'welcome', + subject: 'Willkommen bei uns!' + }, + { + delay: 24 * 60 * 60 * 1000, // 1 Tag + template: 'getting-started', + subject: 'Erste Schritte' + }, + { + delay: 3 * 24 * 60 * 60 * 1000, // 3 Tage + template: 'tips', + subject: '5 Tipps für den Einstieg' + }, + { + delay: 7 * 24 * 60 * 60 * 1000, // 7 Tage + template: 'feedback', + subject: 'Wie gefällt es Ihnen?' + } + ] +}; + +// Sequence starten +async function startEmailSequence( + userId: string, + email: string, + sequenceId: string, + userData: Record +) { + const sequence = getSequenceById(sequenceId); + if (!sequence) throw new Error(`Sequence ${sequenceId} not found`); + + // Sequence State speichern + await db.emailSequenceState.create({ + data: { + userId, + sequenceId, + currentStep: 0, + status: 'active' + } + }); + + // Jobs für jeden Step einplanen + for (let i = 0; i < sequence.steps.length; i++) { + const step = sequence.steps[i]; + + await emailQueue.add( + 'send-sequence-email', + { + userId, + email, + sequenceId, + stepIndex: i, + template: step.template, + subject: step.subject, + userData + }, + { + delay: step.delay, + jobId: `${userId}-${sequenceId}-${i}` + } + ); + } +} + +// Worker für Sequence Emails +const worker = new Worker('email-sequences', async (job) => { + const { userId, email, sequenceId, stepIndex, template, subject, userData } = job.data; + + // Prüfen ob Sequence noch aktiv + const state = await db.emailSequenceState.findFirst({ + where: { userId, sequenceId } + }); + + if (state?.status !== 'active') { + console.log(`Sequence ${sequenceId} for user ${userId} is not active, skipping`); + return; + } + + // Email senden + const EmailComponent = getEmailTemplate(template); + + await resend.emails.send({ + from: 'Unser Team ', + to: email, + subject, + react: EmailComponent(userData) + }); + + // State updaten + await db.emailSequenceState.update({ + where: { id: state.id }, + data: { currentStep: stepIndex + 1 } + }); + + console.log(`Sent sequence email: ${template} to ${email}`); +}, { connection }); + +// Sequence abbrechen +async function cancelEmailSequence(userId: string, sequenceId: string) { + // State updaten + await db.emailSequenceState.updateMany({ + where: { userId, sequenceId }, + data: { status: 'cancelled' } + }); + + // Pending Jobs entfernen + const jobs = await emailQueue.getJobs(['delayed', 'waiting']); + for (const job of jobs) { + if (job.data.userId === userId && job.data.sequenceId === sequenceId) { + await job.remove(); + } + } +} +``` + +--- + +## Email Preview & Testing + +```typescript +// email-preview.ts (Development) +import { render } from '@react-email/render'; +import WelcomeEmail from '@/emails/welcome'; + +// HTML Preview generieren +const html = render(WelcomeEmail({ + userName: 'Max Mustermann', + verificationUrl: 'https://example.com/verify?token=abc123' +})); + +console.log(html); + +// React Email Dev Server +// package.json +{ + "scripts": { + "email:dev": "email dev --port 3001" + } +} + +// Dann: npm run email:dev +// Browser: http://localhost:3001 +``` + +```typescript +// Email Testing mit Jest +import { render } from '@react-email/render'; +import WelcomeEmail from '@/emails/welcome'; + +describe('WelcomeEmail', () => { + it('renders with correct user name', () => { + const html = render(WelcomeEmail({ + userName: 'Test User', + verificationUrl: 'https://example.com/verify' + })); + + expect(html).toContain('Test User'); + expect(html).toContain('Willkommen'); + }); + + it('includes verification link', () => { + const html = render(WelcomeEmail({ + userName: 'Test User', + verificationUrl: 'https://example.com/verify?token=123' + })); + + expect(html).toContain('https://example.com/verify?token=123'); + }); + + it('has valid HTML structure', () => { + const html = render(WelcomeEmail({ + userName: 'Test User', + verificationUrl: 'https://example.com/verify' + })); + + expect(html).toMatch(//); + }); +}); +``` + +--- + +## Webhook Handling (Delivery Status) + +```typescript +// app/api/webhooks/resend/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { Webhook } from 'svix'; + +const webhookSecret = process.env.RESEND_WEBHOOK_SECRET!; + +export async function POST(request: NextRequest) { + const payload = await request.text(); + const headers = { + 'svix-id': request.headers.get('svix-id')!, + 'svix-timestamp': request.headers.get('svix-timestamp')!, + 'svix-signature': request.headers.get('svix-signature')! + }; + + const wh = new Webhook(webhookSecret); + + let event: any; + try { + event = wh.verify(payload, headers); + } catch (err) { + return NextResponse.json({ error: 'Invalid signature' }, { status: 400 }); + } + + // Event Type handling + switch (event.type) { + case 'email.sent': + await handleEmailSent(event.data); + break; + + case 'email.delivered': + await handleEmailDelivered(event.data); + break; + + case 'email.opened': + await handleEmailOpened(event.data); + break; + + case 'email.clicked': + await handleEmailClicked(event.data); + break; + + case 'email.bounced': + await handleEmailBounced(event.data); + break; + + case 'email.complained': + await handleEmailComplained(event.data); + break; + } + + return NextResponse.json({ received: true }); +} + +async function handleEmailBounced(data: any) { + // Email-Adresse als ungültig markieren + await db.user.update({ + where: { email: data.to }, + data: { emailStatus: 'bounced' } + }); +} + +async function handleEmailComplained(data: any) { + // Spam-Beschwerde: User aus Listen entfernen + await db.user.update({ + where: { email: data.to }, + data: { marketingOptIn: false } + }); +} +``` + +--- + +## Fazit + +Moderne Email Automation braucht: + +1. **React Email**: Component-basierte Templates +2. **Resend**: Developer-First API +3. **Sequences**: Automatisierte Drip Campaigns +4. **Webhooks**: Delivery Tracking + +Email als Code – testbar, typsicher, versioniert. + +--- + +## Bildprompts + +1. "Email template components assembling like Lego blocks, React Email concept" +2. "Email sequence timeline with automated sends, drip campaign visualization" +3. "Delivery status tracking with checkmarks, email analytics dashboard" + +--- + +## Quellen + +- [React Email Documentation](https://react.email/) +- [Resend Documentation](https://resend.com/docs) +- [Email Accessibility Guidelines](https://www.emailonacid.com/blog/article/email-development/email-accessibilty-in-2019/) +- [Can I Email](https://www.caniemail.com/) diff --git a/blog-posts/60-workflow-orchestration.md b/blog-posts/60-workflow-orchestration.md new file mode 100644 index 0000000..9e31b3e --- /dev/null +++ b/blog-posts/60-workflow-orchestration.md @@ -0,0 +1,522 @@ +# Workflow Orchestration mit Temporal + +**Meta-Description:** Workflow Orchestration für komplexe Business Logic. Temporal.io, Durable Workflows, Sagas und Error Recovery. + +**Keywords:** Workflow Orchestration, Temporal, Durable Workflows, Saga Pattern, Business Process, Error Recovery + +--- + +## Einführung + +Komplexe Business-Prozesse brauchen mehr als einfache Funktionen. **Workflow Orchestration** mit Temporal bietet durable Execution, automatische Retries und State Management für langlebige Prozesse. + +--- + +## Warum Workflow Orchestration? + +``` +┌─────────────────────────────────────────────────────────────┐ +│ WORKFLOW ORCHESTRATION │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Probleme ohne Orchestration: │ +│ ├── Verteilte Transaktionen │ +│ ├── Timeout/Failure Handling │ +│ ├── State Management über Zeit │ +│ ├── Retries mit Exponential Backoff │ +│ └── Compensating Transactions (Rollbacks) │ +│ │ +│ Temporal Lösung: │ +│ ├── Durable Execution (überleben Crashes) │ +│ ├── Automatische Retries │ +│ ├── Timeouts auf allen Ebenen │ +│ ├── Saga Pattern Support │ +│ └── Workflow as Code (TypeScript) │ +│ │ +│ Use Cases: │ +│ ├── E-Commerce: Order Processing │ +│ ├── Finance: Payment Processing │ +│ ├── HR: Onboarding Workflows │ +│ ├── DevOps: CI/CD Pipelines │ +│ └── Data: ETL Pipelines │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Temporal Setup + +```bash +# Temporal Server (Docker) +docker run --rm -p 7233:7233 temporalio/auto-setup + +# SDK installieren +npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity +``` + +```typescript +// src/activities.ts +import { proxyActivities, defineActivity } from '@temporalio/workflow'; + +// Activities sind normale async Funktionen +export const activities = { + async sendEmail(to: string, subject: string, body: string): Promise { + console.log(`Sending email to ${to}`); + // Resend, SendGrid, etc. + await resend.emails.send({ to, subject, html: body }); + return true; + }, + + async chargePayment(userId: string, amount: number): Promise { + console.log(`Charging ${amount} for user ${userId}`); + // Stripe, PayPal, etc. + const charge = await stripe.charges.create({ + amount: Math.round(amount * 100), + currency: 'eur', + customer: userId + }); + return charge.id; + }, + + async reserveInventory(productId: string, quantity: number): Promise { + console.log(`Reserving ${quantity} of ${productId}`); + // Inventory Service + const reservation = await inventoryService.reserve(productId, quantity); + return reservation.id; + }, + + async releaseInventory(reservationId: string): Promise { + console.log(`Releasing reservation ${reservationId}`); + await inventoryService.release(reservationId); + }, + + async refundPayment(chargeId: string): Promise { + console.log(`Refunding charge ${chargeId}`); + await stripe.refunds.create({ charge: chargeId }); + }, + + async createShipment(orderId: string, address: Address): Promise { + console.log(`Creating shipment for order ${orderId}`); + const shipment = await shippingService.create({ orderId, address }); + return shipment.trackingNumber; + } +}; + +export type Activities = typeof activities; +``` + +--- + +## Workflow Definition + +```typescript +// src/workflows/order-processing.ts +import { + proxyActivities, + sleep, + defineSignal, + defineQuery, + setHandler, + condition, + ApplicationFailure +} from '@temporalio/workflow'; +import type { Activities } from '../activities'; + +// Activity Proxies mit Retry Config +const { + sendEmail, + chargePayment, + reserveInventory, + releaseInventory, + refundPayment, + createShipment +} = proxyActivities({ + startToCloseTimeout: '1 minute', + retry: { + initialInterval: '1 second', + backoffCoefficient: 2, + maximumAttempts: 5, + nonRetryableErrorTypes: ['InvalidPaymentError'] + } +}); + +// Signals für externe Events +const cancelOrderSignal = defineSignal('cancelOrder'); +const orderShippedSignal = defineSignal<[string]>('orderShipped'); + +// Queries für Status-Abfragen +const getStatusQuery = defineQuery('getStatus'); + +interface OrderInput { + orderId: string; + userId: string; + products: Array<{ productId: string; quantity: number }>; + totalAmount: number; + shippingAddress: Address; +} + +interface OrderStatus { + state: 'pending' | 'paid' | 'reserved' | 'shipped' | 'cancelled' | 'failed'; + chargeId?: string; + reservationIds?: string[]; + trackingNumber?: string; + error?: string; +} + +export async function orderProcessingWorkflow(input: OrderInput): Promise { + let status: OrderStatus = { state: 'pending' }; + let cancelled = false; + + // Signal Handler + setHandler(cancelOrderSignal, () => { + cancelled = true; + }); + + // Query Handler + setHandler(getStatusQuery, () => status); + + try { + // 1. Payment + status.chargeId = await chargePayment(input.userId, input.totalAmount); + status.state = 'paid'; + + if (cancelled) { + await compensatePayment(status.chargeId); + status.state = 'cancelled'; + return status; + } + + // 2. Inventory Reservation + status.reservationIds = []; + for (const product of input.products) { + const reservationId = await reserveInventory(product.productId, product.quantity); + status.reservationIds.push(reservationId); + } + status.state = 'reserved'; + + if (cancelled) { + await compensate(status); + status.state = 'cancelled'; + return status; + } + + // 3. Shipment + status.trackingNumber = await createShipment(input.orderId, input.shippingAddress); + status.state = 'shipped'; + + // 4. Confirmation Email + await sendEmail( + input.userId, + 'Ihre Bestellung wurde versandt', + `Tracking: ${status.trackingNumber}` + ); + + return status; + } catch (error) { + // Compensating Transactions + await compensate(status); + status.state = 'failed'; + status.error = (error as Error).message; + + // Notify Customer + await sendEmail( + input.userId, + 'Problem mit Ihrer Bestellung', + 'Bitte kontaktieren Sie unseren Support.' + ); + + throw error; + } +} + +// Saga Compensation +async function compensate(status: OrderStatus) { + // Reverse order: Shipment → Inventory → Payment + + // Release Inventory + if (status.reservationIds) { + for (const reservationId of status.reservationIds) { + await releaseInventory(reservationId); + } + } + + // Refund Payment + if (status.chargeId) { + await compensatePayment(status.chargeId); + } +} + +async function compensatePayment(chargeId: string) { + await refundPayment(chargeId); +} +``` + +--- + +## Worker Setup + +```typescript +// src/worker.ts +import { Worker } from '@temporalio/worker'; +import { activities } from './activities'; + +async function run() { + const worker = await Worker.create({ + workflowsPath: require.resolve('./workflows'), + activities, + taskQueue: 'order-processing', + // Worker Options + maxConcurrentActivityExecutions: 10, + maxConcurrentWorkflowTaskExecutions: 5 + }); + + console.log('Worker started'); + await worker.run(); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); +``` + +--- + +## Client Usage + +```typescript +// src/client.ts +import { Client, Connection } from '@temporalio/client'; +import { orderProcessingWorkflow } from './workflows/order-processing'; + +async function main() { + const connection = await Connection.connect({ + address: 'localhost:7233' + }); + + const client = new Client({ connection }); + + // Workflow starten + const handle = await client.workflow.start(orderProcessingWorkflow, { + taskQueue: 'order-processing', + workflowId: `order-${orderId}`, + args: [{ + orderId: '12345', + userId: 'user-789', + products: [ + { productId: 'prod-1', quantity: 2 }, + { productId: 'prod-2', quantity: 1 } + ], + totalAmount: 99.99, + shippingAddress: { + street: 'Hauptstraße 1', + city: 'Berlin', + zip: '10115' + } + }] + }); + + console.log(`Started workflow: ${handle.workflowId}`); + + // Status abfragen + const status = await handle.query('getStatus'); + console.log('Current status:', status); + + // Auf Completion warten + const result = await handle.result(); + console.log('Workflow completed:', result); +} + +// Order canceln +async function cancelOrder(workflowId: string) { + const handle = client.workflow.getHandle(workflowId); + await handle.signal('cancelOrder'); +} +``` + +--- + +## API Route Integration + +```typescript +// app/api/orders/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { Client, Connection } from '@temporalio/client'; +import { orderProcessingWorkflow } from '@/workflows/order-processing'; + +let client: Client; + +async function getClient() { + if (!client) { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS || 'localhost:7233' + }); + client = new Client({ connection }); + } + return client; +} + +export async function POST(request: NextRequest) { + const body = await request.json(); + + const client = await getClient(); + + const handle = await client.workflow.start(orderProcessingWorkflow, { + taskQueue: 'order-processing', + workflowId: `order-${body.orderId}`, + args: [body] + }); + + return NextResponse.json({ + workflowId: handle.workflowId, + status: 'started' + }); +} + +export async function GET(request: NextRequest) { + const workflowId = request.nextUrl.searchParams.get('workflowId'); + + if (!workflowId) { + return NextResponse.json({ error: 'workflowId required' }, { status: 400 }); + } + + const client = await getClient(); + const handle = client.workflow.getHandle(workflowId); + + try { + const status = await handle.query('getStatus'); + return NextResponse.json({ workflowId, status }); + } catch (error) { + return NextResponse.json({ error: 'Workflow not found' }, { status: 404 }); + } +} +``` + +--- + +## Timer & Scheduling + +```typescript +// src/workflows/subscription-renewal.ts +import { proxyActivities, sleep, continueAsNew } from '@temporalio/workflow'; + +export async function subscriptionRenewalWorkflow( + userId: string, + planId: string +): Promise { + const { chargeSubscription, sendRenewalReminder, sendRenewalConfirmation } = + proxyActivities({ + startToCloseTimeout: '1 minute' + }); + + // Reminder 7 Tage vor Ablauf + await sleep('23 days'); + await sendRenewalReminder(userId); + + // Warte bis Ablauf + await sleep('7 days'); + + // Charge + await chargeSubscription(userId, planId); + await sendRenewalConfirmation(userId); + + // Workflow für nächsten Monat fortsetzen + await continueAsNew(userId, planId); +} +``` + +--- + +## Error Handling & Compensation + +```typescript +// Saga Pattern mit expliziter Compensation +import { proxyActivities, ApplicationFailure } from '@temporalio/workflow'; + +interface SagaStep { + execute: () => Promise; + compensate: (result: T) => Promise; +} + +async function runSaga(steps: SagaStep[]): Promise { + const results: T[] = []; + const completedSteps: SagaStep[] = []; + + try { + for (const step of steps) { + const result = await step.execute(); + results.push(result); + completedSteps.push(step); + } + return results; + } catch (error) { + // Compensate in reverse order + for (let i = completedSteps.length - 1; i >= 0; i--) { + const step = completedSteps[i]; + const result = results[i]; + try { + await step.compensate(result); + } catch (compensationError) { + console.error('Compensation failed:', compensationError); + // Log aber nicht re-throw + } + } + throw error; + } +} + +// Verwendung +export async function bookingWorkflow(input: BookingInput) { + const results = await runSaga([ + { + execute: () => reserveHotel(input.hotelId, input.dates), + compensate: (reservationId) => cancelHotelReservation(reservationId) + }, + { + execute: () => reserveFlight(input.flightId), + compensate: (reservationId) => cancelFlightReservation(reservationId) + }, + { + execute: () => chargeCustomer(input.customerId, input.total), + compensate: (chargeId) => refundCustomer(chargeId) + } + ]); + + return { + hotelReservation: results[0], + flightReservation: results[1], + chargeId: results[2] + }; +} +``` + +--- + +## Fazit + +Temporal Workflow Orchestration bietet: + +1. **Durability**: Workflows überleben Server-Crashes +2. **Visibility**: Vollständige History & State +3. **Reliability**: Automatische Retries & Timeouts +4. **Scalability**: Horizontale Worker-Skalierung + +Für komplexe Business-Prozesse unverzichtbar. + +--- + +## Bildprompts + +1. "Workflow steps connected with arrows, showing state persistence" +2. "Saga pattern with forward and backward arrows, compensation flow" +3. "Multiple workers processing workflow tasks, distributed orchestration" + +--- + +## Quellen + +- [Temporal.io Documentation](https://docs.temporal.io/) +- [Temporal TypeScript SDK](https://typescript.temporal.io/) +- [Saga Pattern](https://microservices.io/patterns/data/saga.html) +- [Durable Execution](https://temporal.io/blog/building-reliable-distributed-systems-in-node) diff --git a/blog-posts/61-home-assistant-integration.md b/blog-posts/61-home-assistant-integration.md new file mode 100644 index 0000000..42d9e68 --- /dev/null +++ b/blog-posts/61-home-assistant-integration.md @@ -0,0 +1,786 @@ +# Home Assistant Integration & Automation + +**Meta-Description:** Smart Home Automation mit Home Assistant. WebSocket API, Node-RED Integration, Custom Automations und YAML-Konfiguration. + +**Keywords:** Home Assistant, Smart Home, Automation, Node-RED, WebSocket API, YAML, IoT Platform + +--- + +## Einführung + +**Home Assistant** ist die führende Open-Source Smart Home Plattform. Mit über 2.800 Integrations, lokaler Kontrolle und einer aktiven Community bietet sie die perfekte Grundlage für jedes Smart Home Projekt. + +--- + +## Home Assistant Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ HOME ASSISTANT ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Frontend │ │ Core │ │ Supervisor │ │ +│ │ (React) │←──→│ (Python) │←──→│ (Docker) │ │ +│ └─────────────┘ └──────┬──────┘ └─────────────┘ │ +│ │ │ +│ ┌─────────────────────────┼─────────────────────────┐ │ +│ │ Integrations │ │ +│ ├─────────────────────────┼─────────────────────────┤ │ +│ │ ┌────────┐ ┌────────┐ │ ┌────────┐ ┌────────┐ │ │ +│ │ │ Zigbee │ │ Z-Wave │ │ │ MQTT │ │ HTTP │ │ │ +│ │ └────────┘ └────────┘ │ └────────┘ └────────┘ │ │ +│ │ ┌────────┐ ┌────────┐ │ ┌────────┐ ┌────────┐ │ │ +│ │ │ Thread │ │ Matter │ │ │Bluetooth│ │ Cloud │ │ │ +│ │ └────────┘ └────────┘ │ └────────┘ └────────┘ │ │ +│ └───────────────────────────────────────────────────┘ │ +│ │ +│ Protocols: WebSocket, REST API, MQTT │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Installation & Setup + +```bash +# Docker Installation +docker run -d \ + --name homeassistant \ + --privileged \ + --restart=unless-stopped \ + -e TZ=Europe/Berlin \ + -v /path/to/config:/config \ + -v /run/dbus:/run/dbus:ro \ + --network=host \ + ghcr.io/home-assistant/home-assistant:stable + +# Oder mit Docker Compose +# docker-compose.yml +version: '3' +services: + homeassistant: + container_name: homeassistant + image: "ghcr.io/home-assistant/home-assistant:stable" + volumes: + - ./config:/config + - /etc/localtime:/etc/localtime:ro + - /run/dbus:/run/dbus:ro + restart: unless-stopped + privileged: true + network_mode: host +``` + +--- + +## WebSocket API Integration + +```typescript +// lib/homeassistant-client.ts +import WebSocket from 'ws'; + +interface HAMessage { + id?: number; + type: string; + [key: string]: any; +} + +interface HAState { + entity_id: string; + state: string; + attributes: Record; + last_changed: string; + last_updated: string; +} + +class HomeAssistantClient { + private ws: WebSocket | null = null; + private messageId = 1; + private pendingRequests: Map void; + reject: (error: Error) => void; + }> = new Map(); + private eventSubscribers: Map void>> = new Map(); + + constructor( + private url: string, + private token: string + ) {} + + async connect(): Promise { + return new Promise((resolve, reject) => { + this.ws = new WebSocket(this.url); + + this.ws.on('open', () => { + console.log('Connected to Home Assistant'); + }); + + this.ws.on('message', async (data) => { + const message = JSON.parse(data.toString()); + await this.handleMessage(message, resolve); + }); + + this.ws.on('error', (error) => { + console.error('WebSocket error:', error); + reject(error); + }); + + this.ws.on('close', () => { + console.log('Connection closed'); + this.reconnect(); + }); + }); + } + + private async handleMessage(message: any, onAuth?: (value: void) => void) { + switch (message.type) { + case 'auth_required': + this.send({ type: 'auth', access_token: this.token }); + break; + + case 'auth_ok': + console.log('Authenticated with Home Assistant'); + onAuth?.(); + break; + + case 'auth_invalid': + throw new Error('Invalid authentication'); + + case 'result': + const pending = this.pendingRequests.get(message.id); + if (pending) { + if (message.success) { + pending.resolve(message.result); + } else { + pending.reject(new Error(message.error?.message)); + } + this.pendingRequests.delete(message.id); + } + break; + + case 'event': + this.handleEvent(message.event); + break; + } + } + + private handleEvent(event: any) { + const eventType = event.event_type; + const subscribers = this.eventSubscribers.get(eventType); + + if (subscribers) { + subscribers.forEach(callback => callback(event.data)); + } + + // Wildcard subscribers + const allSubscribers = this.eventSubscribers.get('*'); + if (allSubscribers) { + allSubscribers.forEach(callback => callback(event)); + } + } + + private send(message: HAMessage) { + if (this.ws?.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify(message)); + } + } + + private async sendCommand(message: Omit): Promise { + return new Promise((resolve, reject) => { + const id = this.messageId++; + this.pendingRequests.set(id, { resolve, reject }); + this.send({ ...message, id }); + + // Timeout nach 30 Sekunden + setTimeout(() => { + if (this.pendingRequests.has(id)) { + this.pendingRequests.delete(id); + reject(new Error('Request timeout')); + } + }, 30000); + }); + } + + // States abrufen + async getStates(): Promise { + return this.sendCommand({ type: 'get_states' }); + } + + async getState(entityId: string): Promise { + const states = await this.getStates(); + return states.find(s => s.entity_id === entityId); + } + + // Services aufrufen + async callService( + domain: string, + service: string, + serviceData?: Record, + target?: { entity_id?: string | string[] } + ): Promise { + await this.sendCommand({ + type: 'call_service', + domain, + service, + service_data: serviceData, + target + }); + } + + // Events abonnieren + async subscribeEvents( + eventType: string, + callback: (data: any) => void + ): Promise<() => void> { + if (!this.eventSubscribers.has(eventType)) { + this.eventSubscribers.set(eventType, new Set()); + + // Bei HA registrieren + await this.sendCommand({ + type: 'subscribe_events', + event_type: eventType === '*' ? undefined : eventType + }); + } + + this.eventSubscribers.get(eventType)!.add(callback); + + // Unsubscribe Function + return () => { + this.eventSubscribers.get(eventType)?.delete(callback); + }; + } + + // Trigger State Change + async subscribeStateChanges( + entityId: string, + callback: (newState: HAState, oldState: HAState) => void + ): Promise<() => void> { + return this.subscribeEvents('state_changed', (data) => { + if (data.entity_id === entityId) { + callback(data.new_state, data.old_state); + } + }); + } + + private reconnect() { + setTimeout(() => { + console.log('Reconnecting...'); + this.connect(); + }, 5000); + } + + disconnect() { + this.ws?.close(); + } +} + +// Verwendung +const ha = new HomeAssistantClient( + 'ws://homeassistant.local:8123/api/websocket', + process.env.HA_TOKEN! +); + +await ha.connect(); + +// Alle States abrufen +const states = await ha.getStates(); +console.log(`${states.length} entities found`); + +// Licht einschalten +await ha.callService('light', 'turn_on', { + brightness: 255, + color_temp: 300 +}, { + entity_id: 'light.wohnzimmer' +}); + +// State Changes beobachten +ha.subscribeStateChanges('sensor.temperature', (newState, oldState) => { + console.log(`Temperature changed: ${oldState.state} → ${newState.state}`); +}); +``` + +--- + +## REST API Integration + +```typescript +// lib/homeassistant-rest.ts +interface HARestConfig { + baseUrl: string; + token: string; +} + +class HomeAssistantREST { + private baseUrl: string; + private headers: HeadersInit; + + constructor(config: HARestConfig) { + this.baseUrl = config.baseUrl.replace(/\/$/, ''); + this.headers = { + 'Authorization': `Bearer ${config.token}`, + 'Content-Type': 'application/json' + }; + } + + private async request( + method: string, + endpoint: string, + body?: any + ): Promise { + const response = await fetch(`${this.baseUrl}/api${endpoint}`, { + method, + headers: this.headers, + body: body ? JSON.stringify(body) : undefined + }); + + if (!response.ok) { + throw new Error(`HA API Error: ${response.status}`); + } + + return response.json(); + } + + // API Status + async getApiStatus(): Promise<{ message: string }> { + return this.request('GET', '/'); + } + + // Config + async getConfig(): Promise { + return this.request('GET', '/config'); + } + + // States + async getStates(): Promise { + return this.request('GET', '/states'); + } + + async getState(entityId: string): Promise { + return this.request('GET', `/states/${entityId}`); + } + + async setState(entityId: string, state: string, attributes?: Record): Promise { + return this.request('POST', `/states/${entityId}`, { + state, + attributes + }); + } + + // Services + async getServices(): Promise> { + return this.request('GET', '/services'); + } + + async callService( + domain: string, + service: string, + data?: Record + ): Promise { + return this.request('POST', `/services/${domain}/${service}`, data); + } + + // History + async getHistory( + entityId: string, + startTime?: Date, + endTime?: Date + ): Promise { + const params = new URLSearchParams(); + if (startTime) params.set('start_time', startTime.toISOString()); + if (endTime) params.set('end_time', endTime.toISOString()); + params.set('filter_entity_id', entityId); + + return this.request('GET', `/history/period?${params}`); + } + + // Logbook + async getLogbook( + entityId?: string, + startTime?: Date, + endTime?: Date + ): Promise { + const params = new URLSearchParams(); + if (entityId) params.set('entity', entityId); + if (startTime) params.set('start_time', startTime.toISOString()); + if (endTime) params.set('end_time', endTime.toISOString()); + + return this.request('GET', `/logbook?${params}`); + } + + // Template Rendering + async renderTemplate(template: string): Promise { + const result = await this.request<{ result: string }>('POST', '/template', { template }); + return result.result; + } +} + +// API Route für Next.js +// app/api/home/lights/route.ts +import { NextRequest, NextResponse } from 'next/server'; + +const ha = new HomeAssistantREST({ + baseUrl: process.env.HA_URL!, + token: process.env.HA_TOKEN! +}); + +export async function GET() { + const states = await ha.getStates(); + const lights = states.filter(s => s.entity_id.startsWith('light.')); + + return NextResponse.json(lights.map(light => ({ + id: light.entity_id, + name: light.attributes.friendly_name, + state: light.state, + brightness: light.attributes.brightness, + color_temp: light.attributes.color_temp + }))); +} + +export async function POST(request: NextRequest) { + const { entityId, action, ...params } = await request.json(); + + await ha.callService('light', action, { + entity_id: entityId, + ...params + }); + + return NextResponse.json({ success: true }); +} +``` + +--- + +## YAML Automations + +```yaml +# automations.yaml + +# Licht bei Bewegung einschalten +- id: 'motion_light_on' + alias: 'Flur Licht bei Bewegung' + description: 'Schaltet das Flur-Licht bei Bewegung ein' + trigger: + - platform: state + entity_id: binary_sensor.flur_motion + to: 'on' + condition: + - condition: state + entity_id: sun.sun + state: 'below_horizon' + - condition: state + entity_id: input_boolean.guest_mode + state: 'off' + action: + - service: light.turn_on + target: + entity_id: light.flur + data: + brightness_pct: 80 + transition: 2 + - delay: '00:05:00' + - service: light.turn_off + target: + entity_id: light.flur + data: + transition: 5 + mode: restart + +# Temperatur-basierte Heizungssteuerung +- id: 'heating_control' + alias: 'Heizung Temperatur Kontrolle' + trigger: + - platform: numeric_state + entity_id: sensor.wohnzimmer_temperature + below: 20 + condition: + - condition: state + entity_id: input_boolean.heating_enabled + state: 'on' + - condition: time + after: '06:00:00' + before: '22:00:00' + action: + - service: climate.set_temperature + target: + entity_id: climate.wohnzimmer + data: + temperature: 22 + hvac_mode: heat + +# Guten Morgen Routine +- id: 'morning_routine' + alias: 'Guten Morgen Routine' + trigger: + - platform: time + at: input_datetime.wakeup_time + condition: + - condition: state + entity_id: input_boolean.alarm_enabled + state: 'on' + - condition: state + entity_id: person.user + state: 'home' + action: + - service: light.turn_on + target: + entity_id: light.schlafzimmer + data: + brightness_pct: 10 + color_temp: 500 + - delay: '00:05:00' + - service: light.turn_on + data: + brightness_pct: 50 + color_temp: 370 + transition: 300 + - service: media_player.play_media + target: + entity_id: media_player.schlafzimmer_speaker + data: + media_content_type: music + media_content_id: 'spotify:playlist:morning' + - service: notify.mobile_app + data: + message: "Guten Morgen! Es sind {{ states('sensor.outside_temperature') }}°C draußen." +``` + +--- + +## Node-RED Integration + +```json +// Node-RED Flow für Home Assistant +[ + { + "id": "ha-server", + "type": "server", + "name": "Home Assistant", + "addon": true + }, + { + "id": "motion-trigger", + "type": "server-state-changed", + "name": "Motion Detected", + "server": "ha-server", + "entityId": "binary_sensor.motion", + "entityIdType": "exact", + "outputInitially": false, + "stateType": "str", + "ifState": "on", + "ifStateType": "str", + "outputOnlyOnStateChange": true, + "wires": [["check-conditions"]] + }, + { + "id": "check-conditions", + "type": "api-current-state", + "name": "Check Sun State", + "server": "ha-server", + "entityId": "sun.sun", + "outputProperties": [ + { + "property": "payload", + "propertyType": "msg", + "value": "", + "valueType": "entityState" + } + ], + "wires": [["filter-night"]] + }, + { + "id": "filter-night", + "type": "switch", + "name": "Night Only", + "property": "payload", + "rules": [ + { "t": "eq", "v": "below_horizon", "vt": "str" } + ], + "wires": [["turn-on-light"]] + }, + { + "id": "turn-on-light", + "type": "api-call-service", + "name": "Turn On Light", + "server": "ha-server", + "service_domain": "light", + "service": "turn_on", + "entityId": "light.hallway", + "data": "{\"brightness_pct\": 80, \"transition\": 2}", + "wires": [[]] + } +] +``` + +```typescript +// Custom Node-RED Node +import { Red, Node, NodeProperties } from 'node-red'; + +interface SmartLightConfig extends NodeProperties { + server: string; + entityId: string; + brightness: number; + colorTemp: number; +} + +module.exports = function(RED: Red) { + function SmartLightNode(this: Node, config: SmartLightConfig) { + RED.nodes.createNode(this, config); + + const server = RED.nodes.getNode(config.server) as any; + + this.on('input', async (msg: any) => { + try { + const brightness = msg.brightness || config.brightness; + const colorTemp = msg.colorTemp || config.colorTemp; + + await server.websocket.callService('light', 'turn_on', { + entity_id: config.entityId, + brightness_pct: brightness, + color_temp: colorTemp + }); + + this.status({ fill: 'green', shape: 'dot', text: 'Light on' }); + this.send({ ...msg, payload: { success: true } }); + } catch (error) { + this.status({ fill: 'red', shape: 'ring', text: 'Error' }); + this.error(error); + } + }); + } + + RED.nodes.registerType('smart-light', SmartLightNode); +}; +``` + +--- + +## Custom Integration Development + +```python +# custom_components/my_sensor/sensor.py +"""Custom sensor integration for Home Assistant.""" +from homeassistant.components.sensor import ( + SensorEntity, + SensorDeviceClass, + SensorStateClass, +) +from homeassistant.const import UnitOfTemperature +from homeassistant.core import HomeAssistant +from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the sensor platform.""" + async_add_entities([ + MySensor(config_entry), + ]) + +class MySensor(SensorEntity): + """Representation of a custom sensor.""" + + _attr_name = "My Custom Sensor" + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + + def __init__(self, config_entry: ConfigEntry) -> None: + """Initialize the sensor.""" + self._attr_unique_id = f"{config_entry.entry_id}_temperature" + self._attr_native_value = None + + async def async_update(self) -> None: + """Fetch new state data for the sensor.""" + # API Call oder Sensor-Abfrage + self._attr_native_value = await self._fetch_temperature() + + async def _fetch_temperature(self) -> float: + """Fetch temperature from external source.""" + # Implementierung + return 21.5 +``` + +--- + +## Dashboard Configuration + +```yaml +# ui-lovelace.yaml +title: Smart Home +views: + - title: Übersicht + path: overview + icon: mdi:home + cards: + - type: weather-forecast + entity: weather.home + + - type: grid + columns: 3 + cards: + - type: button + entity: light.wohnzimmer + name: Wohnzimmer + icon: mdi:sofa + tap_action: + action: toggle + + - type: button + entity: light.schlafzimmer + name: Schlafzimmer + icon: mdi:bed + + - type: button + entity: script.movie_mode + name: Kino Modus + icon: mdi:movie + + - type: entities + title: Temperaturen + entities: + - entity: sensor.wohnzimmer_temperature + - entity: sensor.schlafzimmer_temperature + - entity: sensor.aussen_temperature + + - type: history-graph + title: Temperaturverlauf + hours_to_show: 24 + entities: + - entity: sensor.wohnzimmer_temperature + - entity: sensor.aussen_temperature +``` + +--- + +## Fazit + +Home Assistant bietet: + +1. **Lokale Kontrolle**: Keine Cloud-Abhängigkeit +2. **2800+ Integrations**: Alle Smart Home Protokolle +3. **Flexible APIs**: WebSocket, REST, MQTT +4. **Node-RED**: Visual Automation Flows + +Die perfekte Plattform für Smart Home Projekte. + +--- + +## Bildprompts + +1. "Smart home dashboard with device controls, Home Assistant interface" +2. "Automation flow connecting sensors and actuators, Node-RED visual programming" +3. "Home network diagram with IoT devices, local smart home server" + +--- + +## Quellen + +- [Home Assistant Documentation](https://www.home-assistant.io/) +- [Home Assistant 2026.1 Release](https://www.home-assistant.io/blog/2026/01/07/release-20261) +- [Node-RED Home Assistant](https://flows.nodered.org/node/node-red-contrib-home-assistant) +- [Home Assistant WebSocket API](https://developers.home-assistant.io/docs/api/websocket) diff --git a/blog-posts/62-mqtt-protocol-iot.md b/blog-posts/62-mqtt-protocol-iot.md new file mode 100644 index 0000000..8327b0c --- /dev/null +++ b/blog-posts/62-mqtt-protocol-iot.md @@ -0,0 +1,815 @@ +# MQTT Protocol für IoT-Kommunikation + +**Meta-Description:** MQTT Protokoll für IoT. Publish/Subscribe Pattern, QoS Levels, Broker Setup und Node.js Integration mit MQTT.js. + +**Keywords:** MQTT, IoT, Publish Subscribe, MQTT.js, Mosquitto, Message Broker, QoS, Real-time + +--- + +## Einführung + +**MQTT** (Message Queuing Telemetry Transport) ist das Standard-Protokoll für IoT-Kommunikation. Leichtgewichtig, effizient und für instabile Netzwerke optimiert – perfekt für Sensor-Netzwerke und Smart Home Anwendungen. + +--- + +## MQTT Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ MQTT ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Publishers Broker Subscribers │ +│ ┌─────────┐ ┌──────────┐ ┌─────────┐ │ +│ │ Sensor │────────→│ │────────→│Dashboard│ │ +│ └─────────┘ │ │ └─────────┘ │ +│ │ Mosquitto│ │ +│ ┌─────────┐ │ / EMQX │ ┌─────────┐ │ +│ │ Device │────────→│ │────────→│ Backend │ │ +│ └─────────┘ │ │ └─────────┘ │ +│ │ │ │ +│ ┌─────────┐ │ │ ┌─────────┐ │ +│ │ Gateway │←───────→│ │←───────→│ Mobile │ │ +│ └─────────┘ └──────────┘ └─────────┘ │ +│ │ +│ Topic Structure: │ +│ home/living-room/temperature → 21.5 │ +│ home/living-room/humidity → 45 │ +│ home/bedroom/light/state → on │ +│ home/bedroom/light/brightness → 80 │ +│ │ +│ QoS Levels: │ +│ 0: At most once (Fire & Forget) │ +│ 1: At least once (Acknowledged) │ +│ 2: Exactly once (Assured) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Mosquitto Broker Setup + +```bash +# Docker Installation +docker run -d \ + --name mosquitto \ + -p 1883:1883 \ + -p 9001:9001 \ + -v $(pwd)/mosquitto/config:/mosquitto/config \ + -v $(pwd)/mosquitto/data:/mosquitto/data \ + -v $(pwd)/mosquitto/log:/mosquitto/log \ + eclipse-mosquitto + +# mosquitto/config/mosquitto.conf +listener 1883 +listener 9001 +protocol websockets + +allow_anonymous false +password_file /mosquitto/config/password.txt + +persistence true +persistence_location /mosquitto/data/ + +log_dest file /mosquitto/log/mosquitto.log +log_type all + +# TLS Konfiguration +listener 8883 +cafile /mosquitto/certs/ca.crt +certfile /mosquitto/certs/server.crt +keyfile /mosquitto/certs/server.key +require_certificate false +``` + +```yaml +# docker-compose.yml +version: '3.8' +services: + mosquitto: + image: eclipse-mosquitto:2 + container_name: mosquitto + ports: + - "1883:1883" # MQTT + - "8883:8883" # MQTT/TLS + - "9001:9001" # WebSocket + volumes: + - ./mosquitto/config:/mosquitto/config + - ./mosquitto/data:/mosquitto/data + - ./mosquitto/log:/mosquitto/log + restart: unless-stopped +``` + +--- + +## MQTT.js Client + +```typescript +// lib/mqtt-client.ts +import mqtt, { MqttClient, IClientOptions, QoS } from 'mqtt'; + +interface MQTTConfig { + brokerUrl: string; + options?: IClientOptions; +} + +interface PublishOptions { + qos?: QoS; + retain?: boolean; +} + +type MessageHandler = (topic: string, message: string, packet: any) => void; + +class IoTMQTTClient { + private client: MqttClient | null = null; + private subscriptions: Map> = new Map(); + private connected = false; + private reconnectAttempts = 0; + private maxReconnectAttempts = 10; + + constructor(private config: MQTTConfig) {} + + async connect(): Promise { + return new Promise((resolve, reject) => { + const options: IClientOptions = { + clientId: `mqtt_${Math.random().toString(16).slice(2, 10)}`, + clean: true, + connectTimeout: 30000, + reconnectPeriod: 5000, + keepalive: 60, + ...this.config.options + }; + + this.client = mqtt.connect(this.config.brokerUrl, options); + + this.client.on('connect', () => { + console.log('Connected to MQTT broker'); + this.connected = true; + this.reconnectAttempts = 0; + + // Re-subscribe nach Reconnect + this.resubscribe(); + resolve(); + }); + + this.client.on('message', (topic, payload, packet) => { + const message = payload.toString(); + this.handleMessage(topic, message, packet); + }); + + this.client.on('error', (error) => { + console.error('MQTT Error:', error); + if (!this.connected) { + reject(error); + } + }); + + this.client.on('close', () => { + console.log('MQTT connection closed'); + this.connected = false; + }); + + this.client.on('reconnect', () => { + this.reconnectAttempts++; + console.log(`Reconnecting... Attempt ${this.reconnectAttempts}`); + + if (this.reconnectAttempts >= this.maxReconnectAttempts) { + this.client?.end(); + console.error('Max reconnection attempts reached'); + } + }); + + this.client.on('offline', () => { + console.log('MQTT client offline'); + }); + }); + } + + private handleMessage(topic: string, message: string, packet: any) { + // Exakte Matches + const handlers = this.subscriptions.get(topic); + if (handlers) { + handlers.forEach(handler => handler(topic, message, packet)); + } + + // Wildcard Matches + for (const [pattern, patternHandlers] of this.subscriptions) { + if (this.matchTopic(pattern, topic)) { + patternHandlers.forEach(handler => handler(topic, message, packet)); + } + } + } + + private matchTopic(pattern: string, topic: string): boolean { + if (pattern === topic) return false; // Already handled above + + const patternParts = pattern.split('/'); + const topicParts = topic.split('/'); + + for (let i = 0; i < patternParts.length; i++) { + if (patternParts[i] === '#') { + return true; // Multi-level wildcard matches rest + } + if (patternParts[i] === '+') { + continue; // Single-level wildcard matches any single level + } + if (patternParts[i] !== topicParts[i]) { + return false; + } + } + + return patternParts.length === topicParts.length; + } + + private resubscribe() { + for (const [topic] of this.subscriptions) { + this.client?.subscribe(topic, { qos: 1 }); + } + } + + // Publish + async publish( + topic: string, + message: string | object, + options: PublishOptions = {} + ): Promise { + return new Promise((resolve, reject) => { + if (!this.client || !this.connected) { + reject(new Error('Not connected to broker')); + return; + } + + const payload = typeof message === 'object' + ? JSON.stringify(message) + : message; + + this.client.publish( + topic, + payload, + { + qos: options.qos || 1, + retain: options.retain || false + }, + (error) => { + if (error) reject(error); + else resolve(); + } + ); + }); + } + + // Subscribe + subscribe( + topic: string, + handler: MessageHandler, + qos: QoS = 1 + ): () => void { + if (!this.subscriptions.has(topic)) { + this.subscriptions.set(topic, new Set()); + this.client?.subscribe(topic, { qos }); + } + + this.subscriptions.get(topic)!.add(handler); + + // Unsubscribe function + return () => { + const handlers = this.subscriptions.get(topic); + if (handlers) { + handlers.delete(handler); + if (handlers.size === 0) { + this.subscriptions.delete(topic); + this.client?.unsubscribe(topic); + } + } + }; + } + + // Disconnect + disconnect(): Promise { + return new Promise((resolve) => { + this.client?.end(false, {}, () => { + this.connected = false; + resolve(); + }); + }); + } + + isConnected(): boolean { + return this.connected; + } +} + +export { IoTMQTTClient }; +``` + +--- + +## Sensor Data Publishing + +```typescript +// devices/temperature-sensor.ts +import { IoTMQTTClient } from '../lib/mqtt-client'; + +interface SensorReading { + device_id: string; + timestamp: string; + temperature: number; + humidity: number; + battery: number; +} + +class TemperatureSensor { + private client: IoTMQTTClient; + private deviceId: string; + private baseTopic: string; + private intervalId?: NodeJS.Timeout; + + constructor( + brokerUrl: string, + deviceId: string, + options?: { username?: string; password?: string } + ) { + this.deviceId = deviceId; + this.baseTopic = `sensors/${deviceId}`; + + this.client = new IoTMQTTClient({ + brokerUrl, + options: { + username: options?.username, + password: options?.password, + will: { + topic: `${this.baseTopic}/status`, + payload: Buffer.from('offline'), + qos: 1, + retain: true + } + } + }); + } + + async start(intervalMs: number = 60000): Promise { + await this.client.connect(); + + // Online Status + await this.client.publish( + `${this.baseTopic}/status`, + 'online', + { retain: true } + ); + + // Device Info + await this.client.publish( + `${this.baseTopic}/info`, + { + device_id: this.deviceId, + type: 'temperature_humidity', + firmware: '1.0.0', + started_at: new Date().toISOString() + }, + { retain: true } + ); + + // Periodisch Daten senden + this.intervalId = setInterval(() => this.publishReading(), intervalMs); + + // Initial Reading + await this.publishReading(); + + console.log(`Sensor ${this.deviceId} started`); + } + + private async publishReading(): Promise { + const reading: SensorReading = { + device_id: this.deviceId, + timestamp: new Date().toISOString(), + temperature: this.readTemperature(), + humidity: this.readHumidity(), + battery: this.readBattery() + }; + + // JSON Format + await this.client.publish( + `${this.baseTopic}/data`, + reading, + { qos: 1 } + ); + + // Home Assistant Discovery Format + await this.client.publish( + `homeassistant/sensor/${this.deviceId}/temperature/state`, + reading.temperature.toString() + ); + + await this.client.publish( + `homeassistant/sensor/${this.deviceId}/humidity/state`, + reading.humidity.toString() + ); + + console.log(`Published: ${JSON.stringify(reading)}`); + } + + // Simulierte Sensor-Werte (in Produktion: echte Hardware-Reads) + private readTemperature(): number { + return Math.round((20 + Math.random() * 5) * 10) / 10; + } + + private readHumidity(): number { + return Math.round((40 + Math.random() * 20) * 10) / 10; + } + + private readBattery(): number { + return Math.round((80 + Math.random() * 20) * 10) / 10; + } + + async stop(): Promise { + if (this.intervalId) { + clearInterval(this.intervalId); + } + + await this.client.publish( + `${this.baseTopic}/status`, + 'offline', + { retain: true } + ); + + await this.client.disconnect(); + console.log(`Sensor ${this.deviceId} stopped`); + } +} + +// Verwendung +const sensor = new TemperatureSensor( + 'mqtt://localhost:1883', + 'living-room-01', + { + username: process.env.MQTT_USER, + password: process.env.MQTT_PASS + } +); + +await sensor.start(30000); // Alle 30 Sekunden + +// Graceful Shutdown +process.on('SIGTERM', async () => { + await sensor.stop(); + process.exit(0); +}); +``` + +--- + +## Data Subscriber Service + +```typescript +// services/mqtt-data-collector.ts +import { IoTMQTTClient } from '../lib/mqtt-client'; +import { InfluxDBClient } from '../lib/influxdb'; + +interface SensorData { + device_id: string; + timestamp: string; + [key: string]: any; +} + +class MQTTDataCollector { + private mqtt: IoTMQTTClient; + private influx: InfluxDBClient; + private subscriptions: Array<() => void> = []; + + constructor( + mqttUrl: string, + influxConfig: { url: string; token: string; org: string; bucket: string } + ) { + this.mqtt = new IoTMQTTClient({ + brokerUrl: mqttUrl, + options: { + clientId: 'data-collector', + username: process.env.MQTT_USER, + password: process.env.MQTT_PASS + } + }); + + this.influx = new InfluxDBClient(influxConfig); + } + + async start(): Promise { + await this.mqtt.connect(); + + // Subscribe to all sensor data + const unsubSensors = this.mqtt.subscribe( + 'sensors/+/data', + async (topic, message) => { + try { + const data = JSON.parse(message) as SensorData; + await this.processSensorData(topic, data); + } catch (error) { + console.error('Failed to process sensor data:', error); + } + } + ); + this.subscriptions.push(unsubSensors); + + // Subscribe to device status + const unsubStatus = this.mqtt.subscribe( + 'sensors/+/status', + (topic, message) => { + const deviceId = topic.split('/')[1]; + console.log(`Device ${deviceId} is ${message}`); + this.updateDeviceStatus(deviceId, message); + } + ); + this.subscriptions.push(unsubStatus); + + // Subscribe to commands + const unsubCommands = this.mqtt.subscribe( + 'commands/#', + this.handleCommand.bind(this) + ); + this.subscriptions.push(unsubCommands); + + console.log('Data collector started'); + } + + private async processSensorData(topic: string, data: SensorData): Promise { + const deviceId = topic.split('/')[1]; + + // In InfluxDB speichern + await this.influx.writePoint({ + measurement: 'sensor_readings', + tags: { + device_id: deviceId, + location: this.getDeviceLocation(deviceId) + }, + fields: { + temperature: data.temperature, + humidity: data.humidity, + battery: data.battery + }, + timestamp: new Date(data.timestamp) + }); + + // Alerts prüfen + await this.checkAlerts(deviceId, data); + } + + private async checkAlerts(deviceId: string, data: SensorData): Promise { + // Temperatur-Alert + if (data.temperature > 30) { + await this.mqtt.publish('alerts/temperature/high', { + device_id: deviceId, + temperature: data.temperature, + timestamp: data.timestamp, + message: `High temperature alert: ${data.temperature}°C` + }); + } + + // Batterie-Alert + if (data.battery < 20) { + await this.mqtt.publish('alerts/battery/low', { + device_id: deviceId, + battery: data.battery, + timestamp: data.timestamp, + message: `Low battery alert: ${data.battery}%` + }); + } + } + + private handleCommand(topic: string, message: string): void { + const parts = topic.split('/'); + const target = parts[1]; + const action = parts[2]; + + console.log(`Command: ${action} for ${target}`); + + // Command weiterleiten an Gerät + this.mqtt.publish(`devices/${target}/command`, { + action, + payload: JSON.parse(message), + timestamp: new Date().toISOString() + }); + } + + private getDeviceLocation(deviceId: string): string { + const locationMap: Record = { + 'living-room-01': 'living_room', + 'bedroom-01': 'bedroom', + 'kitchen-01': 'kitchen' + }; + return locationMap[deviceId] || 'unknown'; + } + + private updateDeviceStatus(deviceId: string, status: string): void { + // Status in DB speichern + console.log(`Device ${deviceId} status: ${status}`); + } + + async stop(): Promise { + this.subscriptions.forEach(unsub => unsub()); + await this.mqtt.disconnect(); + } +} +``` + +--- + +## WebSocket Bridge (Browser) + +```typescript +// lib/mqtt-websocket.ts +import mqtt, { MqttClient } from 'mqtt'; + +class MQTTWebSocketClient { + private client: MqttClient | null = null; + + async connect(wsUrl: string, options?: { + username?: string; + password?: string; + }): Promise { + return new Promise((resolve, reject) => { + this.client = mqtt.connect(wsUrl, { + protocol: 'wss', + clientId: `web_${Math.random().toString(16).slice(2, 8)}`, + ...options + }); + + this.client.on('connect', () => { + console.log('WebSocket MQTT connected'); + resolve(); + }); + + this.client.on('error', reject); + }); + } + + subscribe(topic: string, callback: (topic: string, message: string) => void): () => void { + this.client?.subscribe(topic); + + const handler = (t: string, payload: Buffer) => { + if (this.topicMatches(topic, t)) { + callback(t, payload.toString()); + } + }; + + this.client?.on('message', handler); + + return () => { + this.client?.unsubscribe(topic); + this.client?.removeListener('message', handler); + }; + } + + publish(topic: string, message: any): void { + const payload = typeof message === 'object' + ? JSON.stringify(message) + : String(message); + this.client?.publish(topic, payload); + } + + private topicMatches(pattern: string, topic: string): boolean { + const patternParts = pattern.split('/'); + const topicParts = topic.split('/'); + + for (let i = 0; i < patternParts.length; i++) { + if (patternParts[i] === '#') return true; + if (patternParts[i] === '+') continue; + if (patternParts[i] !== topicParts[i]) return false; + } + + return patternParts.length === topicParts.length; + } +} + +// React Hook +import { useState, useEffect, useCallback } from 'react'; + +export function useMQTT(wsUrl: string) { + const [client] = useState(() => new MQTTWebSocketClient()); + const [connected, setConnected] = useState(false); + + useEffect(() => { + client.connect(wsUrl).then(() => setConnected(true)); + }, [wsUrl]); + + const subscribe = useCallback(( + topic: string, + callback: (topic: string, message: string) => void + ) => { + return client.subscribe(topic, callback); + }, [client]); + + const publish = useCallback((topic: string, message: any) => { + client.publish(topic, message); + }, [client]); + + return { connected, subscribe, publish }; +} + +// Verwendung in React Component +function SensorDashboard() { + const { connected, subscribe } = useMQTT('wss://mqtt.example.com:9001'); + const [temperature, setTemperature] = useState(null); + + useEffect(() => { + if (!connected) return; + + const unsub = subscribe('sensors/+/data', (topic, message) => { + const data = JSON.parse(message); + setTemperature(data.temperature); + }); + + return unsub; + }, [connected, subscribe]); + + return ( +
+ {connected ? ( +

Temperature: {temperature ?? 'Loading...'}°C

+ ) : ( +

Connecting...

+ )} +
+ ); +} +``` + +--- + +## Secure MQTT with TLS + +```typescript +// lib/secure-mqtt.ts +import mqtt from 'mqtt'; +import fs from 'fs'; +import path from 'path'; + +const secureClient = mqtt.connect('mqtts://mqtt.example.com:8883', { + clientId: `secure_${Date.now()}`, + username: process.env.MQTT_USER, + password: process.env.MQTT_PASS, + + // TLS Optionen + ca: fs.readFileSync(path.join(__dirname, 'certs/ca.crt')), + cert: fs.readFileSync(path.join(__dirname, 'certs/client.crt')), + key: fs.readFileSync(path.join(__dirname, 'certs/client.key')), + + // Bei selbstsignierten Zertifikaten + rejectUnauthorized: process.env.NODE_ENV === 'production', + + // Protokoll-Version + protocolVersion: 5, // MQTT 5.0 + + // Clean Start (MQTT 5.0) + clean: true, + + // Session Expiry (MQTT 5.0) + properties: { + sessionExpiryInterval: 3600 + } +}); + +secureClient.on('connect', (connack) => { + console.log('Secure connection established'); + console.log('Session present:', connack.sessionPresent); +}); +``` + +--- + +## QoS Level Comparison + +| QoS | Name | Garantie | Use Case | +|-----|------|----------|----------| +| 0 | At most once | Keine | Telemetrie, unwichtige Daten | +| 1 | At least once | Zustellung garantiert | Sensor-Daten, Commands | +| 2 | Exactly once | Keine Duplikate | Zahlungen, kritische Daten | + +--- + +## Fazit + +MQTT für IoT bietet: + +1. **Leichtgewichtig**: Minimaler Overhead +2. **Publish/Subscribe**: Entkoppelte Kommunikation +3. **QoS Levels**: Flexible Zustellgarantien +4. **Wildcards**: Flexible Topic-Struktur + +Standard für IoT-Kommunikation. + +--- + +## Bildprompts + +1. "IoT devices connected through message broker, publish subscribe pattern" +2. "Sensor data flowing to central hub, MQTT network visualization" +3. "Quality of Service levels diagram, message delivery guarantee" + +--- + +## Quellen + +- [MQTT.js GitHub](https://github.com/mqttjs/MQTT.js/) +- [HiveMQ MQTT Guide](https://www.hivemq.com/blog/ultimate-guide-on-how-to-use-mqtt-with-node-js/) +- [EMQX MQTT Tutorial](https://www.emqx.com/en/blog/how-to-use-mqtt-in-nodejs) +- [Mosquitto Documentation](https://mosquitto.org/documentation/) diff --git a/blog-posts/63-esp32-development.md b/blog-posts/63-esp32-development.md new file mode 100644 index 0000000..4a2b173 --- /dev/null +++ b/blog-posts/63-esp32-development.md @@ -0,0 +1,734 @@ +# ESP32 Development mit PlatformIO + +**Meta-Description:** ESP32 Entwicklung mit PlatformIO. WiFi, Sensoren, MQTT Integration und OTA Updates für IoT-Projekte. + +**Keywords:** ESP32, PlatformIO, Arduino, IoT, WiFi, MQTT, Sensors, Embedded Development + +--- + +## Einführung + +Der **ESP32** ist der vielseitigste Microcontroller für IoT-Projekte. Mit WiFi, Bluetooth, dualen Cores und zahlreichen GPIOs – kombiniert mit **PlatformIO** als moderne Entwicklungsumgebung. + +--- + +## ESP32 Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ESP32 ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ CPU: Xtensa dual-core 32-bit LX6 @ 240MHz │ +│ │ +│ Memory: │ +│ ├── 520 KB SRAM │ +│ ├── 4 MB Flash (external) │ +│ └── 448 KB ROM │ +│ │ +│ Connectivity: │ +│ ├── WiFi 802.11 b/g/n (2.4 GHz) │ +│ ├── Bluetooth 4.2 + BLE │ +│ └── Multiple SPI, I2C, UART, I2S │ +│ │ +│ GPIO: │ +│ ├── 34 GPIO Pins │ +│ ├── 18 ADC Channels (12-bit) │ +│ ├── 2 DAC Channels (8-bit) │ +│ ├── 16 PWM Channels │ +│ └── 10 Touch Sensors │ +│ │ +│ Power: │ +│ ├── Operating Voltage: 3.3V │ +│ ├── Deep Sleep: ~10µA │ +│ └── Active WiFi: ~160mA │ +│ │ +│ Peripherals: RTC, Timers, Watchdog, Hall Sensor │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## PlatformIO Setup + +```bash +# VS Code Extension installieren +# Extensions → PlatformIO IDE + +# Oder CLI Installation +pip install platformio + +# Neues Projekt erstellen +pio project init --board esp32dev --ide vscode +``` + +```ini +; platformio.ini +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 115200 +upload_speed = 921600 + +; Build Flags +build_flags = + -DCORE_DEBUG_LEVEL=3 + -DCONFIG_ARDUHAL_LOG_COLORS=1 + +; Libraries +lib_deps = + bblanchon/ArduinoJson@^7.0.0 + knolleary/PubSubClient@^2.8 + adafruit/DHT sensor library@^1.4.6 + adafruit/Adafruit Unified Sensor@^1.1.14 + +; OTA Updates +upload_protocol = espota +upload_port = 192.168.1.100 + +; Filesystem +board_build.filesystem = littlefs + +; Partitions +board_build.partitions = min_spiffs.csv +``` + +--- + +## WiFi Connection + +```cpp +// src/main.cpp +#include +#include +#include + +// Credentials (besser: WiFiManager oder NVS) +const char* ssid = "YourNetwork"; +const char* password = "YourPassword"; + +// Status LED +const int LED_PIN = 2; + +void setupWiFi() { + Serial.println("Connecting to WiFi..."); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + + int attempts = 0; + while (WiFi.status() != WL_CONNECTED && attempts < 30) { + delay(500); + Serial.print("."); + digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Blink + attempts++; + } + + if (WiFi.status() == WL_CONNECTED) { + Serial.println("\nWiFi connected!"); + Serial.print("IP: "); + Serial.println(WiFi.localIP()); + Serial.print("RSSI: "); + Serial.println(WiFi.RSSI()); + digitalWrite(LED_PIN, HIGH); + } else { + Serial.println("\nWiFi connection failed!"); + // Fallback: AP Mode für Konfiguration + startConfigPortal(); + } +} + +void startConfigPortal() { + WiFiManager wifiManager; + + // Reset settings for testing + // wifiManager.resetSettings(); + + // Custom Parameters + WiFiManagerParameter mqtt_server("mqtt", "MQTT Server", "mqtt.local", 40); + wifiManager.addParameter(&mqtt_server); + + // Auto-connect oder Config Portal + if (!wifiManager.autoConnect("ESP32-Setup", "password123")) { + Serial.println("Failed to connect, restarting..."); + delay(3000); + ESP.restart(); + } + + Serial.println("Connected via WiFiManager!"); + Serial.println(mqtt_server.getValue()); +} + +void setup() { + Serial.begin(115200); + pinMode(LED_PIN, OUTPUT); + + setupWiFi(); +} + +void loop() { + // WiFi Reconnect + if (WiFi.status() != WL_CONNECTED) { + Serial.println("WiFi lost, reconnecting..."); + setupWiFi(); + } + + delay(1000); +} +``` + +--- + +## Sensor Reading (DHT22) + +```cpp +// src/sensors.cpp +#include +#include + +#define DHT_PIN 4 +#define DHT_TYPE DHT22 + +DHT dht(DHT_PIN, DHT_TYPE); + +struct SensorData { + float temperature; + float humidity; + float heatIndex; + bool valid; +}; + +SensorData readDHT22() { + SensorData data = {0, 0, 0, false}; + + // Mehrere Versuche für zuverlässige Messung + for (int i = 0; i < 3; i++) { + data.temperature = dht.readTemperature(); + data.humidity = dht.readHumidity(); + + if (!isnan(data.temperature) && !isnan(data.humidity)) { + data.heatIndex = dht.computeHeatIndex( + data.temperature, + data.humidity, + false // Celsius + ); + data.valid = true; + break; + } + + delay(2000); // DHT braucht Zeit zwischen Messungen + } + + return data; +} + +String sensorDataToJson(const SensorData& data) { + JsonDocument doc; + + doc["device_id"] = WiFi.macAddress(); + doc["timestamp"] = millis(); + doc["temperature"] = round(data.temperature * 10) / 10.0; + doc["humidity"] = round(data.humidity * 10) / 10.0; + doc["heat_index"] = round(data.heatIndex * 10) / 10.0; + doc["valid"] = data.valid; + doc["rssi"] = WiFi.RSSI(); + + String output; + serializeJson(doc, output); + return output; +} + +void setup() { + Serial.begin(115200); + dht.begin(); + + // Warm-up Zeit + delay(2000); +} + +void loop() { + SensorData data = readDHT22(); + + if (data.valid) { + String json = sensorDataToJson(data); + Serial.println(json); + } else { + Serial.println("Sensor read failed!"); + } + + delay(10000); // Alle 10 Sekunden +} +``` + +--- + +## MQTT Integration + +```cpp +// src/mqtt_client.cpp +#include +#include +#include + +const char* mqtt_server = "mqtt.example.com"; +const int mqtt_port = 1883; +const char* mqtt_user = "esp32"; +const char* mqtt_pass = "secret"; + +WiFiClient espClient; +PubSubClient mqtt(espClient); + +String deviceId; +String baseTopic; + +void mqttCallback(char* topic, byte* payload, unsigned int length) { + Serial.printf("Message [%s]: ", topic); + + // Payload zu String + String message; + for (unsigned int i = 0; i < length; i++) { + message += (char)payload[i]; + } + Serial.println(message); + + // JSON parsen + JsonDocument doc; + if (deserializeJson(doc, message) == DeserializationError::Ok) { + handleCommand(doc); + } +} + +void handleCommand(const JsonDocument& doc) { + const char* action = doc["action"]; + + if (strcmp(action, "restart") == 0) { + Serial.println("Restart command received"); + ESP.restart(); + } + else if (strcmp(action, "led") == 0) { + bool state = doc["state"]; + digitalWrite(LED_BUILTIN, state); + Serial.printf("LED: %s\n", state ? "ON" : "OFF"); + } + else if (strcmp(action, "config") == 0) { + // Konfiguration aktualisieren + int interval = doc["interval"] | 10000; + Serial.printf("New interval: %d\n", interval); + } +} + +void mqttReconnect() { + while (!mqtt.connected()) { + Serial.println("Connecting to MQTT..."); + + // Last Will Testament + String willTopic = baseTopic + "/status"; + + if (mqtt.connect( + deviceId.c_str(), + mqtt_user, + mqtt_pass, + willTopic.c_str(), + 1, // QoS + true, // Retain + "offline" + )) { + Serial.println("MQTT connected!"); + + // Online Status + mqtt.publish(willTopic.c_str(), "online", true); + + // Subscribe to commands + String cmdTopic = baseTopic + "/command"; + mqtt.subscribe(cmdTopic.c_str()); + + // Broadcast Commands + mqtt.subscribe("devices/all/command"); + + } else { + Serial.printf("MQTT failed, rc=%d\n", mqtt.state()); + delay(5000); + } + } +} + +void publishSensorData(const SensorData& data) { + if (!mqtt.connected()) { + mqttReconnect(); + } + + JsonDocument doc; + doc["device_id"] = deviceId; + doc["temperature"] = data.temperature; + doc["humidity"] = data.humidity; + doc["heat_index"] = data.heatIndex; + doc["rssi"] = WiFi.RSSI(); + doc["uptime"] = millis() / 1000; + + String output; + serializeJson(doc, output); + + String topic = baseTopic + "/data"; + mqtt.publish(topic.c_str(), output.c_str()); + + Serial.println("Data published"); +} + +void setupMQTT() { + deviceId = "esp32-" + WiFi.macAddress(); + deviceId.replace(":", ""); + baseTopic = "sensors/" + deviceId; + + mqtt.setServer(mqtt_server, mqtt_port); + mqtt.setCallback(mqttCallback); + mqtt.setBufferSize(512); // Größerer Buffer für JSON + + mqttReconnect(); +} + +void loopMQTT() { + if (!mqtt.connected()) { + mqttReconnect(); + } + mqtt.loop(); +} +``` + +--- + +## OTA Updates + +```cpp +// src/ota.cpp +#include +#include + +void setupOTA() { + // Hostname + ArduinoOTA.setHostname("esp32-sensor"); + + // Password + ArduinoOTA.setPassword("ota_secret"); + + // Port (default: 3232) + ArduinoOTA.setPort(3232); + + ArduinoOTA.onStart([]() { + String type = ArduinoOTA.getCommand() == U_FLASH + ? "sketch" : "filesystem"; + Serial.println("OTA Start: " + type); + + // MQTT disconnect + mqtt.disconnect(); + }); + + ArduinoOTA.onEnd([]() { + Serial.println("\nOTA End"); + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + switch (error) { + case OTA_AUTH_ERROR: Serial.println("Auth Failed"); break; + case OTA_BEGIN_ERROR: Serial.println("Begin Failed"); break; + case OTA_CONNECT_ERROR: Serial.println("Connect Failed"); break; + case OTA_RECEIVE_ERROR: Serial.println("Receive Failed"); break; + case OTA_END_ERROR: Serial.println("End Failed"); break; + } + }); + + ArduinoOTA.begin(); + Serial.println("OTA Ready"); +} + +void loopOTA() { + ArduinoOTA.handle(); +} +``` + +--- + +## Deep Sleep für Batterie-Betrieb + +```cpp +// src/deep_sleep.cpp +#include + +#define uS_TO_S_FACTOR 1000000ULL +#define SLEEP_DURATION_S 300 // 5 Minuten + +RTC_DATA_ATTR int bootCount = 0; + +void printWakeupReason() { + esp_sleep_wakeup_cause_t reason = esp_sleep_get_wakeup_cause(); + + switch (reason) { + case ESP_SLEEP_WAKEUP_EXT0: + Serial.println("Wakeup: External GPIO"); + break; + case ESP_SLEEP_WAKEUP_TIMER: + Serial.println("Wakeup: Timer"); + break; + case ESP_SLEEP_WAKEUP_TOUCHPAD: + Serial.println("Wakeup: Touchpad"); + break; + default: + Serial.printf("Wakeup: Other (%d)\n", reason); + break; + } +} + +void goToSleep() { + Serial.println("Going to deep sleep..."); + Serial.flush(); + + // Timer Wakeup + esp_sleep_enable_timer_wakeup(SLEEP_DURATION_S * uS_TO_S_FACTOR); + + // GPIO Wakeup (z.B. Button) + esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, LOW); + + // WiFi und Bluetooth ausschalten + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); + btStop(); + + // Deep Sleep starten + esp_deep_sleep_start(); +} + +void setup() { + Serial.begin(115200); + + bootCount++; + Serial.printf("Boot count: %d\n", bootCount); + printWakeupReason(); + + // Sensor lesen und senden + setupWiFi(); + setupMQTT(); + + SensorData data = readDHT22(); + if (data.valid) { + publishSensorData(data); + } + + // Kurz warten für MQTT + delay(1000); + mqtt.loop(); + + // Zurück in Deep Sleep + goToSleep(); +} + +void loop() { + // Wird bei Deep Sleep nicht erreicht +} +``` + +--- + +## Web Server für Konfiguration + +```cpp +// src/webserver.cpp +#include +#include +#include + +AsyncWebServer server(80); + +void setupWebServer() { + // Static Files aus LittleFS + if (!LittleFS.begin(true)) { + Serial.println("LittleFS mount failed"); + return; + } + + server.serveStatic("/", LittleFS, "/www/") + .setDefaultFile("index.html"); + + // API: Sensor Status + server.on("/api/status", HTTP_GET, [](AsyncWebServerRequest *request) { + JsonDocument doc; + + doc["device_id"] = deviceId; + doc["ip"] = WiFi.localIP().toString(); + doc["rssi"] = WiFi.RSSI(); + doc["uptime"] = millis() / 1000; + doc["free_heap"] = ESP.getFreeHeap(); + doc["temperature"] = lastReading.temperature; + doc["humidity"] = lastReading.humidity; + + String output; + serializeJson(doc, output); + + request->send(200, "application/json", output); + }); + + // API: Konfiguration + server.on("/api/config", HTTP_GET, [](AsyncWebServerRequest *request) { + JsonDocument doc; + + doc["mqtt_server"] = mqtt_server; + doc["mqtt_port"] = mqtt_port; + doc["interval"] = readInterval; + + String output; + serializeJson(doc, output); + + request->send(200, "application/json", output); + }); + + // API: Konfiguration speichern + AsyncCallbackJsonWebHandler* configHandler = new AsyncCallbackJsonWebHandler( + "/api/config", + [](AsyncWebServerRequest *request, JsonVariant &json) { + JsonObject obj = json.as(); + + // Konfiguration in NVS speichern + saveConfig(obj); + + request->send(200, "application/json", "{\"success\": true}"); + } + ); + server.addHandler(configHandler); + + // API: Restart + server.on("/api/restart", HTTP_POST, [](AsyncWebServerRequest *request) { + request->send(200, "application/json", "{\"restarting\": true}"); + delay(100); + ESP.restart(); + }); + + // 404 Handler + server.onNotFound([](AsyncWebServerRequest *request) { + request->send(404, "text/plain", "Not Found"); + }); + + server.begin(); + Serial.println("Web server started"); +} +``` + +--- + +## GPIO & PWM Control + +```cpp +// src/gpio_control.cpp + +// LED PWM +const int LED_PIN = 2; +const int PWM_CHANNEL = 0; +const int PWM_FREQ = 5000; +const int PWM_RESOLUTION = 8; + +void setupPWM() { + ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION); + ledcAttachPin(LED_PIN, PWM_CHANNEL); +} + +void setLEDBrightness(int brightness) { + // 0-255 + ledcWrite(PWM_CHANNEL, brightness); +} + +void fadeLED() { + for (int duty = 0; duty <= 255; duty++) { + ledcWrite(PWM_CHANNEL, duty); + delay(10); + } + for (int duty = 255; duty >= 0; duty--) { + ledcWrite(PWM_CHANNEL, duty); + delay(10); + } +} + +// Analog Input +const int ANALOG_PIN = 34; + +int readAnalog() { + // 12-bit ADC: 0-4095 + int raw = analogRead(ANALOG_PIN); + + // Zu Spannung konvertieren (3.3V Ref) + float voltage = (raw / 4095.0) * 3.3; + + return raw; +} + +// Touch Sensor +const int TOUCH_PIN = T0; // GPIO4 + +void setupTouch() { + touchAttachInterrupt(TOUCH_PIN, touchCallback, 40); +} + +void touchCallback() { + Serial.println("Touch detected!"); +} +``` + +--- + +## Projektstruktur + +``` +esp32-sensor/ +├── platformio.ini +├── src/ +│ ├── main.cpp +│ ├── wifi.cpp +│ ├── mqtt.cpp +│ ├── sensors.cpp +│ ├── ota.cpp +│ └── webserver.cpp +├── include/ +│ ├── config.h +│ └── secrets.h +├── lib/ +│ └── CustomLibrary/ +├── data/ +│ └── www/ +│ ├── index.html +│ └── style.css +└── test/ + └── test_main.cpp +``` + +--- + +## Fazit + +ESP32 mit PlatformIO bietet: + +1. **Dual-Core Power**: 240MHz für anspruchsvolle Aufgaben +2. **Connectivity**: WiFi + Bluetooth integriert +3. **Low Power**: Deep Sleep für Batterie-Betrieb +4. **Modern Tooling**: PlatformIO + VS Code + +Ideale Plattform für IoT-Projekte. + +--- + +## Bildprompts + +1. "ESP32 microcontroller with connected sensors, breadboard prototype" +2. "PlatformIO IDE with code and serial monitor, embedded development" +3. "IoT sensor node with battery and antenna, compact device design" + +--- + +## Quellen + +- [PlatformIO ESP32 Documentation](https://docs.platformio.org/en/latest/platforms/espressif32.html) +- [Random Nerd Tutorials ESP32](https://randomnerdtutorials.com/vs-code-platformio-ide-esp32-esp8266-arduino/) +- [ESP32 Arduino Core](https://docs.espressif.com/projects/arduino-esp32/en/latest/) +- [Sunfounder ESP32 Guide](https://www.sunfounder.com/blogs/news/how-to-set-up-esp32-on-platformio-with-vs-code-complete-step-by-step-guide) diff --git a/blog-posts/64-raspberry-pi-iot.md b/blog-posts/64-raspberry-pi-iot.md new file mode 100644 index 0000000..13a9f8e --- /dev/null +++ b/blog-posts/64-raspberry-pi-iot.md @@ -0,0 +1,766 @@ +# Raspberry Pi IoT Projekte mit Node.js + +**Meta-Description:** IoT-Projekte mit Raspberry Pi und Node.js. GPIO-Steuerung, Sensor-Integration, MQTT und Web Dashboards. + +**Keywords:** Raspberry Pi, Node.js, GPIO, IoT, Sensors, Home Automation, MQTT, Embedded Linux + +--- + +## Einführung + +Der **Raspberry Pi** ist der perfekte Single-Board Computer für IoT-Projekte. Mit **Node.js** kombiniert man die Hardware-Nähe mit dem mächtigen npm-Ökosystem für Web-basierte IoT-Anwendungen. + +--- + +## Raspberry Pi Setup + +``` +┌─────────────────────────────────────────────────────────────┐ +│ RASPBERRY PI 5 SPECIFICATIONS │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ CPU: Broadcom BCM2712 quad-core Arm Cortex-A76 @ 2.4GHz │ +│ RAM: 4GB / 8GB LPDDR4X-4267 │ +│ │ +│ Connectivity: │ +│ ├── Gigabit Ethernet │ +│ ├── Dual-band WiFi 802.11ac │ +│ ├── Bluetooth 5.0 / BLE │ +│ ├── 2× USB 3.0 + 2× USB 2.0 │ +│ └── PCIe 2.0 x1 │ +│ │ +│ GPIO: │ +│ ├── 40-pin GPIO Header │ +│ ├── 26 GPIO Pins │ +│ ├── I2C, SPI, UART │ +│ └── PWM (Hardware) │ +│ │ +│ Storage: microSD / NVMe (via HAT) │ +│ Power: 5V/5A USB-C │ +│ │ +│ GPIO Pinout (Subset): │ +│ ┌────┬────┬────────────────────────┐ │ +│ │ 1 │ 2 │ 3.3V | 5V │ │ +│ │ 3 │ 4 │ GPIO2 | 5V │ │ +│ │ 5 │ 6 │ GPIO3 | GND │ │ +│ │ 7 │ 8 │ GPIO4 | GPIO14(TX) │ │ +│ │ 9 │ 10 │ GND | GPIO15(RX) │ │ +│ └────┴────┴────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Node.js Installation + +```bash +# Node.js via NodeSource (empfohlen) +curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +sudo apt-get install -y nodejs + +# Alternativ: NVM (Node Version Manager) +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash +source ~/.bashrc +nvm install 22 +nvm use 22 + +# Verify +node --version +npm --version + +# Build Tools für native Addons +sudo apt-get install -y build-essential python3 + +# GPIO Berechtigungen (ohne sudo) +sudo usermod -aG gpio $USER +sudo usermod -aG i2c $USER +sudo usermod -aG spi $USER +``` + +--- + +## GPIO Control mit Node.js + +```typescript +// lib/gpio.ts +import { Gpio } from 'onoff'; + +// LED Control +const LED_PIN = 17; +const led = new Gpio(LED_PIN, 'out'); + +// LED einschalten +led.writeSync(1); + +// LED ausschalten +led.writeSync(0); + +// Asynchrone Steuerung +async function blinkLED(times: number, intervalMs: number) { + for (let i = 0; i < times; i++) { + await led.write(1); + await sleep(intervalMs); + await led.write(0); + await sleep(intervalMs); + } +} + +// Button mit Interrupt +const BUTTON_PIN = 18; +const button = new Gpio(BUTTON_PIN, 'in', 'both', { debounceTimeout: 50 }); + +button.watch((err, value) => { + if (err) { + console.error('Button error:', err); + return; + } + console.log(`Button: ${value === 1 ? 'pressed' : 'released'}`); +}); + +// Cleanup bei Programmende +process.on('SIGINT', () => { + led.unexport(); + button.unexport(); + process.exit(); +}); + +// Helper +function sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} +``` + +```typescript +// Für Raspberry Pi 5: gpiod verwenden +// lib/gpio-pi5.ts +import { Chip, Line } from '@iiot2k/gpiox'; + +const chip = new Chip(4); // Pi5 verwendet Chip 4 + +// LED Setup +const ledLine = chip.getLine(17); +ledLine.requestOutput('led'); + +// LED steuern +ledLine.setValue(1); // On +ledLine.setValue(0); // Off + +// Button Setup mit Events +const buttonLine = chip.getLine(18); +buttonLine.requestInput('button'); +buttonLine.requestBothEdges(); + +buttonLine.addEventListener('value', (event) => { + console.log(`Button value: ${event.value}`); +}); +``` + +--- + +## Sensor-Integration + +```typescript +// lib/sensors/dht22.ts +import sensor from 'node-dht-sensor'; + +const DHT_TYPE = 22; // DHT22 +const DHT_PIN = 4; + +interface Reading { + temperature: number; + humidity: number; + timestamp: Date; +} + +export async function readDHT22(): Promise { + return new Promise((resolve, reject) => { + sensor.read(DHT_TYPE, DHT_PIN, (err, temperature, humidity) => { + if (err) { + reject(err); + return; + } + + resolve({ + temperature: Math.round(temperature * 10) / 10, + humidity: Math.round(humidity * 10) / 10, + timestamp: new Date() + }); + }); + }); +} + +// Retry Logic für zuverlässigere Messungen +export async function readDHT22WithRetry(maxRetries = 3): Promise { + let lastError: Error | null = null; + + for (let i = 0; i < maxRetries; i++) { + try { + const reading = await readDHT22(); + + // Plausibilitätsprüfung + if (reading.temperature > -40 && reading.temperature < 80 && + reading.humidity >= 0 && reading.humidity <= 100) { + return reading; + } + } catch (err) { + lastError = err as Error; + await sleep(2000); // DHT braucht Zeit zwischen Messungen + } + } + + throw lastError || new Error('Failed to read DHT22'); +} +``` + +```typescript +// lib/sensors/bme280.ts +import BME280 from 'bme280-sensor'; + +const bme280 = new BME280({ i2cBusNo: 1, i2cAddress: 0x76 }); + +interface EnvironmentData { + temperature: number; + humidity: number; + pressure: number; +} + +export async function initBME280(): Promise { + await bme280.init(); + console.log('BME280 initialized'); +} + +export async function readBME280(): Promise { + const data = await bme280.readSensorData(); + + return { + temperature: Math.round(data.temperature_C * 10) / 10, + humidity: Math.round(data.humidity * 10) / 10, + pressure: Math.round(data.pressure_hPa * 10) / 10 + }; +} +``` + +```typescript +// lib/sensors/ultrasonic.ts +import { Gpio } from 'onoff'; + +const TRIG_PIN = 23; +const ECHO_PIN = 24; + +const trigger = new Gpio(TRIG_PIN, 'out'); +const echo = new Gpio(ECHO_PIN, 'in', 'both'); + +export function measureDistance(): Promise { + return new Promise((resolve, reject) => { + let startTime: bigint; + let endTime: bigint; + let timeout: NodeJS.Timeout; + + // Timeout nach 1 Sekunde + timeout = setTimeout(() => { + reject(new Error('Measurement timeout')); + }, 1000); + + echo.watch((err, value) => { + if (err) { + clearTimeout(timeout); + reject(err); + return; + } + + if (value === 1) { + startTime = process.hrtime.bigint(); + } else { + endTime = process.hrtime.bigint(); + + clearTimeout(timeout); + + // Distanz berechnen + const duration = Number(endTime - startTime) / 1e9; // Sekunden + const distance = (duration * 34300) / 2; // cm + + resolve(Math.round(distance * 10) / 10); + } + }); + + // Trigger Puls + trigger.writeSync(1); + setTimeout(() => trigger.writeSync(0), 10); // 10µs Puls + }); +} +``` + +--- + +## MQTT Sensor Node + +```typescript +// apps/sensor-node.ts +import mqtt from 'mqtt'; +import { readDHT22WithRetry } from './lib/sensors/dht22'; +import { measureDistance } from './lib/sensors/ultrasonic'; +import os from 'os'; + +interface SensorNodeConfig { + brokerUrl: string; + deviceId: string; + readIntervalMs: number; +} + +class SensorNode { + private client: mqtt.MqttClient; + private config: SensorNodeConfig; + private baseTopic: string; + private running = false; + + constructor(config: SensorNodeConfig) { + this.config = config; + this.baseTopic = `sensors/${config.deviceId}`; + + this.client = mqtt.connect(config.brokerUrl, { + clientId: config.deviceId, + will: { + topic: `${this.baseTopic}/status`, + payload: Buffer.from('offline'), + qos: 1, + retain: true + } + }); + } + + async start(): Promise { + await this.waitForConnection(); + + // Online Status + this.client.publish( + `${this.baseTopic}/status`, + 'online', + { retain: true } + ); + + // Device Info + this.client.publish( + `${this.baseTopic}/info`, + JSON.stringify({ + device_id: this.config.deviceId, + hostname: os.hostname(), + platform: os.platform(), + arch: os.arch(), + uptime: os.uptime(), + memory: { + total: os.totalmem(), + free: os.freemem() + } + }), + { retain: true } + ); + + // Commands abonnieren + this.client.subscribe(`${this.baseTopic}/command`); + this.client.on('message', (topic, payload) => { + this.handleCommand(payload.toString()); + }); + + this.running = true; + this.sensorLoop(); + } + + private async sensorLoop(): Promise { + while (this.running) { + try { + // Alle Sensoren lesen + const [dht, distance] = await Promise.allSettled([ + readDHT22WithRetry(), + measureDistance() + ]); + + const data: Record = { + device_id: this.config.deviceId, + timestamp: new Date().toISOString() + }; + + if (dht.status === 'fulfilled') { + data.temperature = dht.value.temperature; + data.humidity = dht.value.humidity; + } + + if (distance.status === 'fulfilled') { + data.distance = distance.value; + } + + // System Metrics + data.system = { + cpu_temp: await this.getCPUTemperature(), + memory_usage: (1 - os.freemem() / os.totalmem()) * 100, + load_average: os.loadavg()[0] + }; + + // Publish + this.client.publish( + `${this.baseTopic}/data`, + JSON.stringify(data), + { qos: 1 } + ); + + console.log('Published:', data); + + } catch (error) { + console.error('Sensor read error:', error); + } + + await this.sleep(this.config.readIntervalMs); + } + } + + private handleCommand(payload: string): void { + try { + const command = JSON.parse(payload); + + switch (command.action) { + case 'restart': + console.log('Restart command received'); + process.exit(0); // Systemd wird neustarten + break; + + case 'setInterval': + this.config.readIntervalMs = command.value; + console.log(`Interval set to ${command.value}ms`); + break; + + case 'status': + this.publishStatus(); + break; + } + } catch (error) { + console.error('Command parse error:', error); + } + } + + private async getCPUTemperature(): Promise { + try { + const { readFile } = await import('fs/promises'); + const temp = await readFile('/sys/class/thermal/thermal_zone0/temp', 'utf8'); + return parseInt(temp) / 1000; + } catch { + return 0; + } + } + + private publishStatus(): void { + this.client.publish( + `${this.baseTopic}/status/detailed`, + JSON.stringify({ + running: this.running, + uptime: process.uptime(), + memory: process.memoryUsage(), + interval: this.config.readIntervalMs + }) + ); + } + + private waitForConnection(): Promise { + return new Promise((resolve, reject) => { + this.client.on('connect', () => { + console.log('Connected to MQTT broker'); + resolve(); + }); + + this.client.on('error', reject); + + setTimeout(() => reject(new Error('Connection timeout')), 30000); + }); + } + + private sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + stop(): void { + this.running = false; + this.client.publish( + `${this.baseTopic}/status`, + 'offline', + { retain: true } + ); + this.client.end(); + } +} + +// Verwendung +const node = new SensorNode({ + brokerUrl: process.env.MQTT_URL || 'mqtt://localhost:1883', + deviceId: process.env.DEVICE_ID || `rpi-${os.hostname()}`, + readIntervalMs: 30000 +}); + +node.start(); + +// Graceful Shutdown +process.on('SIGINT', () => { + console.log('Shutting down...'); + node.stop(); + process.exit(0); +}); +``` + +--- + +## Web Dashboard + +```typescript +// apps/dashboard/server.ts +import express from 'express'; +import { createServer } from 'http'; +import { Server as SocketServer } from 'socket.io'; +import mqtt from 'mqtt'; + +const app = express(); +const server = createServer(app); +const io = new SocketServer(server); + +// MQTT Client +const mqttClient = mqtt.connect(process.env.MQTT_URL || 'mqtt://localhost:1883'); + +// Sensor Data speichern +const sensorData: Map = new Map(); +const MAX_HISTORY = 100; + +mqttClient.on('connect', () => { + console.log('MQTT connected'); + mqttClient.subscribe('sensors/+/data'); + mqttClient.subscribe('sensors/+/status'); +}); + +mqttClient.on('message', (topic, payload) => { + const parts = topic.split('/'); + const deviceId = parts[1]; + const type = parts[2]; + + if (type === 'data') { + const data = JSON.parse(payload.toString()); + + // History speichern + if (!sensorData.has(deviceId)) { + sensorData.set(deviceId, []); + } + + const history = sensorData.get(deviceId)!; + history.push(data); + + if (history.length > MAX_HISTORY) { + history.shift(); + } + + // An WebSocket Clients senden + io.emit('sensorData', { deviceId, data }); + } + + if (type === 'status') { + io.emit('deviceStatus', { + deviceId, + status: payload.toString() + }); + } +}); + +// Static Files +app.use(express.static('public')); + +// API Endpoints +app.get('/api/devices', (req, res) => { + const devices = Array.from(sensorData.keys()).map(id => ({ + id, + lastReading: sensorData.get(id)?.slice(-1)[0] + })); + + res.json(devices); +}); + +app.get('/api/devices/:id/history', (req, res) => { + const history = sensorData.get(req.params.id) || []; + res.json(history); +}); + +// WebSocket Events +io.on('connection', (socket) => { + console.log('Client connected'); + + // Initial Data senden + sensorData.forEach((history, deviceId) => { + socket.emit('initialData', { + deviceId, + history: history.slice(-20) + }); + }); + + // Command an Gerät senden + socket.on('command', ({ deviceId, action, payload }) => { + mqttClient.publish( + `sensors/${deviceId}/command`, + JSON.stringify({ action, ...payload }) + ); + }); +}); + +server.listen(3000, () => { + console.log('Dashboard running on http://localhost:3000'); +}); +``` + +```html + + + + + Pi Sensor Dashboard + + + + + +

Pi Sensor Dashboard

+ +
+
+
--
+
Temperature (°C)
+
+
+
--
+
Humidity (%)
+
+
+ +
+
+ + + + +``` + +--- + +## Systemd Service + +```ini +# /etc/systemd/system/sensor-node.service +[Unit] +Description=Raspberry Pi Sensor Node +After=network.target + +[Service] +Type=simple +User=pi +WorkingDirectory=/home/pi/sensor-node +ExecStart=/usr/bin/node dist/apps/sensor-node.js +Restart=always +RestartSec=10 +Environment=NODE_ENV=production +Environment=MQTT_URL=mqtt://localhost:1883 + +[Install] +WantedBy=multi-user.target +``` + +```bash +# Service aktivieren +sudo systemctl daemon-reload +sudo systemctl enable sensor-node +sudo systemctl start sensor-node + +# Status prüfen +sudo systemctl status sensor-node +journalctl -u sensor-node -f +``` + +--- + +## Fazit + +Raspberry Pi mit Node.js bietet: + +1. **Full Linux**: Komplettes OS mit npm-Ökosystem +2. **GPIO Access**: Direkte Hardware-Steuerung +3. **Networking**: WiFi, Ethernet, Bluetooth +4. **Web Stack**: Express, Socket.io, React + +Perfekt für IoT-Gateways und Edge Computing. + +--- + +## Bildprompts + +1. "Raspberry Pi with connected sensors and wires, IoT prototype" +2. "Web dashboard showing sensor data graphs, real-time monitoring" +3. "Smart home hub with Pi and multiple sensor nodes, IoT network" + +--- + +## Quellen + +- [Node.js on Raspberry Pi Guide](https://fleetstack.io/blog/nodejs-raspberry-pi-complete-guide) +- [InfluxDB + Node-RED + Raspberry Pi](https://www.influxdata.com/blog/node-red-influxdb-raspberry-pi-iot-sensor-tutorial/) +- [onoff GPIO Library](https://www.npmjs.com/package/onoff) +- [SitePoint GPIO Tutorial](https://www.sitepoint.com/getting-started-with-the-raspberry-pi-gpio-pins-in-node-js/) diff --git a/blog-posts/65-matter-thread-protocol.md b/blog-posts/65-matter-thread-protocol.md new file mode 100644 index 0000000..7304b5b --- /dev/null +++ b/blog-posts/65-matter-thread-protocol.md @@ -0,0 +1,629 @@ +# Matter & Thread: Smart Home Standards + +**Meta-Description:** Matter und Thread Protokolle für Smart Homes. Interoperabilität, Thread Border Router und Home Assistant Integration. + +**Keywords:** Matter, Thread, Smart Home, IoT Protocol, Border Router, CSA, Home Assistant, Zigbee + +--- + +## Einführung + +**Matter** und **Thread** revolutionieren Smart Homes. Matter bietet Interoperabilität zwischen Apple, Google und Amazon. Thread liefert das energieeffiziente Mesh-Netzwerk. Zusammen die Zukunft vernetzter Häuser. + +--- + +## Matter & Thread Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ MATTER & THREAD ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Application Layer: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ MATTER │ │ +│ │ (Unified Smart Home Protocol by CSA) │ │ +│ │ - Device Types (Lights, Locks, Sensors, ...) │ │ +│ │ - Secure Commissioning │ │ +│ │ - Local & Cloud Control │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ Transport Layer: │ │ +│ ┌────────────┬───────────┴───────────┬────────────┐ │ +│ │ WiFi │ Thread │ Ethernet │ │ +│ │ (2.4GHz) │ (Low-Power Mesh) │ │ │ +│ └────────────┴───────────────────────┴────────────┘ │ +│ │ +│ Thread Network: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ +│ │ │ Border │──────│ Router │──────│ Router │ │ │ +│ │ │ Router │ │ Device │ │ Device │ │ │ +│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ +│ │ │ │ │ │ │ +│ │ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │ │ +│ │ │ Sleepy │ │ Sleepy │ │ Full │ │ │ +│ │ │Endpoint │ │Endpoint │ │ Device │ │ │ +│ │ └─────────┘ └─────────┘ └─────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Device Types: │ +│ - Full Thread Device: Always on, extends mesh │ +│ - Sleepy End Device: Battery, wakes on demand │ +│ - Border Router: Connects Thread to IP network │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Matter Device Types + +```typescript +// Unterstützte Matter Device Types +const matterDeviceTypes = { + // Beleuchtung + onOffLight: 0x0100, + dimmableLight: 0x0101, + colorTemperatureLight: 0x010C, + extendedColorLight: 0x010D, + + // Steckdosen & Schalter + onOffPluginUnit: 0x010A, + onOffLightSwitch: 0x0103, + dimmerSwitch: 0x0104, + + // Sensoren + contactSensor: 0x0015, + lightSensor: 0x0106, + occupancySensor: 0x0107, + temperatureSensor: 0x0302, + humiditySensor: 0x0307, + flowSensor: 0x0306, + + // Sicherheit + doorLock: 0x000A, + windowCovering: 0x0202, + + // Klima + thermostat: 0x0301, + fan: 0x002B, + airQualitySensor: 0x002C, + + // Media + basicVideoPlayer: 0x0028, + speaker: 0x0022, + + // Sonstiges + bridge: 0x000E, + aggregator: 0x000F +}; +``` + +--- + +## Thread Network Basics + +```typescript +// Thread Network Concepts + +interface ThreadNetwork { + networkName: string; // Max 16 Bytes + extendedPanId: string; // 8 Bytes + panId: number; // 16-bit + channel: number; // 11-26 (2.4GHz) + networkKey: string; // 128-bit AES key + meshLocalPrefix: string; // IPv6 /64 +} + +interface ThreadDevice { + type: 'router' | 'end_device' | 'sleepy_end_device'; + rloc16: number; // 16-bit Router Locator + extAddress: string; // 64-bit Extended Address + ipAddresses: string[]; // IPv6 Addresses + isLeader: boolean; + isBorderRouter: boolean; +} + +// Thread 1.4 Features (ab 2026 required) +const thread14Features = { + // Credential Sharing zwischen Border Routern + credentialSharing: true, + + // Automatisches Network Joining + seamlessJoining: true, + + // Improved Power Management + enhancedSleepMode: true, + + // IPv6 Border Routing + borderRouting: true +}; +``` + +--- + +## Home Assistant Matter Integration + +```yaml +# configuration.yaml + +# Matter Integration aktivieren +matter: + +# Thread Integration +thread: + +# Companion App für Commissioning +mobile_app: + +# Automatische Erkennung +default_config: +``` + +```typescript +// Matter Device via Home Assistant WebSocket API +// lib/matter-ha-client.ts + +import WebSocket from 'ws'; + +interface MatterDevice { + node_id: number; + date_commissioned: string; + last_interview: string; + interview_version: number; + available: boolean; + is_bridge: boolean; + device_info: { + vendor_id: number; + vendor_name: string; + product_id: number; + product_name: string; + node_label: string; + serial_number: string; + software_version: number; + hardware_version: number; + }; +} + +class MatterHAClient { + private ws: WebSocket; + private messageId = 1; + + constructor(haUrl: string, token: string) { + this.ws = new WebSocket(haUrl); + + this.ws.on('open', () => { + // Authentifizieren + this.ws.send(JSON.stringify({ + type: 'auth', + access_token: token + })); + }); + } + + // Matter Devices abrufen + async getMatterNodes(): Promise { + return this.sendCommand({ + type: 'matter/get_nodes' + }); + } + + // Neues Device commissionen + async commissionDevice(code: string): Promise { + return this.sendCommand({ + type: 'matter/commission', + code + }); + } + + // Device Interview (Capabilities abfragen) + async interviewNode(nodeId: number): Promise { + return this.sendCommand({ + type: 'matter/interview_node', + node_id: nodeId + }); + } + + // Device entfernen + async removeNode(nodeId: number): Promise { + return this.sendCommand({ + type: 'matter/remove_node', + node_id: nodeId + }); + } + + // Cluster Command senden + async sendClusterCommand( + nodeId: number, + endpoint: number, + cluster: string, + command: string, + payload: Record + ): Promise { + return this.sendCommand({ + type: 'matter/send_command', + node_id: nodeId, + endpoint_id: endpoint, + cluster_id: cluster, + command: command, + payload + }); + } + + private sendCommand(message: any): Promise { + return new Promise((resolve, reject) => { + const id = this.messageId++; + + const handler = (data: WebSocket.RawData) => { + const response = JSON.parse(data.toString()); + + if (response.id === id) { + this.ws.off('message', handler); + + if (response.success) { + resolve(response.result); + } else { + reject(new Error(response.error?.message)); + } + } + }; + + this.ws.on('message', handler); + this.ws.send(JSON.stringify({ ...message, id })); + }); + } +} + +// Beispiel: Licht steuern +const client = new MatterHAClient( + 'ws://homeassistant.local:8123/api/websocket', + process.env.HA_TOKEN! +); + +// OnOff Cluster Command +await client.sendClusterCommand( + 1, // Node ID + 1, // Endpoint + 'on_off', + 'on', + {} +); + +// Level Control +await client.sendClusterCommand( + 1, + 1, + 'level_control', + 'move_to_level_with_on_off', + { + level: 128, // 0-254 + transition_time: 10 // 1/10 Sekunden + } +); +``` + +--- + +## Thread Border Router Setup + +```typescript +// Thread Border Router mit OpenThread + +interface BorderRouterConfig { + networkName: string; + channel: number; + panId: number; + extPanId: string; + networkKey: string; +} + +// OpenThread Border Router API +class OTBRClient { + private baseUrl: string; + + constructor(host: string, port: number = 8081) { + this.baseUrl = `http://${host}:${port}`; + } + + // Network Status + async getNetworkStatus(): Promise { + const response = await fetch(`${this.baseUrl}/node`); + return response.json(); + } + + // Dataset (Network Credentials) + async getActiveDataset(): Promise { + const response = await fetch(`${this.baseUrl}/node/dataset/active`); + return response.json(); + } + + // Neues Netzwerk erstellen + async createNetwork(config: BorderRouterConfig): Promise { + await fetch(`${this.baseUrl}/node/dataset/active`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + NetworkName: config.networkName, + Channel: config.channel, + PanId: config.panId, + ExtPanId: config.extPanId, + NetworkKey: config.networkKey + }) + }); + + // Thread Interface aktivieren + await fetch(`${this.baseUrl}/node/state`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ State: 'enable' }) + }); + } + + // Topology abrufen + async getTopology(): Promise { + const response = await fetch(`${this.baseUrl}/diagnostics`); + return response.json(); + } + + // External Commissioning + async startCommissioner(pskd: string): Promise { + await fetch(`${this.baseUrl}/commissioner/start`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ pskd }) + }); + } +} +``` + +--- + +## Matter Commissioning Flow + +``` +┌─────────────────────────────────────────────────────────────┐ +│ MATTER COMMISSIONING FLOW │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 1. Device Discovery │ +│ ├── QR Code / Manual Pairing Code │ +│ ├── BLE Advertisement (für WiFi Devices) │ +│ └── mDNS Discovery (Thread/Ethernet) │ +│ │ +│ 2. PASE (Passcode-Authenticated Session) │ +│ ├── Secure Channel mit Pairing Code │ +│ └── SPAKE2+ Key Exchange │ +│ │ +│ 3. Certificate Installation │ +│ ├── Node Operational Certificate (NOC) │ +│ ├── Intermediate CA Certificate │ +│ └── Trusted Root Certificate │ +│ │ +│ 4. Network Provisioning │ +│ ├── WiFi Credentials (für WiFi Devices) │ +│ └── Thread Dataset (für Thread Devices) │ +│ │ +│ 5. CASE (Certificate-Authenticated Session) │ +│ ├── mTLS mit Operational Certificates │ +│ └── Secure Communication Channel │ +│ │ +│ 6. Device Interview │ +│ ├── Supported Clusters & Attributes │ +│ └── Device Type & Capabilities │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +```typescript +// Matter QR Code Parsing +function parseMatterQRCode(qrCode: string): MatterSetupPayload { + // Format: MT:Y3.13OTB00KA0648G00 + const prefix = 'MT:'; + + if (!qrCode.startsWith(prefix)) { + throw new Error('Invalid Matter QR code'); + } + + const payload = qrCode.slice(prefix.length); + + // Base38 Decode + const decoded = base38Decode(payload); + + return { + version: (decoded >> 0) & 0x7, + vendorId: (decoded >> 3) & 0xFFFF, + productId: (decoded >> 19) & 0xFFFF, + customFlow: (decoded >> 35) & 0x3, + discoveryCapabilities: (decoded >> 37) & 0xFF, + discriminator: (decoded >> 45) & 0xFFF, + passcode: (decoded >> 57) & 0x7FFFFFF + }; +} + +interface MatterSetupPayload { + version: number; + vendorId: number; + productId: number; + customFlow: number; + discoveryCapabilities: number; + discriminator: number; + passcode: number; +} +``` + +--- + +## Thread vs Zigbee vs WiFi + +| Feature | Thread | Zigbee | WiFi | +|---------|--------|--------|------| +| **Protocol** | IPv6 native | Custom | IPv4/IPv6 | +| **Mesh** | Ja | Ja | Nein | +| **Power** | Sehr niedrig | Sehr niedrig | Hoch | +| **Range** | 10-30m | 10-30m | 30-100m | +| **Bandwidth** | 250 kbps | 250 kbps | 100+ Mbps | +| **Devices/Network** | 250+ | 65.000 | ~250 | +| **Matter Support** | Native | Via Bridge | Native | +| **Battery Life** | Jahre | Jahre | Tage-Wochen | +| **Frequency** | 2.4 GHz | 2.4 GHz | 2.4/5 GHz | + +--- + +## Thread Credential Sharing (1.4) + +```typescript +// Thread 1.4: Automatic Credential Sharing + +interface ThreadCredentials { + networkName: string; + extendedPanId: string; + networkKey: string; + pskc: string; // Pre-Shared Key for Commissioner + channel: number; + panId: number; + meshLocalPrefix: string; + securityPolicy: { + rotationTime: number; + flags: number; + }; +} + +// Border Router synchronisiert Credentials automatisch +// Kein manuelles Übertragen mehr nötig + +class ThreadNetworkManager { + private credentials: ThreadCredentials | null = null; + + // Neuer Border Router joint existierendes Netzwerk + async joinExistingNetwork(borderRouterId: string): Promise { + // Thread 1.4: Credentials werden automatisch geteilt + // Kein Setup von mehreren Border Routern mehr nötig + + const existingRouters = await this.discoverBorderRouters(); + + if (existingRouters.length > 0) { + // Request Credentials vom existierenden Router + this.credentials = await this.requestCredentials( + existingRouters[0] + ); + + console.log('Joined existing Thread network'); + } else { + // Neues Netzwerk erstellen + this.credentials = this.generateNewCredentials(); + console.log('Created new Thread network'); + } + } + + private async discoverBorderRouters(): Promise { + // mDNS Discovery für _meshcop._udp + // ... + return []; + } + + private async requestCredentials(routerId: string): Promise { + // Secure request via TLS + // ... + throw new Error('Not implemented'); + } + + private generateNewCredentials(): ThreadCredentials { + return { + networkName: `ThreadNetwork-${randomHex(4)}`, + extendedPanId: randomHex(16), + networkKey: randomHex(32), + pskc: randomHex(32), + channel: 15 + Math.floor(Math.random() * 11), // 15-26 + panId: Math.floor(Math.random() * 0xFFFF), + meshLocalPrefix: 'fd00::/64', + securityPolicy: { + rotationTime: 672, // Stunden + flags: 0xF8 + } + }; + } +} + +function randomHex(bytes: number): string { + const array = new Uint8Array(bytes); + crypto.getRandomValues(array); + return Array.from(array) + .map(b => b.toString(16).padStart(2, '0')) + .join(''); +} +``` + +--- + +## Multi-Admin (Fabric) + +```typescript +// Matter Multi-Admin: Device in mehreren Ökosystemen + +interface Fabric { + fabricId: bigint; + vendorId: number; + rootPublicKey: string; + label: string; +} + +// Ein Matter Device kann bis zu 5 Fabrics unterstützen +const maxFabrics = 5; + +// Beispiel: Gleiche Lampe in Apple Home, Google Home und Home Assistant +const deviceFabrics: Fabric[] = [ + { + fabricId: 1n, + vendorId: 0x1234, // Apple + rootPublicKey: '...', + label: 'Apple Home' + }, + { + fabricId: 2n, + vendorId: 0x5678, // Google + rootPublicKey: '...', + label: 'Google Home' + }, + { + fabricId: 3n, + vendorId: 0x9ABC, // Home Assistant + rootPublicKey: '...', + label: 'Home Assistant' + } +]; + +// Alle Controller können das Gerät steuern +// Kein "Walled Garden" mehr! +``` + +--- + +## Fazit + +Matter & Thread bieten: + +1. **Interoperabilität**: Apple, Google, Amazon, Samsung +2. **Lokale Kontrolle**: Kein Cloud-Zwang +3. **Mesh Networking**: Zuverlässige Abdeckung +4. **Multi-Admin**: Ein Gerät, viele Ökosysteme + +Die Zukunft des Smart Homes ist offen. + +--- + +## Bildprompts + +1. "Smart home devices connected via Thread mesh network, interoperability" +2. "Matter logo with connected ecosystems, Apple Google Amazon unified" +3. "Thread border router connecting mesh to home network, IoT gateway" + +--- + +## Quellen + +- [Matter Standard 2026 Review](https://matter-smarthome.de/en/development/the-matter-standard-in-2026-a-status-review/) +- [Home Assistant Matter Integration](https://www.home-assistant.io/integrations/matter/) +- [Thread Protocol Overview](https://www.home-assistant.io/integrations/thread/) +- [CSA Matter Specification](https://csa-iot.org/all-solutions/matter/) +- [Qorvo Thread/WiFi Guide](https://www.qorvo.com/design-hub/blog/simplifying-smart-homes-learn-how-matter-thread-and-wi-fi-are-revolutionizing-iot-connectivity) diff --git a/blog-posts/66-zigbee-zigbee2mqtt.md b/blog-posts/66-zigbee-zigbee2mqtt.md new file mode 100644 index 0000000..2406516 --- /dev/null +++ b/blog-posts/66-zigbee-zigbee2mqtt.md @@ -0,0 +1,728 @@ +# Zigbee & Zigbee2MQTT Integration + +**Meta-Description:** Zigbee Smart Home mit Zigbee2MQTT. Lokale Kontrolle, MQTT Bridge, Device Pairing und Home Assistant Integration. + +**Keywords:** Zigbee, Zigbee2MQTT, Smart Home, MQTT, Home Assistant, Local Control, Mesh Network + +--- + +## Einführung + +**Zigbee** ist das bewährte Protokoll für batteriebetriebene Smart Home Geräte. **Zigbee2MQTT** macht es unabhängig von proprietären Bridges – lokale Kontrolle via MQTT mit über 4.000 unterstützten Geräten. + +--- + +## Zigbee Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ZIGBEE2MQTT ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Applications │ │ +│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ +│ │ │ Home │ │ Node │ │ Custom │ │ │ +│ │ │ Assistant │ │ RED │ │ App │ │ │ +│ │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │ +│ └────────┼──────────────┼──────────────┼─────────────┘ │ +│ │ │ │ │ +│ ┌────────┴──────────────┴──────────────┴─────────────┐ │ +│ │ MQTT Broker │ │ +│ │ (Mosquitto / EMQX) │ │ +│ └────────────────────────┬───────────────────────────┘ │ +│ │ │ +│ ┌────────────────────────┴───────────────────────────┐ │ +│ │ Zigbee2MQTT │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │ │ +│ │ │ Frontend │ │ Bridge │ │ Converters│ │ │ +│ │ │ (Web UI) │ │ (Core) │ │ (4000+) │ │ │ +│ │ └─────────────┘ └─────────────┘ └───────────┘ │ │ +│ └────────────────────────┬───────────────────────────┘ │ +│ │ │ +│ ┌────────────────────────┴───────────────────────────┐ │ +│ │ zigbee-herdsman │ │ +│ │ (Zigbee Protocol Stack) │ │ +│ └────────────────────────┬───────────────────────────┘ │ +│ │ │ +│ ┌────────────────────────┴───────────────────────────┐ │ +│ │ USB Adapter (Coordinator) │ │ +│ │ Sonoff ZBDongle-P / ConBee II / CC2652P │ │ +│ └────────────────────────┬───────────────────────────┘ │ +│ │ │ +│ ┌────────────────────────┴───────────────────────────┐ │ +│ │ Zigbee Mesh Network │ │ +│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ +│ │ │Router│───│Router│───│Router│───│ End │ │ │ +│ │ │(Plug)│ │(Bulb)│ │(Plug)│ │Device│ │ │ +│ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ +│ └────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Installation + +```bash +# Docker Installation (empfohlen) +mkdir zigbee2mqtt-data +cd zigbee2mqtt-data + +# configuration.yaml erstellen +cat > configuration.yaml << 'EOF' +homeassistant: true +permit_join: false +mqtt: + base_topic: zigbee2mqtt + server: mqtt://localhost:1883 + user: mqtt_user + password: mqtt_password +serial: + port: /dev/ttyUSB0 + adapter: zstack +frontend: + port: 8080 +advanced: + network_key: GENERATE + pan_id: GENERATE + channel: 15 + log_level: info + last_seen: 'ISO_8601' +EOF +``` + +```yaml +# docker-compose.yml +version: '3.8' +services: + zigbee2mqtt: + container_name: zigbee2mqtt + image: koenkk/zigbee2mqtt:latest + restart: unless-stopped + volumes: + - ./zigbee2mqtt-data:/app/data + - /run/udev:/run/udev:ro + ports: + - 8080:8080 + environment: + - TZ=Europe/Berlin + devices: + - /dev/ttyUSB0:/dev/ttyUSB0 + depends_on: + - mosquitto + + mosquitto: + container_name: mosquitto + image: eclipse-mosquitto:2 + restart: unless-stopped + volumes: + - ./mosquitto/config:/mosquitto/config + - ./mosquitto/data:/mosquitto/data + - ./mosquitto/log:/mosquitto/log + ports: + - 1883:1883 + - 9001:9001 +``` + +--- + +## MQTT Topics + +```typescript +// Zigbee2MQTT MQTT Topic Structure + +// Base Topic: zigbee2mqtt + +// Device State (Subscribe) +// zigbee2mqtt/[friendly_name] +{ + "state": "ON", + "brightness": 254, + "color_temp": 370, + "linkquality": 156, + "last_seen": "2026-01-18T10:30:00.000Z" +} + +// Device Control (Publish) +// zigbee2mqtt/[friendly_name]/set +{ + "state": "ON", + "brightness": 200, + "color_temp": 300, + "transition": 2 +} + +// Device Availability +// zigbee2mqtt/[friendly_name]/availability +"online" | "offline" + +// Bridge Status +// zigbee2mqtt/bridge/state +"online" | "offline" + +// Bridge Info +// zigbee2mqtt/bridge/info +{ + "version": "1.40.0", + "commit": "abc123", + "coordinator": { + "ieee_address": "0x00124b001cda7a45", + "type": "zStack3x0" + }, + "network": { + "channel": 15, + "pan_id": 6754, + "extended_pan_id": "0xdddddddddddddddd" + } +} + +// Device List +// zigbee2mqtt/bridge/devices +[ + { + "ieee_address": "0x00158d00012345678", + "friendly_name": "living_room_light", + "type": "Router", + "model": "LED1545G12", + "vendor": "IKEA" + } +] +``` + +--- + +## Node.js Integration + +```typescript +// lib/zigbee2mqtt-client.ts +import mqtt, { MqttClient } from 'mqtt'; + +interface Z2MDevice { + ieee_address: string; + friendly_name: string; + type: 'Coordinator' | 'Router' | 'EndDevice'; + model?: string; + vendor?: string; + supported: boolean; + definition?: { + description: string; + supports: string; + exposes: Z2MExpose[]; + }; +} + +interface Z2MExpose { + type: string; + name: string; + property: string; + access: number; + values?: string[]; + value_min?: number; + value_max?: number; +} + +interface DeviceState { + state?: 'ON' | 'OFF'; + brightness?: number; + color_temp?: number; + color?: { x: number; y: number }; + temperature?: number; + humidity?: number; + battery?: number; + occupancy?: boolean; + contact?: boolean; + linkquality?: number; + last_seen?: string; +} + +class Zigbee2MQTTClient { + private client: MqttClient; + private baseTopic: string; + private devices: Map = new Map(); + private states: Map = new Map(); + private listeners: Map void>> = new Map(); + + constructor( + brokerUrl: string, + baseTopic: string = 'zigbee2mqtt' + ) { + this.baseTopic = baseTopic; + this.client = mqtt.connect(brokerUrl); + + this.client.on('connect', () => this.onConnect()); + this.client.on('message', (topic, payload) => + this.onMessage(topic, payload.toString()) + ); + } + + private onConnect(): void { + console.log('Connected to MQTT broker'); + + // Subscribe to all Z2M topics + this.client.subscribe(`${this.baseTopic}/#`); + + // Request device list + this.publish('bridge/request/devices', ''); + } + + private onMessage(topic: string, payload: string): void { + const relativeTopic = topic.replace(`${this.baseTopic}/`, ''); + + // Bridge Events + if (relativeTopic === 'bridge/devices') { + const devices = JSON.parse(payload) as Z2MDevice[]; + this.updateDeviceList(devices); + return; + } + + if (relativeTopic === 'bridge/state') { + console.log(`Bridge state: ${payload}`); + return; + } + + // Device State Updates + if (!relativeTopic.includes('/') || relativeTopic.endsWith('/availability')) { + return; + } + + // State Update für Gerät + const deviceName = relativeTopic.split('/')[0]; + + try { + const state = JSON.parse(payload) as DeviceState; + this.states.set(deviceName, { ...this.states.get(deviceName), ...state }); + + // Listener benachrichtigen + this.listeners.get(deviceName)?.forEach(cb => cb(state)); + this.listeners.get('*')?.forEach(cb => cb(state)); + } catch { + // Nicht-JSON Payload ignorieren + } + } + + private updateDeviceList(devices: Z2MDevice[]): void { + devices.forEach(device => { + this.devices.set(device.friendly_name, device); + }); + console.log(`Loaded ${devices.length} devices`); + } + + private publish(topic: string, payload: any): void { + const fullTopic = `${this.baseTopic}/${topic}`; + const message = typeof payload === 'string' + ? payload + : JSON.stringify(payload); + + this.client.publish(fullTopic, message); + } + + // Public API + + getDevices(): Z2MDevice[] { + return Array.from(this.devices.values()); + } + + getDevice(name: string): Z2MDevice | undefined { + return this.devices.get(name); + } + + getState(deviceName: string): DeviceState | undefined { + return this.states.get(deviceName); + } + + // Device Control + setState(deviceName: string, state: Partial): void { + this.publish(`${deviceName}/set`, state); + } + + turnOn(deviceName: string): void { + this.setState(deviceName, { state: 'ON' }); + } + + turnOff(deviceName: string): void { + this.setState(deviceName, { state: 'OFF' }); + } + + toggle(deviceName: string): void { + this.publish(`${deviceName}/set`, { state: 'TOGGLE' }); + } + + setBrightness(deviceName: string, brightness: number, transition?: number): void { + this.setState(deviceName, { + brightness: Math.min(254, Math.max(0, brightness)), + ...(transition && { transition }) + }); + } + + setColorTemp(deviceName: string, colorTemp: number): void { + // Mired: 153 (cool) - 500 (warm) + this.setState(deviceName, { color_temp: colorTemp }); + } + + setColor(deviceName: string, x: number, y: number): void { + this.setState(deviceName, { color: { x, y } }); + } + + // Event Listener + onStateChange( + deviceName: string, + callback: (state: DeviceState) => void + ): () => void { + if (!this.listeners.has(deviceName)) { + this.listeners.set(deviceName, new Set()); + } + + this.listeners.get(deviceName)!.add(callback); + + return () => { + this.listeners.get(deviceName)?.delete(callback); + }; + } + + // Bridge Control + permitJoin(permit: boolean, duration?: number): void { + this.publish('bridge/request/permit_join', { + value: permit, + time: duration || 120 + }); + } + + renameDevice(oldName: string, newName: string): void { + this.publish('bridge/request/device/rename', { + from: oldName, + to: newName + }); + } + + removeDevice(deviceName: string, force?: boolean): void { + this.publish('bridge/request/device/remove', { + id: deviceName, + force: force || false + }); + } + + // OTA Updates + checkOTAUpdates(): void { + this.publish('bridge/request/device/ota_update/check', ''); + } + + updateDevice(deviceName: string): void { + this.publish('bridge/request/device/ota_update/update', { + id: deviceName + }); + } + + disconnect(): void { + this.client.end(); + } +} + +export { Zigbee2MQTTClient }; +``` + +--- + +## Express API Wrapper + +```typescript +// api/zigbee-api.ts +import express from 'express'; +import { Zigbee2MQTTClient } from '../lib/zigbee2mqtt-client'; + +const router = express.Router(); +const z2m = new Zigbee2MQTTClient(process.env.MQTT_URL || 'mqtt://localhost:1883'); + +// GET /api/devices +router.get('/devices', (req, res) => { + const devices = z2m.getDevices() + .filter(d => d.type !== 'Coordinator') + .map(d => ({ + name: d.friendly_name, + ieee: d.ieee_address, + type: d.type, + model: d.model, + vendor: d.vendor, + state: z2m.getState(d.friendly_name) + })); + + res.json(devices); +}); + +// GET /api/devices/:name +router.get('/devices/:name', (req, res) => { + const device = z2m.getDevice(req.params.name); + + if (!device) { + return res.status(404).json({ error: 'Device not found' }); + } + + res.json({ + ...device, + state: z2m.getState(req.params.name) + }); +}); + +// POST /api/devices/:name/set +router.post('/devices/:name/set', (req, res) => { + const device = z2m.getDevice(req.params.name); + + if (!device) { + return res.status(404).json({ error: 'Device not found' }); + } + + z2m.setState(req.params.name, req.body); + res.json({ success: true }); +}); + +// POST /api/devices/:name/toggle +router.post('/devices/:name/toggle', (req, res) => { + z2m.toggle(req.params.name); + res.json({ success: true }); +}); + +// Groups +// POST /api/groups/:name/set +router.post('/groups/:name/set', (req, res) => { + z2m.setState(req.params.name, req.body); + res.json({ success: true }); +}); + +// Bridge Control +// POST /api/bridge/permit_join +router.post('/bridge/permit_join', (req, res) => { + const { permit, duration } = req.body; + z2m.permitJoin(permit, duration); + res.json({ success: true }); +}); + +export default router; +``` + +--- + +## Home Assistant Auto-Discovery + +```typescript +// Zigbee2MQTT sendet automatisch Home Assistant Discovery Messages + +// homeassistant/light/0x00158d00012345678/light/config +{ + "name": "Living Room Light", + "unique_id": "0x00158d00012345678_light_zigbee2mqtt", + "state_topic": "zigbee2mqtt/living_room_light", + "command_topic": "zigbee2mqtt/living_room_light/set", + "availability": [ + { "topic": "zigbee2mqtt/bridge/state" }, + { "topic": "zigbee2mqtt/living_room_light/availability" } + ], + "brightness": true, + "brightness_scale": 254, + "color_mode": true, + "supported_color_modes": ["color_temp", "xy"], + "min_mireds": 153, + "max_mireds": 500, + "schema": "json", + "device": { + "identifiers": ["zigbee2mqtt_0x00158d00012345678"], + "name": "Living Room Light", + "model": "LED1545G12", + "manufacturer": "IKEA", + "via_device": "zigbee2mqtt_bridge" + } +} +``` + +--- + +## Device Groups + +```yaml +# configuration.yaml + +groups: + living_room: + friendly_name: Wohnzimmer + devices: + - living_room_light_1 + - living_room_light_2 + - living_room_light_3 + + all_lights: + friendly_name: Alle Lichter + devices: + - living_room_light_1 + - living_room_light_2 + - bedroom_light + - kitchen_light +``` + +```typescript +// Gruppen steuern +z2m.setState('living_room', { state: 'ON', brightness: 200 }); + +// Alle Lichter aus +z2m.turnOff('all_lights'); +``` + +--- + +## Scenes & Automations + +```yaml +# configuration.yaml + +# Szenen definieren +scenes: + - name: movie_mode + data: + living_room: + state: 'ON' + brightness: 50 + color_temp: 500 + kitchen: + state: 'OFF' + + - name: morning_routine + data: + bedroom_light: + state: 'ON' + brightness: 100 + color_temp: 250 +``` + +```typescript +// Szene aktivieren +z2m.publish('scene/recall', { scene: 'movie_mode' }); + +// Custom Automation +z2m.onStateChange('motion_sensor', (state) => { + if (state.occupancy) { + z2m.turnOn('hallway_light'); + + // Auto-Off nach 5 Minuten + setTimeout(() => { + const currentState = z2m.getState('motion_sensor'); + if (!currentState?.occupancy) { + z2m.turnOff('hallway_light'); + } + }, 5 * 60 * 1000); + } +}); + +// Temperature-basierte Automation +z2m.onStateChange('temperature_sensor', (state) => { + if (state.temperature && state.temperature > 25) { + z2m.publish('fan/set', { state: 'ON', preset_mode: 'high' }); + } +}); +``` + +--- + +## Zigbee Network Map + +```typescript +// Network Topology abrufen + +// zigbee2mqtt/bridge/request/networkmap +z2m.publish('bridge/request/networkmap', { type: 'raw' }); + +// Antwort auf zigbee2mqtt/bridge/response/networkmap +interface NetworkMap { + nodes: Array<{ + ieee_address: string; + friendly_name: string; + type: 'Coordinator' | 'Router' | 'EndDevice'; + network_address: number; + lqi: number; // Link Quality Indicator + relationship: number; + }>; + links: Array<{ + source: { ieee_address: string }; + target: { ieee_address: string }; + lqi: number; + depth: number; + }>; +} +``` + +--- + +## Troubleshooting + +```typescript +// Häufige Probleme und Lösungen + +// 1. Gerät reagiert nicht +async function troubleshootDevice(deviceName: string) { + const state = z2m.getState(deviceName); + + // Linkqualität prüfen + if (state?.linkquality && state.linkquality < 50) { + console.log('Low link quality - consider adding router devices'); + } + + // Availability prüfen + // Subscribe to zigbee2mqtt/[device]/availability + + // Device Interview neu durchführen + z2m.publish('bridge/request/device/interview', { id: deviceName }); +} + +// 2. Pairing funktioniert nicht +function resetAndPair() { + // Permit Join aktivieren + z2m.permitJoin(true, 180); // 3 Minuten + + // Gerät in Pairing-Modus versetzen (geräteabhängig) + console.log('Reset device and put in pairing mode'); +} + +// 3. Mesh-Netzwerk optimieren +function optimizeMesh() { + // Router-Geräte (Steckdosen, Lampen) sind wichtig für Mesh + // Mindestens ein Router pro Raum + + const devices = z2m.getDevices(); + const routers = devices.filter(d => d.type === 'Router'); + const endDevices = devices.filter(d => d.type === 'EndDevice'); + + console.log(`Routers: ${routers.length}, End Devices: ${endDevices.length}`); + + if (endDevices.length / routers.length > 6) { + console.log('Warning: Not enough routers for stable mesh'); + } +} +``` + +--- + +## Fazit + +Zigbee2MQTT bietet: + +1. **Lokale Kontrolle**: Keine Cloud-Abhängigkeit +2. **4000+ Geräte**: Herstellerunabhängig +3. **MQTT Integration**: Einfache Anbindung +4. **Open Source**: Aktive Community + +Der beste Weg für ein offenes Smart Home. + +--- + +## Bildprompts + +1. "Zigbee mesh network diagram with router and end devices connected" +2. "Zigbee2MQTT web interface showing device list and controls" +3. "USB Zigbee coordinator stick connected to Raspberry Pi, smart home hub" + +--- + +## Quellen + +- [Zigbee2MQTT Documentation](https://www.zigbee2mqtt.io/) +- [zigbee-herdsman GitHub](https://github.com/Koenkk/zigbee-herdsman) +- [Supported Devices](https://www.zigbee2mqtt.io/supported-devices/) +- [Zigbee Technology 2026](https://zigbeehubs.com/zigbee-technology-2026/) diff --git a/blog-posts/67-influxdb-sensor-data.md b/blog-posts/67-influxdb-sensor-data.md new file mode 100644 index 0000000..144c9cf --- /dev/null +++ b/blog-posts/67-influxdb-sensor-data.md @@ -0,0 +1,733 @@ +# IoT Sensor Data mit InfluxDB + +**Meta-Description:** Time Series Daten mit InfluxDB speichern und analysieren. Flux Queries, Grafana Dashboards und IoT Data Pipelines. + +**Keywords:** InfluxDB, Time Series, IoT, Sensor Data, Grafana, Flux, Data Analytics, Monitoring + +--- + +## Einführung + +**InfluxDB** ist die führende Time Series Database für IoT und Monitoring. Optimiert für Sensor-Daten, Metriken und Events – mit der mächtigen **Flux** Query Language und nahtloser **Grafana** Integration. + +--- + +## InfluxDB Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ INFLUXDB IOT ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Data Sources: │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ Sensors │ │ Devices │ │ Gateways│ │ APIs │ │ +│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ +│ │ │ │ │ │ +│ └────────────┴────────────┴────────────┘ │ +│ │ │ +│ Ingestion: ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Line Protocol / Telegraf / Client Libraries │ │ +│ │ MQTT / HTTP API / Native UDP │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ Storage: ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ InfluxDB │ │ +│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ +│ │ │ Bucket │ │ Bucket │ │ Bucket │ │ │ +│ │ │ (sensors) │ │ (metrics) │ │ (events) │ │ │ +│ │ └───────────┘ └───────────┘ └───────────┘ │ │ +│ │ │ │ +│ │ Time Series Index (TSI) + Time Structured Merge │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ Query & Visualization: ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ +│ │ │ Flux │ │ Grafana │ │ API │ │ │ +│ │ │ Queries │ │Dashboard│ │ Clients │ │ │ +│ │ └─────────┘ └─────────┘ └─────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Setup & Installation + +```yaml +# docker-compose.yml +version: '3.8' +services: + influxdb: + image: influxdb:2.7 + container_name: influxdb + ports: + - 8086:8086 + volumes: + - influxdb-data:/var/lib/influxdb2 + - influxdb-config:/etc/influxdb2 + environment: + - DOCKER_INFLUXDB_INIT_MODE=setup + - DOCKER_INFLUXDB_INIT_USERNAME=admin + - DOCKER_INFLUXDB_INIT_PASSWORD=supersecret + - DOCKER_INFLUXDB_INIT_ORG=myorg + - DOCKER_INFLUXDB_INIT_BUCKET=sensors + - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=my-super-secret-token + + grafana: + image: grafana/grafana:latest + container_name: grafana + ports: + - 3000:3000 + volumes: + - grafana-data:/var/lib/grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + depends_on: + - influxdb + +volumes: + influxdb-data: + influxdb-config: + grafana-data: +``` + +--- + +## Node.js Client + +```typescript +// lib/influxdb-client.ts +import { InfluxDB, Point, WriteApi, QueryApi, flux } from '@influxdata/influxdb-client'; + +interface InfluxConfig { + url: string; + token: string; + org: string; + bucket: string; +} + +interface SensorReading { + measurement: string; + tags: Record; + fields: Record; + timestamp?: Date; +} + +class InfluxDBClient { + private client: InfluxDB; + private writeApi: WriteApi; + private queryApi: QueryApi; + private config: InfluxConfig; + + constructor(config: InfluxConfig) { + this.config = config; + this.client = new InfluxDB({ + url: config.url, + token: config.token + }); + + this.writeApi = this.client.getWriteApi(config.org, config.bucket, 'ms'); + this.queryApi = this.client.getQueryApi(config.org); + + // Batch Settings + this.writeApi.useDefaultTags({ source: 'node-app' }); + } + + // Single Point schreiben + writePoint(reading: SensorReading): void { + const point = new Point(reading.measurement); + + // Tags hinzufügen + Object.entries(reading.tags).forEach(([key, value]) => { + point.tag(key, value); + }); + + // Fields hinzufügen + Object.entries(reading.fields).forEach(([key, value]) => { + if (typeof value === 'number') { + if (Number.isInteger(value)) { + point.intField(key, value); + } else { + point.floatField(key, value); + } + } else if (typeof value === 'boolean') { + point.booleanField(key, value); + } else { + point.stringField(key, value); + } + }); + + // Timestamp + if (reading.timestamp) { + point.timestamp(reading.timestamp); + } + + this.writeApi.writePoint(point); + } + + // Mehrere Points schreiben + writePoints(readings: SensorReading[]): void { + readings.forEach(reading => this.writePoint(reading)); + } + + // Sofort schreiben (flush) + async flush(): Promise { + await this.writeApi.flush(); + } + + // Flux Query ausführen + async query(fluxQuery: string): Promise { + const results: T[] = []; + + return new Promise((resolve, reject) => { + this.queryApi.queryRows(fluxQuery, { + next: (row, tableMeta) => { + const obj = tableMeta.toObject(row) as T; + results.push(obj); + }, + error: (error) => reject(error), + complete: () => resolve(results) + }); + }); + } + + // Letzte Messung abrufen + async getLatest( + measurement: string, + tags?: Record + ): Promise { + let tagFilter = ''; + if (tags) { + tagFilter = Object.entries(tags) + .map(([key, value]) => `r["${key}"] == "${value}"`) + .join(' and '); + } + + const query = ` + from(bucket: "${this.config.bucket}") + |> range(start: -1h) + |> filter(fn: (r) => r["_measurement"] == "${measurement}") + ${tagFilter ? `|> filter(fn: (r) => ${tagFilter})` : ''} + |> last() + `; + + const results = await this.query(query); + return results[0]; + } + + // Aggregierte Daten abrufen + async getAggregated( + measurement: string, + field: string, + aggregation: 'mean' | 'max' | 'min' | 'sum' | 'count', + window: string, + range: string = '-24h', + tags?: Record + ): Promise { + let tagFilter = ''; + if (tags) { + tagFilter = Object.entries(tags) + .map(([key, value]) => `r["${key}"] == "${value}"`) + .join(' and '); + } + + const query = ` + from(bucket: "${this.config.bucket}") + |> range(start: ${range}) + |> filter(fn: (r) => r["_measurement"] == "${measurement}") + |> filter(fn: (r) => r["_field"] == "${field}") + ${tagFilter ? `|> filter(fn: (r) => ${tagFilter})` : ''} + |> aggregateWindow(every: ${window}, fn: ${aggregation}, createEmpty: false) + |> yield(name: "${aggregation}") + `; + + return this.query(query); + } + + // Cleanup + async close(): Promise { + await this.writeApi.close(); + } +} + +export { InfluxDBClient }; +``` + +--- + +## Sensor Data Collection + +```typescript +// services/sensor-collector.ts +import { InfluxDBClient } from '../lib/influxdb-client'; +import mqtt from 'mqtt'; + +interface MQTTSensorMessage { + device_id: string; + temperature?: number; + humidity?: number; + pressure?: number; + battery?: number; + rssi?: number; + timestamp?: string; +} + +class SensorDataCollector { + private influx: InfluxDBClient; + private mqtt: mqtt.MqttClient; + private flushInterval: NodeJS.Timeout | null = null; + + constructor( + influxConfig: { url: string; token: string; org: string; bucket: string }, + mqttUrl: string + ) { + this.influx = new InfluxDBClient(influxConfig); + this.mqtt = mqtt.connect(mqttUrl); + + this.mqtt.on('connect', () => { + console.log('MQTT connected'); + this.mqtt.subscribe('sensors/+/data'); + this.mqtt.subscribe('zigbee2mqtt/+'); + }); + + this.mqtt.on('message', (topic, payload) => { + this.handleMessage(topic, payload.toString()); + }); + + // Periodisch flushen + this.flushInterval = setInterval(() => { + this.influx.flush().catch(console.error); + }, 10000); + } + + private handleMessage(topic: string, payload: string): void { + try { + const data = JSON.parse(payload); + + if (topic.startsWith('sensors/')) { + this.processSensorData(topic, data); + } else if (topic.startsWith('zigbee2mqtt/')) { + this.processZigbeeData(topic, data); + } + } catch (error) { + console.error('Failed to parse message:', error); + } + } + + private processSensorData(topic: string, data: MQTTSensorMessage): void { + const deviceId = topic.split('/')[1]; + + // Environment Data + if (data.temperature !== undefined || data.humidity !== undefined) { + this.influx.writePoint({ + measurement: 'environment', + tags: { + device_id: deviceId, + location: this.getDeviceLocation(deviceId) + }, + fields: { + ...(data.temperature !== undefined && { temperature: data.temperature }), + ...(data.humidity !== undefined && { humidity: data.humidity }), + ...(data.pressure !== undefined && { pressure: data.pressure }) + }, + timestamp: data.timestamp ? new Date(data.timestamp) : new Date() + }); + } + + // Device Metrics + if (data.battery !== undefined || data.rssi !== undefined) { + this.influx.writePoint({ + measurement: 'device_metrics', + tags: { + device_id: deviceId + }, + fields: { + ...(data.battery !== undefined && { battery: data.battery }), + ...(data.rssi !== undefined && { rssi: data.rssi }) + } + }); + } + } + + private processZigbeeData(topic: string, data: any): void { + const deviceName = topic.split('/')[1]; + + // Skip bridge topics + if (deviceName === 'bridge') return; + + const fields: Record = {}; + + // Bekannte Felder extrahieren + if (data.temperature !== undefined) fields.temperature = data.temperature; + if (data.humidity !== undefined) fields.humidity = data.humidity; + if (data.pressure !== undefined) fields.pressure = data.pressure; + if (data.battery !== undefined) fields.battery = data.battery; + if (data.linkquality !== undefined) fields.linkquality = data.linkquality; + if (data.illuminance !== undefined) fields.illuminance = data.illuminance; + if (data.occupancy !== undefined) fields.occupancy = data.occupancy ? 1 : 0; + if (data.contact !== undefined) fields.contact = data.contact ? 1 : 0; + + if (Object.keys(fields).length > 0) { + this.influx.writePoint({ + measurement: 'zigbee_sensors', + tags: { + device: deviceName, + type: this.inferDeviceType(data) + }, + fields + }); + } + } + + private getDeviceLocation(deviceId: string): string { + const locationMap: Record = { + 'living-room-01': 'living_room', + 'bedroom-01': 'bedroom', + 'kitchen-01': 'kitchen', + 'outdoor-01': 'outdoor' + }; + return locationMap[deviceId] || 'unknown'; + } + + private inferDeviceType(data: any): string { + if (data.occupancy !== undefined) return 'motion_sensor'; + if (data.contact !== undefined) return 'contact_sensor'; + if (data.illuminance !== undefined) return 'light_sensor'; + if (data.temperature !== undefined) return 'climate_sensor'; + return 'unknown'; + } + + async stop(): Promise { + if (this.flushInterval) { + clearInterval(this.flushInterval); + } + await this.influx.close(); + this.mqtt.end(); + } +} + +// Verwendung +const collector = new SensorDataCollector( + { + url: process.env.INFLUX_URL || 'http://localhost:8086', + token: process.env.INFLUX_TOKEN!, + org: 'myorg', + bucket: 'sensors' + }, + process.env.MQTT_URL || 'mqtt://localhost:1883' +); +``` + +--- + +## Flux Queries + +```typescript +// Flux Query Examples + +// 1. Durchschnittstemperatur pro Raum (letzte 24h) +const avgTempByRoom = ` +from(bucket: "sensors") + |> range(start: -24h) + |> filter(fn: (r) => r["_measurement"] == "environment") + |> filter(fn: (r) => r["_field"] == "temperature") + |> group(columns: ["location"]) + |> mean() + |> yield(name: "avg_temperature") +`; + +// 2. Min/Max Temperatur pro Tag +const tempMinMax = ` +from(bucket: "sensors") + |> range(start: -7d) + |> filter(fn: (r) => r["_measurement"] == "environment") + |> filter(fn: (r) => r["_field"] == "temperature") + |> aggregateWindow(every: 1d, fn: min, createEmpty: false) + |> yield(name: "min") + +from(bucket: "sensors") + |> range(start: -7d) + |> filter(fn: (r) => r["_measurement"] == "environment") + |> filter(fn: (r) => r["_field"] == "temperature") + |> aggregateWindow(every: 1d, fn: max, createEmpty: false) + |> yield(name: "max") +`; + +// 3. Bewegungserkennung Events +const motionEvents = ` +from(bucket: "sensors") + |> range(start: -1h) + |> filter(fn: (r) => r["_measurement"] == "zigbee_sensors") + |> filter(fn: (r) => r["type"] == "motion_sensor") + |> filter(fn: (r) => r["_field"] == "occupancy") + |> filter(fn: (r) => r["_value"] == 1) + |> count() + |> group(columns: ["device"]) +`; + +// 4. Batterie-Status aller Geräte +const batteryStatus = ` +from(bucket: "sensors") + |> range(start: -1h) + |> filter(fn: (r) => r["_field"] == "battery") + |> last() + |> filter(fn: (r) => r["_value"] < 20) + |> yield(name: "low_battery") +`; + +// 5. Anomalie-Erkennung (außerhalb 2 Standardabweichungen) +const anomalies = ` +import "math" + +data = from(bucket: "sensors") + |> range(start: -7d) + |> filter(fn: (r) => r["_measurement"] == "environment") + |> filter(fn: (r) => r["_field"] == "temperature") + |> filter(fn: (r) => r["location"] == "living_room") + +stats = data + |> mean() + |> map(fn: (r) => ({r with mean: r._value})) + |> join( + tables: {std: data |> stddev()}, + on: ["location"], + method: "inner" + ) + +data + |> map(fn: (r) => ({ + r with + upper: stats.mean + 2.0 * stats._value, + lower: stats.mean - 2.0 * stats._value + })) + |> filter(fn: (r) => r._value > r.upper or r._value < r.lower) +`; + +// 6. Downsampling für Dashboard +const downsampled = ` +from(bucket: "sensors") + |> range(start: -30d) + |> filter(fn: (r) => r["_measurement"] == "environment") + |> filter(fn: (r) => r["_field"] == "temperature") + |> aggregateWindow( + every: 1h, + fn: mean, + createEmpty: false + ) + |> yield(name: "hourly_avg") +`; +``` + +--- + +## API Endpoints + +```typescript +// api/metrics-api.ts +import express from 'express'; +import { InfluxDBClient } from '../lib/influxdb-client'; + +const router = express.Router(); +const influx = new InfluxDBClient({ + url: process.env.INFLUX_URL!, + token: process.env.INFLUX_TOKEN!, + org: 'myorg', + bucket: 'sensors' +}); + +// GET /api/metrics/temperature?location=living_room&range=24h +router.get('/metrics/temperature', async (req, res) => { + const { location, range = '24h' } = req.query; + + try { + const data = await influx.getAggregated( + 'environment', + 'temperature', + 'mean', + '15m', + `-${range}`, + location ? { location: location as string } : undefined + ); + + res.json(data.map(d => ({ + time: d._time, + value: d._value, + location: d.location + }))); + } catch (error) { + res.status(500).json({ error: 'Query failed' }); + } +}); + +// GET /api/metrics/latest +router.get('/metrics/latest', async (req, res) => { + const query = ` + from(bucket: "sensors") + |> range(start: -1h) + |> filter(fn: (r) => r["_measurement"] == "environment") + |> last() + |> pivot( + rowKey: ["_time", "location"], + columnKey: ["_field"], + valueColumn: "_value" + ) + `; + + try { + const data = await influx.query(query); + res.json(data); + } catch (error) { + res.status(500).json({ error: 'Query failed' }); + } +}); + +// GET /api/metrics/summary +router.get('/metrics/summary', async (req, res) => { + const query = ` + from(bucket: "sensors") + |> range(start: -24h) + |> filter(fn: (r) => r["_measurement"] == "environment") + |> filter(fn: (r) => r["_field"] == "temperature") + |> group(columns: ["location"]) + |> reduce( + fn: (r, accumulator) => ({ + count: accumulator.count + 1, + sum: accumulator.sum + r._value, + min: if r._value < accumulator.min then r._value else accumulator.min, + max: if r._value > accumulator.max then r._value else accumulator.max + }), + identity: {count: 0, sum: 0.0, min: 100.0, max: -100.0} + ) + |> map(fn: (r) => ({ + location: r.location, + count: r.count, + avg: r.sum / float(v: r.count), + min: r.min, + max: r.max + })) + `; + + try { + const data = await influx.query(query); + res.json(data); + } catch (error) { + res.status(500).json({ error: 'Query failed' }); + } +}); + +export default router; +``` + +--- + +## Retention Policies + +```typescript +// Data Retention Management + +// InfluxDB 2.x verwendet Bucket Retention + +// Bucket erstellen mit Retention (via API) +async function createBucketWithRetention( + influxUrl: string, + token: string, + org: string, + bucketName: string, + retentionSeconds: number +) { + const response = await fetch(`${influxUrl}/api/v2/buckets`, { + method: 'POST', + headers: { + 'Authorization': `Token ${token}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + name: bucketName, + orgID: org, + retentionRules: [{ + type: 'expire', + everySeconds: retentionSeconds + }] + }) + }); + + return response.json(); +} + +// Beispiel: Verschiedene Buckets für verschiedene Retention +const buckets = { + 'sensors_raw': 7 * 24 * 60 * 60, // 7 Tage für Rohdaten + 'sensors_hourly': 30 * 24 * 60 * 60, // 30 Tage für Stundendaten + 'sensors_daily': 365 * 24 * 60 * 60, // 1 Jahr für Tagesdaten + 'sensors_archive': 0 // Unbegrenzt für Archiv +}; +``` + +--- + +## Grafana Dashboard + +```json +{ + "dashboard": { + "title": "IoT Sensor Dashboard", + "panels": [ + { + "title": "Temperature", + "type": "timeseries", + "datasource": "InfluxDB", + "targets": [ + { + "query": "from(bucket: \"sensors\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"environment\")\n |> filter(fn: (r) => r[\"_field\"] == \"temperature\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)" + } + ] + }, + { + "title": "Humidity", + "type": "gauge", + "datasource": "InfluxDB", + "targets": [ + { + "query": "from(bucket: \"sensors\")\n |> range(start: -5m)\n |> filter(fn: (r) => r[\"_measurement\"] == \"environment\")\n |> filter(fn: (r) => r[\"_field\"] == \"humidity\")\n |> last()" + } + ] + } + ] + } +} +``` + +--- + +## Fazit + +InfluxDB für IoT bietet: + +1. **Optimiert für Time Series**: Hohe Write-Performance +2. **Flux Language**: Mächtige Queries & Transformationen +3. **Retention Policies**: Automatische Datenbereinigung +4. **Grafana Integration**: Professionelle Visualisierung + +Die Standard-Lösung für IoT Analytics. + +--- + +## Bildprompts + +1. "Time series graph showing sensor data over time, temperature humidity" +2. "Grafana dashboard with IoT metrics, colorful panels and gauges" +3. "Data pipeline from sensors through InfluxDB to visualization" + +--- + +## Quellen + +- [InfluxDB Documentation](https://docs.influxdata.com/influxdb/) +- [Flux Language Reference](https://docs.influxdata.com/flux/) +- [InfluxDB Cloud IoT](https://www.influxdata.com/influxdb-cloud-iot/) +- [Grafana InfluxDB Datasource](https://grafana.com/docs/grafana/latest/datasources/influxdb/) diff --git a/blog-posts/68-iot-security.md b/blog-posts/68-iot-security.md new file mode 100644 index 0000000..77a9c13 --- /dev/null +++ b/blog-posts/68-iot-security.md @@ -0,0 +1,825 @@ +# IoT Security Best Practices + +**Meta-Description:** IoT Security implementieren. Device Authentication, Verschlüsselung, Secure Boot und Firmware Updates für sichere IoT-Systeme. + +**Keywords:** IoT Security, Device Authentication, TLS, Encryption, Secure Boot, Firmware Update, MQTT Security + +--- + +## Einführung + +IoT-Sicherheit ist kritisch – vernetzte Geräte sind potenzielle Angriffsziele. **Device Authentication, Verschlüsselung, Secure Boot** und sichere **Firmware Updates** sind Pflicht für jedes professionelle IoT-System. + +--- + +## IoT Security Layers + +``` +┌─────────────────────────────────────────────────────────────┐ +│ IOT SECURITY ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Layer 7 - Application Security: │ +│ ├── Input Validation │ +│ ├── API Security (Rate Limiting, Auth) │ +│ └── Secure Data Handling │ +│ │ +│ Layer 6 - Identity & Access: │ +│ ├── Device Authentication (Certificates, Tokens) │ +│ ├── User Authentication (MFA, Passkeys) │ +│ └── Role-Based Access Control (RBAC) │ +│ │ +│ Layer 5 - Communication Security: │ +│ ├── TLS 1.3 Encryption │ +│ ├── mTLS (Mutual TLS) │ +│ └── VPN / Private Networks │ +│ │ +│ Layer 4 - Network Security: │ +│ ├── Firewalls / Segmentation │ +│ ├── Intrusion Detection (IDS) │ +│ └── Traffic Monitoring │ +│ │ +│ Layer 3 - Device Security: │ +│ ├── Secure Boot │ +│ ├── Hardware Security Module (HSM) │ +│ ├── Firmware Signing │ +│ └── Secure Storage (Encrypted) │ +│ │ +│ Layer 2 - Physical Security: │ +│ ├── Tamper Detection │ +│ ├── Secure Enclosure │ +│ └── Debug Port Disable │ +│ │ +│ Compliance: NIST, ISO 27001, IEC 62443, GDPR │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Device Authentication + +```typescript +// lib/device-auth.ts +import crypto from 'crypto'; +import jwt from 'jsonwebtoken'; + +interface DeviceCredentials { + deviceId: string; + certificate: string; + privateKey: string; +} + +interface DeviceToken { + deviceId: string; + iat: number; + exp: number; + capabilities: string[]; +} + +class DeviceAuthenticator { + private caCertificate: string; + private jwtSecret: string; + + constructor(caCertPath: string, jwtSecret: string) { + this.caCertificate = require('fs').readFileSync(caCertPath, 'utf8'); + this.jwtSecret = jwtSecret; + } + + // X.509 Certificate-based Authentication + verifyCertificate(clientCert: string): boolean { + try { + const cert = new crypto.X509Certificate(clientCert); + const caCert = new crypto.X509Certificate(this.caCertificate); + + // Verify certificate chain + if (!cert.verify(caCert.publicKey)) { + return false; + } + + // Check expiration + const now = new Date(); + if (now < new Date(cert.validFrom) || now > new Date(cert.validTo)) { + return false; + } + + // Check revocation (in production: check CRL/OCSP) + // ... + + return true; + } catch (error) { + console.error('Certificate verification failed:', error); + return false; + } + } + + // Extract Device ID from Certificate + extractDeviceId(clientCert: string): string | null { + try { + const cert = new crypto.X509Certificate(clientCert); + // CN (Common Name) enthält Device ID + const cnMatch = cert.subject.match(/CN=([^,]+)/); + return cnMatch ? cnMatch[1] : null; + } catch { + return null; + } + } + + // JWT Token Generation für Devices + generateDeviceToken(deviceId: string, capabilities: string[]): string { + const payload: Omit = { + deviceId, + capabilities + }; + + return jwt.sign(payload, this.jwtSecret, { + expiresIn: '24h', + issuer: 'iot-platform', + audience: 'iot-devices' + }); + } + + // Token Verification + verifyDeviceToken(token: string): DeviceToken | null { + try { + return jwt.verify(token, this.jwtSecret, { + issuer: 'iot-platform', + audience: 'iot-devices' + }) as DeviceToken; + } catch { + return null; + } + } + + // Challenge-Response Authentication + generateChallenge(): { challenge: string; expiresAt: Date } { + const challenge = crypto.randomBytes(32).toString('hex'); + const expiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 Minuten + + return { challenge, expiresAt }; + } + + verifyChallenge( + challenge: string, + response: string, + devicePublicKey: string + ): boolean { + try { + const verify = crypto.createVerify('SHA256'); + verify.update(challenge); + + return verify.verify(devicePublicKey, response, 'base64'); + } catch { + return false; + } + } +} + +// API Key Authentication (für einfachere Devices) +interface APIKeyConfig { + deviceId: string; + apiKey: string; + createdAt: Date; + lastUsed?: Date; + rateLimit: number; +} + +class APIKeyAuthenticator { + private keys: Map = new Map(); + + generateKey(deviceId: string, rateLimit: number = 100): string { + const apiKey = `iot_${crypto.randomBytes(24).toString('hex')}`; + + this.keys.set(apiKey, { + deviceId, + apiKey, + createdAt: new Date(), + rateLimit + }); + + return apiKey; + } + + validateKey(apiKey: string): APIKeyConfig | null { + const config = this.keys.get(apiKey); + + if (!config) { + return null; + } + + // Update last used + config.lastUsed = new Date(); + + return config; + } + + revokeKey(apiKey: string): void { + this.keys.delete(apiKey); + } +} +``` + +--- + +## TLS/mTLS Communication + +```typescript +// lib/secure-mqtt.ts +import mqtt from 'mqtt'; +import fs from 'fs'; +import tls from 'tls'; + +interface SecureMQTTConfig { + brokerUrl: string; + caCert: string; + clientCert: string; + clientKey: string; + clientId: string; +} + +function createSecureMQTTClient(config: SecureMQTTConfig): mqtt.MqttClient { + return mqtt.connect(config.brokerUrl, { + clientId: config.clientId, + protocol: 'mqtts', + protocolVersion: 5, // MQTT 5.0 + + // TLS Options + ca: fs.readFileSync(config.caCert), + cert: fs.readFileSync(config.clientCert), + key: fs.readFileSync(config.clientKey), + + // Strict verification + rejectUnauthorized: true, + checkServerIdentity: (hostname, cert) => { + // Custom hostname verification if needed + return tls.checkServerIdentity(hostname, cert); + }, + + // Connection Options + keepalive: 60, + reconnectPeriod: 5000, + connectTimeout: 30000, + + // Clean Session (MQTT 5.0) + clean: true, + properties: { + sessionExpiryInterval: 0 + } + }); +} + +// HTTPS Server mit mTLS +import https from 'https'; +import express from 'express'; + +function createSecureServer( + app: express.Application, + caCert: string, + serverCert: string, + serverKey: string +) { + return https.createServer({ + ca: fs.readFileSync(caCert), + cert: fs.readFileSync(serverCert), + key: fs.readFileSync(serverKey), + + // Require client certificates (mTLS) + requestCert: true, + rejectUnauthorized: true, + + // Minimum TLS version + minVersion: 'TLSv1.3', + + // Cipher Suites (only strong ciphers) + ciphers: [ + 'TLS_AES_256_GCM_SHA384', + 'TLS_CHACHA20_POLY1305_SHA256', + 'TLS_AES_128_GCM_SHA256' + ].join(':') + }, app); +} +``` + +--- + +## Secure Data Storage + +```typescript +// lib/secure-storage.ts +import crypto from 'crypto'; +import { promisify } from 'util'; + +const scrypt = promisify(crypto.scrypt); + +interface EncryptedData { + iv: string; + data: string; + tag: string; + salt: string; +} + +class SecureStorage { + private masterKey: Buffer | null = null; + + // Key Derivation von Password/PIN + async deriveKey(password: string, salt: Buffer): Promise { + return scrypt(password, salt, 32) as Promise; + } + + // Initialize mit Master Key + async initialize(masterPassword: string): Promise { + const salt = crypto.randomBytes(32); + this.masterKey = await this.deriveKey(masterPassword, salt); + } + + // AES-256-GCM Encryption + encrypt(plaintext: string): EncryptedData { + if (!this.masterKey) { + throw new Error('Storage not initialized'); + } + + const iv = crypto.randomBytes(16); + const salt = crypto.randomBytes(32); + + const cipher = crypto.createCipheriv('aes-256-gcm', this.masterKey, iv); + + let encrypted = cipher.update(plaintext, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + + const tag = cipher.getAuthTag(); + + return { + iv: iv.toString('hex'), + data: encrypted, + tag: tag.toString('hex'), + salt: salt.toString('hex') + }; + } + + // Decryption + decrypt(encryptedData: EncryptedData): string { + if (!this.masterKey) { + throw new Error('Storage not initialized'); + } + + const iv = Buffer.from(encryptedData.iv, 'hex'); + const tag = Buffer.from(encryptedData.tag, 'hex'); + + const decipher = crypto.createDecipheriv('aes-256-gcm', this.masterKey, iv); + decipher.setAuthTag(tag); + + let decrypted = decipher.update(encryptedData.data, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + + return decrypted; + } + + // Secure Credential Storage + async storeCredentials( + deviceId: string, + credentials: Record + ): Promise { + const data = JSON.stringify({ deviceId, credentials, timestamp: Date.now() }); + return this.encrypt(data); + } +} + +// Hardware Security Module (HSM) Integration +interface HSMConfig { + slotId: number; + pin: string; + modulePath: string; +} + +class HSMIntegration { + // Für echte HSM-Integration: PKCS#11 Library verwenden + // z.B. graphene-pk11, pkcs11js + + async sign(data: Buffer, keyLabel: string): Promise { + // HSM signing operation + throw new Error('Implement with actual HSM library'); + } + + async verify( + data: Buffer, + signature: Buffer, + keyLabel: string + ): Promise { + // HSM verification + throw new Error('Implement with actual HSM library'); + } + + async generateKeyPair(keyLabel: string): Promise<{ publicKey: string }> { + // Generate key pair in HSM (private key never leaves HSM) + throw new Error('Implement with actual HSM library'); + } +} +``` + +--- + +## Secure Firmware Updates + +```typescript +// lib/firmware-update.ts +import crypto from 'crypto'; +import fs from 'fs'; +import { pipeline } from 'stream/promises'; + +interface FirmwareMetadata { + version: string; + releaseDate: string; + sha256: string; + signature: string; + size: number; + minVersion: string; + deviceModels: string[]; +} + +class SecureFirmwareUpdater { + private signingPublicKey: string; + + constructor(publicKeyPath: string) { + this.signingPublicKey = fs.readFileSync(publicKeyPath, 'utf8'); + } + + // Firmware Signatur verifizieren + verifySignature(firmwarePath: string, signature: string): boolean { + const firmware = fs.readFileSync(firmwarePath); + + const verify = crypto.createVerify('SHA256'); + verify.update(firmware); + + return verify.verify(this.signingPublicKey, signature, 'base64'); + } + + // Firmware Hash verifizieren + verifyHash(firmwarePath: string, expectedHash: string): boolean { + const firmware = fs.readFileSync(firmwarePath); + const hash = crypto.createHash('sha256').update(firmware).digest('hex'); + + return hash === expectedHash; + } + + // Firmware Download mit Verification + async downloadFirmware( + url: string, + metadata: FirmwareMetadata, + targetPath: string + ): Promise { + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`Download failed: ${response.status}`); + } + + // Streaming Download mit Hash-Berechnung + const hash = crypto.createHash('sha256'); + const fileStream = fs.createWriteStream(targetPath); + + const reader = response.body?.getReader(); + if (!reader) throw new Error('No response body'); + + let downloadedSize = 0; + + while (true) { + const { done, value } = await reader.read(); + + if (done) break; + + hash.update(value); + fileStream.write(value); + downloadedSize += value.length; + } + + fileStream.end(); + + // Size Check + if (downloadedSize !== metadata.size) { + fs.unlinkSync(targetPath); + throw new Error('Size mismatch'); + } + + // Hash Check + const actualHash = hash.digest('hex'); + if (actualHash !== metadata.sha256) { + fs.unlinkSync(targetPath); + throw new Error('Hash mismatch'); + } + + // Signature Check + if (!this.verifySignature(targetPath, metadata.signature)) { + fs.unlinkSync(targetPath); + throw new Error('Signature verification failed'); + } + + return true; + } + + // Version Check + shouldUpdate( + currentVersion: string, + newVersion: string, + minVersion: string + ): boolean { + const current = this.parseVersion(currentVersion); + const target = this.parseVersion(newVersion); + const min = this.parseVersion(minVersion); + + // Current version must be >= minVersion for update + if (this.compareVersions(current, min) < 0) { + return false; + } + + // Only update if target is newer + return this.compareVersions(target, current) > 0; + } + + private parseVersion(version: string): number[] { + return version.split('.').map(Number); + } + + private compareVersions(a: number[], b: number[]): number { + for (let i = 0; i < Math.max(a.length, b.length); i++) { + const av = a[i] || 0; + const bv = b[i] || 0; + if (av > bv) return 1; + if (av < bv) return -1; + } + return 0; + } +} + +// OTA Update API +import express from 'express'; + +const firmwareRouter = express.Router(); + +// GET /api/firmware/check?model=xxx&version=1.0.0 +firmwareRouter.get('/check', async (req, res) => { + const { model, version } = req.query; + + // Check for available updates + const latestFirmware = await getLatestFirmware(model as string); + + if (!latestFirmware) { + return res.json({ updateAvailable: false }); + } + + const updater = new SecureFirmwareUpdater('./keys/firmware-signing.pub'); + + const shouldUpdate = updater.shouldUpdate( + version as string, + latestFirmware.version, + latestFirmware.minVersion + ); + + res.json({ + updateAvailable: shouldUpdate, + ...(shouldUpdate && { firmware: latestFirmware }) + }); +}); +``` + +--- + +## Input Validation & Sanitization + +```typescript +// lib/input-validation.ts +import { z } from 'zod'; + +// Schema für Sensor-Daten +const SensorDataSchema = z.object({ + device_id: z.string() + .min(1) + .max(64) + .regex(/^[a-zA-Z0-9_-]+$/), + + timestamp: z.string() + .datetime() + .refine( + (ts) => new Date(ts) <= new Date(), + 'Timestamp cannot be in the future' + ), + + temperature: z.number() + .min(-50) + .max(100) + .optional(), + + humidity: z.number() + .min(0) + .max(100) + .optional(), + + battery: z.number() + .min(0) + .max(100) + .optional() +}); + +// Schema für Device Commands +const DeviceCommandSchema = z.object({ + action: z.enum(['restart', 'update', 'configure', 'reset']), + target: z.string().min(1).max(64), + payload: z.record(z.unknown()).optional() +}).refine( + (data) => { + // Dangerous actions require additional validation + if (data.action === 'reset') { + return data.payload?.confirm === true; + } + return true; + }, + 'Reset action requires confirmation' +); + +// Middleware für Input Validation +import { Request, Response, NextFunction } from 'express'; + +function validateInput(schema: z.ZodType) { + return (req: Request, res: Response, next: NextFunction) => { + try { + req.body = schema.parse(req.body); + next(); + } catch (error) { + if (error instanceof z.ZodError) { + return res.status(400).json({ + error: 'Validation failed', + details: error.errors + }); + } + next(error); + } + }; +} + +// Rate Limiting +import rateLimit from 'express-rate-limit'; + +const deviceRateLimit = rateLimit({ + windowMs: 60 * 1000, // 1 Minute + max: 60, // 60 Requests pro Minute + keyGenerator: (req) => req.headers['x-device-id'] as string || req.ip, + handler: (req, res) => { + res.status(429).json({ + error: 'Rate limit exceeded', + retryAfter: 60 + }); + } +}); +``` + +--- + +## Security Monitoring + +```typescript +// lib/security-monitoring.ts + +interface SecurityEvent { + timestamp: Date; + eventType: string; + severity: 'low' | 'medium' | 'high' | 'critical'; + deviceId?: string; + sourceIp?: string; + details: Record; +} + +class SecurityMonitor { + private events: SecurityEvent[] = []; + + // Authentication Failure + logAuthFailure(deviceId: string, reason: string, sourceIp: string): void { + this.logEvent({ + eventType: 'auth_failure', + severity: 'medium', + deviceId, + sourceIp, + details: { reason } + }); + + // Check for brute force + this.checkBruteForce(deviceId, sourceIp); + } + + // Anomaly Detection + logAnomaly(deviceId: string, anomalyType: string, value: any): void { + this.logEvent({ + eventType: 'anomaly', + severity: 'medium', + deviceId, + details: { anomalyType, value } + }); + } + + // Firmware Tampering + logTamperAttempt(deviceId: string, details: Record): void { + this.logEvent({ + eventType: 'tamper_attempt', + severity: 'critical', + deviceId, + details + }); + + // Immediate alert + this.sendAlert({ + type: 'tamper_attempt', + deviceId, + details + }); + } + + private logEvent(event: Omit): void { + const fullEvent: SecurityEvent = { + ...event, + timestamp: new Date() + }; + + this.events.push(fullEvent); + + // In production: send to SIEM + console.log('Security Event:', JSON.stringify(fullEvent)); + } + + private checkBruteForce(deviceId: string, sourceIp: string): void { + const recentFailures = this.events.filter(e => + e.eventType === 'auth_failure' && + e.timestamp > new Date(Date.now() - 5 * 60 * 1000) && + (e.deviceId === deviceId || e.sourceIp === sourceIp) + ); + + if (recentFailures.length >= 5) { + this.logEvent({ + eventType: 'brute_force_detected', + severity: 'high', + deviceId, + sourceIp, + details: { failureCount: recentFailures.length } + }); + + // Block IP/Device + this.blockSource(sourceIp, deviceId); + } + } + + private blockSource(ip: string, deviceId: string): void { + // Implement blocking logic + console.log(`Blocking IP: ${ip}, Device: ${deviceId}`); + } + + private sendAlert(alert: Record): void { + // Send to Slack, PagerDuty, etc. + console.log('ALERT:', alert); + } +} +``` + +--- + +## Security Checklist + +| Category | Requirement | Priority | +|----------|------------|----------| +| **Authentication** | Certificate-based device auth | High | +| **Authentication** | No default credentials | Critical | +| **Encryption** | TLS 1.3 for all communications | High | +| **Encryption** | Data at rest encryption | High | +| **Firmware** | Signed firmware updates | Critical | +| **Firmware** | Secure boot chain | High | +| **Network** | Network segmentation | Medium | +| **Network** | Firewall rules | High | +| **Monitoring** | Security event logging | High | +| **Monitoring** | Anomaly detection | Medium | +| **Compliance** | Regular security audits | High | + +--- + +## Fazit + +IoT Security erfordert: + +1. **Defense in Depth**: Mehrere Sicherheitsebenen +2. **Strong Authentication**: Zertifikate statt Passwörter +3. **Encryption**: TLS everywhere +4. **Secure Updates**: Signierte Firmware + +Sicherheit ist keine Option – sie ist Pflicht. + +--- + +## Bildprompts + +1. "IoT security layers diagram, defense in depth concept" +2. "Secure device authentication flow, certificate exchange" +3. "Security monitoring dashboard with alerts and events" + +--- + +## Quellen + +- [IoT Device Security Best Practices 2026](https://www.jaycon.com/iot-device-security-best-practices/) +- [ConnectWise IoT Security Guide](https://www.connectwise.com/blog/how-to-secure-iot-devices) +- [Fortinet IoT Best Practices](https://www.fortinet.com/resources/cyberglossary/iot-best-practices) +- [Azure IoT Security](https://learn.microsoft.com/en-us/azure/iot/iot-overview-security) diff --git a/blog-posts/69-smart-home-dashboard.md b/blog-posts/69-smart-home-dashboard.md new file mode 100644 index 0000000..1a63d32 --- /dev/null +++ b/blog-posts/69-smart-home-dashboard.md @@ -0,0 +1,809 @@ +# Smart Home Dashboard Development + +**Meta-Description:** Smart Home Dashboard mit React und WebSocket. Real-time Updates, Device Control und responsive Design für die Heimautomatisierung. + +**Keywords:** Smart Home Dashboard, React, WebSocket, Home Automation, Real-time UI, Device Control, IoT Dashboard + +--- + +## Einführung + +Ein **Smart Home Dashboard** ist die zentrale Steuerung für alle vernetzten Geräte. Mit **React**, **WebSocket** und modernem UI-Design bauen wir ein responsives Dashboard für Echtzeit-Monitoring und Gerätesteuerung. + +--- + +## Dashboard Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SMART HOME DASHBOARD ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Frontend: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ React + Next.js │ │ +│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ +│ │ │ Device │ │ Room │ │ Scenes │ │ │ +│ │ │ Cards │ │ View │ │ Manager │ │ │ +│ │ └───────────┘ └───────────┘ └───────────┘ │ │ +│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ +│ │ │ Energy │ │ Climate │ │ Security │ │ │ +│ │ │ Monitor │ │ Control │ │ Panel │ │ │ +│ │ └───────────┘ └───────────┘ └───────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ WebSocket / REST │ +│ │ │ +│ Backend: ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ API Gateway / BFF │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌────────────┬───────────┴───────────┬────────────┐ │ +│ │ MQTT │ Home Assistant │ Database │ │ +│ │ Broker │ API │ (States) │ │ +│ └────────────┴───────────────────────┴────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## React Dashboard Components + +```typescript +// components/dashboard/DeviceCard.tsx +'use client'; + +import { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Switch } from '@/components/ui/switch'; +import { Slider } from '@/components/ui/slider'; +import { cn } from '@/lib/utils'; + +interface DeviceCardProps { + device: { + id: string; + name: string; + type: 'light' | 'switch' | 'sensor' | 'thermostat'; + state: 'on' | 'off'; + brightness?: number; + temperature?: number; + humidity?: number; + setpoint?: number; + }; + onStateChange: (id: string, state: Partial) => void; +} + +export function DeviceCard({ device, onStateChange }: DeviceCardProps) { + const [isLoading, setIsLoading] = useState(false); + + const handleToggle = async (checked: boolean) => { + setIsLoading(true); + await onStateChange(device.id, { state: checked ? 'on' : 'off' }); + setIsLoading(false); + }; + + const handleBrightness = async (value: number[]) => { + await onStateChange(device.id, { brightness: value[0] }); + }; + + const handleSetpoint = async (value: number[]) => { + await onStateChange(device.id, { setpoint: value[0] }); + }; + + return ( + + + {device.name} + {(device.type === 'light' || device.type === 'switch') && ( + + )} + + + + {/* Light with Brightness */} + {device.type === 'light' && device.brightness !== undefined && ( +
+
+ Brightness + {device.brightness}% +
+ +
+ )} + + {/* Sensor Readings */} + {device.type === 'sensor' && ( +
+ {device.temperature !== undefined && ( +
+
{device.temperature}°C
+
Temperature
+
+ )} + {device.humidity !== undefined && ( +
+
{device.humidity}%
+
Humidity
+
+ )} +
+ )} + + {/* Thermostat */} + {device.type === 'thermostat' && ( +
+
+
{device.temperature}°C
+
Current
+
+
+
+ Setpoint + {device.setpoint}°C +
+ +
+
+ )} +
+
+ ); +} +``` + +--- + +## Room View + +```typescript +// components/dashboard/RoomView.tsx +'use client'; + +import { useState, useEffect } from 'react'; +import { DeviceCard } from './DeviceCard'; +import { useSmartHome } from '@/hooks/useSmartHome'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { + Home, + Sofa, + Bed, + UtensilsCrossed, + Bath, + TreeDeciduous +} from 'lucide-react'; + +const roomIcons: Record = { + all: Home, + living_room: Sofa, + bedroom: Bed, + kitchen: UtensilsCrossed, + bathroom: Bath, + outdoor: TreeDeciduous +}; + +export function RoomView() { + const { devices, rooms, updateDevice, isConnected } = useSmartHome(); + const [activeRoom, setActiveRoom] = useState('all'); + + const filteredDevices = activeRoom === 'all' + ? devices + : devices.filter(d => d.room === activeRoom); + + return ( +
+ {/* Connection Status */} +
+
+ + {isConnected ? 'Connected' : 'Disconnected'} + +
+ + {/* Room Tabs */} + + + + + All + + {rooms.map(room => { + const Icon = roomIcons[room.id] || Home; + return ( + + + {room.name} + + ); + })} + + + +
+ {filteredDevices.map(device => ( + + ))} +
+ + {filteredDevices.length === 0 && ( +
+ No devices in this room +
+ )} +
+
+
+ ); +} +``` + +--- + +## WebSocket Hook + +```typescript +// hooks/useSmartHome.ts +'use client'; + +import { useState, useEffect, useCallback, useRef } from 'react'; + +interface Device { + id: string; + name: string; + type: 'light' | 'switch' | 'sensor' | 'thermostat'; + room: string; + state: 'on' | 'off'; + brightness?: number; + temperature?: number; + humidity?: number; + setpoint?: number; + lastUpdated: string; +} + +interface Room { + id: string; + name: string; +} + +interface SmartHomeState { + devices: Device[]; + rooms: Room[]; + isConnected: boolean; +} + +export function useSmartHome() { + const [state, setState] = useState({ + devices: [], + rooms: [], + isConnected: false + }); + + const wsRef = useRef(null); + const reconnectTimeoutRef = useRef(); + + const connect = useCallback(() => { + const wsUrl = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:3001'; + const ws = new WebSocket(wsUrl); + + ws.onopen = () => { + console.log('WebSocket connected'); + setState(prev => ({ ...prev, isConnected: true })); + + // Request initial state + ws.send(JSON.stringify({ type: 'get_state' })); + }; + + ws.onmessage = (event) => { + const message = JSON.parse(event.data); + + switch (message.type) { + case 'initial_state': + setState(prev => ({ + ...prev, + devices: message.devices, + rooms: message.rooms + })); + break; + + case 'device_update': + setState(prev => ({ + ...prev, + devices: prev.devices.map(d => + d.id === message.device.id + ? { ...d, ...message.device, lastUpdated: new Date().toISOString() } + : d + ) + })); + break; + + case 'device_added': + setState(prev => ({ + ...prev, + devices: [...prev.devices, message.device] + })); + break; + + case 'device_removed': + setState(prev => ({ + ...prev, + devices: prev.devices.filter(d => d.id !== message.deviceId) + })); + break; + } + }; + + ws.onclose = () => { + console.log('WebSocket disconnected'); + setState(prev => ({ ...prev, isConnected: false })); + + // Reconnect after 5 seconds + reconnectTimeoutRef.current = setTimeout(connect, 5000); + }; + + ws.onerror = (error) => { + console.error('WebSocket error:', error); + }; + + wsRef.current = ws; + }, []); + + useEffect(() => { + connect(); + + return () => { + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current); + } + wsRef.current?.close(); + }; + }, [connect]); + + const updateDevice = useCallback(async ( + deviceId: string, + updates: Partial + ) => { + // Optimistic Update + setState(prev => ({ + ...prev, + devices: prev.devices.map(d => + d.id === deviceId ? { ...d, ...updates } : d + ) + })); + + // Send to server + wsRef.current?.send(JSON.stringify({ + type: 'update_device', + deviceId, + updates + })); + }, []); + + const executeScene = useCallback((sceneId: string) => { + wsRef.current?.send(JSON.stringify({ + type: 'execute_scene', + sceneId + })); + }, []); + + return { + ...state, + updateDevice, + executeScene + }; +} +``` + +--- + +## Scene Manager + +```typescript +// components/dashboard/SceneManager.tsx +'use client'; + +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { + Sun, + Moon, + Film, + Coffee, + PartyPopper, + Power +} from 'lucide-react'; + +interface Scene { + id: string; + name: string; + icon: string; + description: string; +} + +const defaultScenes: Scene[] = [ + { id: 'morning', name: 'Good Morning', icon: 'sun', description: 'Wake up routine' }, + { id: 'movie', name: 'Movie Time', icon: 'film', description: 'Dim lights for movies' }, + { id: 'goodnight', name: 'Good Night', icon: 'moon', description: 'All lights off' }, + { id: 'party', name: 'Party Mode', icon: 'party', description: 'Colorful lights' }, + { id: 'away', name: 'Away', icon: 'power', description: 'Energy saving mode' }, + { id: 'coffee', name: 'Coffee Break', icon: 'coffee', description: 'Cozy atmosphere' } +]; + +const iconMap: Record = { + sun: Sun, + moon: Moon, + film: Film, + coffee: Coffee, + party: PartyPopper, + power: Power +}; + +interface SceneManagerProps { + onExecute: (sceneId: string) => void; +} + +export function SceneManager({ onExecute }: SceneManagerProps) { + const [activeScene, setActiveScene] = useState(null); + const [isExecuting, setIsExecuting] = useState(false); + + const handleExecute = async (sceneId: string) => { + setIsExecuting(true); + setActiveScene(sceneId); + + await onExecute(sceneId); + + setTimeout(() => { + setIsExecuting(false); + }, 1000); + }; + + return ( + + + Scenes + + +
+ {defaultScenes.map(scene => { + const Icon = iconMap[scene.icon] || Sun; + const isActive = activeScene === scene.id; + + return ( + + ); + })} +
+
+
+ ); +} +``` + +--- + +## Climate Control + +```typescript +// components/dashboard/ClimateControl.tsx +'use client'; + +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Thermometer, Droplets, Wind, Plus, Minus } from 'lucide-react'; + +interface ClimateControlProps { + temperature: number; + humidity: number; + setpoint: number; + mode: 'heat' | 'cool' | 'auto' | 'off'; + onSetpointChange: (value: number) => void; + onModeChange: (mode: string) => void; +} + +export function ClimateControl({ + temperature, + humidity, + setpoint, + mode, + onSetpointChange, + onModeChange +}: ClimateControlProps) { + const adjustSetpoint = (delta: number) => { + const newValue = Math.max(16, Math.min(28, setpoint + delta)); + onSetpointChange(newValue); + }; + + return ( + + + + + Climate + + + + {/* Current Values */} +
+
+ +
{temperature}°C
+
Current
+
+
+ +
{humidity}%
+
Humidity
+
+
+ + {/* Setpoint Control */} +
+ + +
+
{setpoint}°C
+
Target
+
+ + +
+ + {/* Mode Selection */} +
+ {['heat', 'cool', 'auto', 'off'].map(m => ( + + ))} +
+
+
+ ); +} +``` + +--- + +## Energy Monitor + +```typescript +// components/dashboard/EnergyMonitor.tsx +'use client'; + +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Progress } from '@/components/ui/progress'; +import { Zap, TrendingDown, TrendingUp } from 'lucide-react'; + +interface EnergyData { + currentPower: number; // Watts + todayUsage: number; // kWh + monthlyUsage: number; // kWh + monthlyBudget: number; // kWh + trend: 'up' | 'down' | 'stable'; + trendPercent: number; +} + +interface EnergyMonitorProps { + data: EnergyData; +} + +export function EnergyMonitor({ data }: EnergyMonitorProps) { + const budgetUsage = (data.monthlyUsage / data.monthlyBudget) * 100; + const TrendIcon = data.trend === 'up' ? TrendingUp : TrendingDown; + + return ( + + + + + Energy + + + + {/* Current Power */} +
+
+ {data.currentPower.toLocaleString()} W +
+
Current Power
+
+ + {/* Usage Stats */} +
+
+
+ {data.todayUsage.toFixed(1)} kWh +
+
Today
+
+
+
+ {data.monthlyUsage.toFixed(0)} kWh +
+
This Month
+
+
+ + {/* Budget Progress */} +
+
+ Monthly Budget + {budgetUsage.toFixed(0)}% +
+ +
+ {data.monthlyUsage.toFixed(0)} / {data.monthlyBudget} kWh +
+
+ + {/* Trend */} +
+ + + {data.trend === 'up' ? '+' : '-'}{data.trendPercent}% + + vs last week +
+
+
+ ); +} +``` + +--- + +## Main Dashboard Layout + +```typescript +// app/dashboard/page.tsx +'use client'; + +import { RoomView } from '@/components/dashboard/RoomView'; +import { SceneManager } from '@/components/dashboard/SceneManager'; +import { ClimateControl } from '@/components/dashboard/ClimateControl'; +import { EnergyMonitor } from '@/components/dashboard/EnergyMonitor'; +import { useSmartHome } from '@/hooks/useSmartHome'; + +export default function DashboardPage() { + const { devices, executeScene, updateDevice } = useSmartHome(); + + // Aggregate climate data + const climateDevice = devices.find(d => d.type === 'thermostat'); + + // Mock energy data (in production: from API) + const energyData = { + currentPower: 1234, + todayUsage: 12.5, + monthlyUsage: 245, + monthlyBudget: 300, + trend: 'down' as const, + trendPercent: 8 + }; + + return ( +
+
+

Smart Home

+

Welcome back!

+
+ +
+ {/* Main Area - Devices */} +
+ +
+ + {/* Sidebar */} +
+ + + {climateDevice && ( + + updateDevice(climateDevice.id, { setpoint: value }) + } + onModeChange={(mode) => + console.log('Mode:', mode) + } + /> + )} + + +
+
+
+ ); +} +``` + +--- + +## Fazit + +Smart Home Dashboard mit React bietet: + +1. **Real-time Updates**: WebSocket für Live-Daten +2. **Intuitive UI**: Room-basierte Navigation +3. **Responsive Design**: Desktop & Mobile +4. **Szenen & Automation**: One-Click Control + +Das perfekte Interface für Smart Home Control. + +--- + +## Bildprompts + +1. "Smart home dashboard on tablet, dark mode with glowing device cards" +2. "Room view with connected devices, modern UI design" +3. "Energy monitoring graph with real-time power consumption" + +--- + +## Quellen + +- [React Documentation](https://react.dev/) +- [shadcn/ui Components](https://ui.shadcn.com/) +- [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) +- [Home Assistant Dashboard](https://www.home-assistant.io/dashboards/) diff --git a/blog-posts/70-edge-computing-iot.md b/blog-posts/70-edge-computing-iot.md new file mode 100644 index 0000000..fabf565 --- /dev/null +++ b/blog-posts/70-edge-computing-iot.md @@ -0,0 +1,823 @@ +# Edge Computing für IoT + +**Meta-Description:** Edge Computing für IoT-Anwendungen. Lokale Datenverarbeitung, ML Inference, Fog Computing und Edge-Cloud Architektur. + +**Keywords:** Edge Computing, IoT, Fog Computing, ML Inference, Local Processing, Edge Gateway, TensorFlow Lite + +--- + +## Einführung + +**Edge Computing** bringt Rechenleistung dorthin, wo Daten entstehen. Für IoT bedeutet das: **niedrige Latenz, Offline-Fähigkeit, Datenschutz** und reduzierte Bandbreite durch lokale Verarbeitung direkt am Gerät oder Gateway. + +--- + +## Edge Computing Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ EDGE COMPUTING ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Cloud Layer: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Cloud Platform (AWS IoT / Azure IoT / GCP IoT) │ │ +│ │ - Long-term Storage │ │ +│ │ - Complex Analytics │ │ +│ │ - ML Model Training │ │ +│ │ - Global Dashboards │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ ↑↓ Aggregated Data │ +│ Edge Layer (Fog): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Edge Gateway / Server │ │ +│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ +│ │ │ Local DB │ │ ML Infer- │ │ Rules │ │ │ +│ │ │ (InfluxDB)│ │ ence │ │ Engine │ │ │ +│ │ └───────────┘ └───────────┘ └───────────┘ │ │ +│ │ - Data Aggregation │ │ +│ │ - Real-time Processing │ │ +│ │ - Local Decisions │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ ↑↓ Filtered Data │ +│ Device Layer: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────┐ │ │ +│ │ │ Sensor │ │ Camera │ │ Actuator│ │ PLC │ │ │ +│ │ │ │ │ (CV) │ │ │ │ │ │ │ +│ │ └─────────┘ └─────────┘ └─────────┘ └───────┘ │ │ +│ │ - Data Collection │ │ +│ │ - Simple Processing │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Latency: Cloud ~100ms | Edge ~10ms | Device ~1ms │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Edge Gateway mit Node.js + +```typescript +// edge-gateway/index.ts +import mqtt from 'mqtt'; +import { InfluxDBClient } from './lib/influxdb'; +import { RulesEngine } from './lib/rules-engine'; +import { MLInference } from './lib/ml-inference'; + +interface EdgeConfig { + mqttBroker: string; + cloudEndpoint: string; + localDb: { + url: string; + token: string; + org: string; + bucket: string; + }; + syncInterval: number; // Millisekunden + offlineBufferSize: number; +} + +class EdgeGateway { + private localMqtt: mqtt.MqttClient; + private cloudMqtt: mqtt.MqttClient | null = null; + private influx: InfluxDBClient; + private rules: RulesEngine; + private ml: MLInference; + private offlineBuffer: any[] = []; + private isCloudConnected = false; + + constructor(private config: EdgeConfig) { + this.influx = new InfluxDBClient(config.localDb); + this.rules = new RulesEngine(); + this.ml = new MLInference(); + } + + async start(): Promise { + // Local MQTT Broker verbinden + this.localMqtt = mqtt.connect(this.config.mqttBroker); + + this.localMqtt.on('connect', () => { + console.log('Connected to local MQTT broker'); + this.localMqtt.subscribe('sensors/#'); + this.localMqtt.subscribe('devices/#'); + }); + + this.localMqtt.on('message', (topic, payload) => { + this.handleLocalMessage(topic, payload.toString()); + }); + + // Cloud Connection (mit Retry) + this.connectToCloud(); + + // Periodische Cloud-Synchronisation + setInterval(() => this.syncToCloud(), this.config.syncInterval); + + // ML Models laden + await this.ml.loadModels(); + + console.log('Edge Gateway started'); + } + + private async handleLocalMessage(topic: string, payload: string): Promise { + try { + const data = JSON.parse(payload); + const timestamp = new Date(); + + // 1. Lokale Speicherung + await this.storeLocally(topic, data, timestamp); + + // 2. Echtzeit-Verarbeitung + const processed = await this.processData(topic, data); + + // 3. Rules Engine + const actions = this.rules.evaluate(topic, processed); + await this.executeActions(actions); + + // 4. ML Inference (wenn relevant) + if (this.shouldRunInference(topic, data)) { + const prediction = await this.ml.predict(data); + await this.handlePrediction(topic, prediction); + } + + // 5. Cloud-Queue (aggregierte Daten) + this.queueForCloud(topic, processed, timestamp); + + } catch (error) { + console.error('Error processing message:', error); + } + } + + private async storeLocally( + topic: string, + data: any, + timestamp: Date + ): Promise { + const measurement = topic.split('/')[0]; + const deviceId = topic.split('/')[1]; + + await this.influx.writePoint({ + measurement, + tags: { device_id: deviceId }, + fields: data, + timestamp + }); + } + + private async processData(topic: string, data: any): Promise { + // Daten normalisieren, filtern, aggregieren + const processed = { ...data }; + + // Outlier Detection + if (data.temperature !== undefined) { + if (data.temperature < -50 || data.temperature > 100) { + processed.temperature_valid = false; + processed.temperature_original = data.temperature; + delete processed.temperature; + } + } + + // Unit Conversion (wenn nötig) + // ... + + return processed; + } + + private queueForCloud(topic: string, data: any, timestamp: Date): void { + // Nur relevante Daten für Cloud + const cloudData = { + topic, + data: this.aggregateForCloud(data), + timestamp: timestamp.toISOString(), + gatewayId: process.env.GATEWAY_ID + }; + + if (this.isCloudConnected) { + this.sendToCloud(cloudData); + } else { + // Offline Buffer + this.offlineBuffer.push(cloudData); + + // Buffer Limit + if (this.offlineBuffer.length > this.config.offlineBufferSize) { + this.offlineBuffer.shift(); // Älteste Daten verwerfen + } + } + } + + private aggregateForCloud(data: any): any { + // Nur wichtige Felder für Cloud + const { temperature, humidity, battery, state } = data; + return { temperature, humidity, battery, state }; + } + + private async syncToCloud(): Promise { + if (!this.isCloudConnected || this.offlineBuffer.length === 0) { + return; + } + + console.log(`Syncing ${this.offlineBuffer.length} messages to cloud`); + + // Batch Upload + const batch = this.offlineBuffer.splice(0, 100); + + try { + await this.sendToCloud({ + type: 'batch', + messages: batch + }); + } catch (error) { + // Bei Fehler: zurück in Buffer + this.offlineBuffer.unshift(...batch); + console.error('Cloud sync failed:', error); + } + } + + private sendToCloud(data: any): void { + this.cloudMqtt?.publish( + `gateways/${process.env.GATEWAY_ID}/data`, + JSON.stringify(data) + ); + } + + private async executeActions(actions: any[]): Promise { + for (const action of actions) { + console.log('Executing action:', action); + + switch (action.type) { + case 'publish': + this.localMqtt.publish(action.topic, JSON.stringify(action.payload)); + break; + + case 'alert': + await this.sendAlert(action.message, action.severity); + break; + + case 'device_command': + this.localMqtt.publish( + `devices/${action.deviceId}/command`, + JSON.stringify(action.command) + ); + break; + } + } + } + + private connectToCloud(): void { + this.cloudMqtt = mqtt.connect(this.config.cloudEndpoint, { + clientId: `gateway-${process.env.GATEWAY_ID}`, + username: process.env.CLOUD_USER, + password: process.env.CLOUD_PASS, + reconnectPeriod: 10000 + }); + + this.cloudMqtt.on('connect', () => { + console.log('Connected to cloud'); + this.isCloudConnected = true; + + // Subscribe to cloud commands + this.cloudMqtt?.subscribe(`gateways/${process.env.GATEWAY_ID}/command`); + }); + + this.cloudMqtt.on('close', () => { + console.log('Cloud connection closed'); + this.isCloudConnected = false; + }); + + this.cloudMqtt.on('message', (topic, payload) => { + this.handleCloudCommand(JSON.parse(payload.toString())); + }); + } + + private handleCloudCommand(command: any): void { + console.log('Cloud command received:', command); + + switch (command.type) { + case 'update_config': + this.updateConfig(command.config); + break; + + case 'update_rules': + this.rules.updateRules(command.rules); + break; + + case 'update_model': + this.ml.updateModel(command.modelId, command.modelUrl); + break; + + case 'device_command': + this.localMqtt.publish( + `devices/${command.deviceId}/command`, + JSON.stringify(command.payload) + ); + break; + } + } +} +``` + +--- + +## ML Inference am Edge + +```typescript +// lib/ml-inference.ts +import * as tf from '@tensorflow/tfjs-node'; + +interface Model { + id: string; + model: tf.LayersModel; + inputShape: number[]; + labels: string[]; +} + +class MLInference { + private models: Map = new Map(); + + async loadModels(): Promise { + // Anomaly Detection Model + const anomalyModel = await tf.loadLayersModel( + 'file://./models/anomaly/model.json' + ); + this.models.set('anomaly', { + id: 'anomaly', + model: anomalyModel, + inputShape: [1, 10], // 10 Features + labels: ['normal', 'anomaly'] + }); + + // Predictive Maintenance Model + const maintenanceModel = await tf.loadLayersModel( + 'file://./models/maintenance/model.json' + ); + this.models.set('maintenance', { + id: 'maintenance', + model: maintenanceModel, + inputShape: [1, 24], // 24 Stunden Historie + labels: ['ok', 'warning', 'critical'] + }); + + console.log(`Loaded ${this.models.size} ML models`); + } + + async predict( + modelId: string, + input: number[] + ): Promise<{ label: string; confidence: number }> { + const modelInfo = this.models.get(modelId); + if (!modelInfo) { + throw new Error(`Model ${modelId} not found`); + } + + // Input Tensor erstellen + const inputTensor = tf.tensor2d([input], modelInfo.inputShape as [number, number]); + + // Inference + const prediction = modelInfo.model.predict(inputTensor) as tf.Tensor; + const probabilities = await prediction.data(); + + // Cleanup + inputTensor.dispose(); + prediction.dispose(); + + // Beste Prediction finden + const maxIndex = probabilities.indexOf(Math.max(...Array.from(probabilities))); + + return { + label: modelInfo.labels[maxIndex], + confidence: probabilities[maxIndex] + }; + } + + async detectAnomaly( + sensorData: { + temperature: number; + humidity: number; + pressure: number; + vibration: number; + } + ): Promise<{ isAnomaly: boolean; confidence: number }> { + // Feature Engineering + const features = [ + sensorData.temperature, + sensorData.humidity, + sensorData.pressure, + sensorData.vibration, + // Normalisierte Werte + (sensorData.temperature - 20) / 30, + (sensorData.humidity - 50) / 50, + // Interaktionen + sensorData.temperature * sensorData.humidity / 1000, + // Placeholder für historische Features + 0, 0, 0 + ]; + + const result = await this.predict('anomaly', features); + + return { + isAnomaly: result.label === 'anomaly', + confidence: result.confidence + }; + } + + async updateModel(modelId: string, modelUrl: string): Promise { + console.log(`Updating model ${modelId} from ${modelUrl}`); + + try { + const newModel = await tf.loadLayersModel(modelUrl); + const existing = this.models.get(modelId); + + if (existing) { + existing.model.dispose(); + existing.model = newModel; + } + + console.log(`Model ${modelId} updated successfully`); + } catch (error) { + console.error(`Failed to update model ${modelId}:`, error); + } + } +} + +export { MLInference }; +``` + +--- + +## Rules Engine + +```typescript +// lib/rules-engine.ts + +interface Rule { + id: string; + name: string; + condition: RuleCondition; + actions: RuleAction[]; + enabled: boolean; + priority: number; +} + +interface RuleCondition { + type: 'simple' | 'compound'; + field?: string; + operator?: '>' | '<' | '==' | '!=' | '>=' | '<='; + value?: any; + logic?: 'AND' | 'OR'; + conditions?: RuleCondition[]; +} + +interface RuleAction { + type: 'publish' | 'alert' | 'device_command' | 'log'; + [key: string]: any; +} + +class RulesEngine { + private rules: Rule[] = []; + + constructor() { + this.loadDefaultRules(); + } + + private loadDefaultRules(): void { + this.rules = [ + // High Temperature Alert + { + id: 'high-temp-alert', + name: 'High Temperature Alert', + condition: { + type: 'simple', + field: 'temperature', + operator: '>', + value: 35 + }, + actions: [ + { + type: 'alert', + message: 'High temperature detected', + severity: 'warning' + }, + { + type: 'publish', + topic: 'alerts/temperature', + payload: { alert: 'high_temperature' } + } + ], + enabled: true, + priority: 1 + }, + + // Low Battery Warning + { + id: 'low-battery', + name: 'Low Battery Warning', + condition: { + type: 'simple', + field: 'battery', + operator: '<', + value: 20 + }, + actions: [ + { + type: 'alert', + message: 'Device battery low', + severity: 'info' + } + ], + enabled: true, + priority: 2 + }, + + // Motion → Light On + { + id: 'motion-light', + name: 'Motion Activated Light', + condition: { + type: 'compound', + logic: 'AND', + conditions: [ + { type: 'simple', field: 'occupancy', operator: '==', value: true }, + { type: 'simple', field: 'ambient_light', operator: '<', value: 100 } + ] + }, + actions: [ + { + type: 'device_command', + deviceId: 'hallway_light', + command: { state: 'ON', brightness: 80 } + } + ], + enabled: true, + priority: 1 + } + ]; + } + + evaluate(topic: string, data: any): RuleAction[] { + const actions: RuleAction[] = []; + + // Regeln nach Priorität sortieren + const sortedRules = [...this.rules] + .filter(r => r.enabled) + .sort((a, b) => a.priority - b.priority); + + for (const rule of sortedRules) { + if (this.evaluateCondition(rule.condition, data)) { + console.log(`Rule triggered: ${rule.name}`); + actions.push(...rule.actions); + } + } + + return actions; + } + + private evaluateCondition(condition: RuleCondition, data: any): boolean { + if (condition.type === 'simple') { + return this.evaluateSimple(condition, data); + } + + if (condition.type === 'compound' && condition.conditions) { + const results = condition.conditions.map(c => + this.evaluateCondition(c, data) + ); + + return condition.logic === 'AND' + ? results.every(r => r) + : results.some(r => r); + } + + return false; + } + + private evaluateSimple(condition: RuleCondition, data: any): boolean { + const value = data[condition.field!]; + + if (value === undefined) return false; + + switch (condition.operator) { + case '>': return value > condition.value; + case '<': return value < condition.value; + case '>=': return value >= condition.value; + case '<=': return value <= condition.value; + case '==': return value === condition.value; + case '!=': return value !== condition.value; + default: return false; + } + } + + updateRules(newRules: Rule[]): void { + this.rules = newRules; + console.log(`Updated ${newRules.length} rules`); + } + + addRule(rule: Rule): void { + this.rules.push(rule); + } + + removeRule(ruleId: string): void { + this.rules = this.rules.filter(r => r.id !== ruleId); + } + + getRules(): Rule[] { + return this.rules; + } +} + +export { RulesEngine }; +``` + +--- + +## Edge-Cloud Synchronisation + +```typescript +// lib/edge-cloud-sync.ts + +interface SyncConfig { + syncInterval: number; + batchSize: number; + retentionLocal: number; // Tage + compressionEnabled: boolean; +} + +class EdgeCloudSync { + private config: SyncConfig; + private pendingSync: any[] = []; + private lastSyncTime: Date | null = null; + + constructor(config: SyncConfig) { + this.config = config; + } + + // Daten für Cloud-Sync vorbereiten + prepareForSync(data: any[]): any { + // Aggregation + const aggregated = this.aggregate(data); + + // Kompression (optional) + if (this.config.compressionEnabled) { + return this.compress(aggregated); + } + + return aggregated; + } + + private aggregate(data: any[]): any { + // Nach Device gruppieren + const byDevice = new Map(); + + data.forEach(item => { + const deviceId = item.device_id; + if (!byDevice.has(deviceId)) { + byDevice.set(deviceId, []); + } + byDevice.get(deviceId)!.push(item); + }); + + // Aggregierte Statistiken pro Device + const result: any[] = []; + + byDevice.forEach((items, deviceId) => { + const temps = items + .map(i => i.temperature) + .filter(t => t !== undefined); + + const humidities = items + .map(i => i.humidity) + .filter(h => h !== undefined); + + result.push({ + device_id: deviceId, + period_start: items[0].timestamp, + period_end: items[items.length - 1].timestamp, + sample_count: items.length, + temperature: temps.length > 0 ? { + min: Math.min(...temps), + max: Math.max(...temps), + avg: temps.reduce((a, b) => a + b, 0) / temps.length + } : null, + humidity: humidities.length > 0 ? { + min: Math.min(...humidities), + max: Math.max(...humidities), + avg: humidities.reduce((a, b) => a + b, 0) / humidities.length + } : null + }); + }); + + return result; + } + + private compress(data: any): Buffer { + const zlib = require('zlib'); + const json = JSON.stringify(data); + return zlib.gzipSync(json); + } + + // Conflict Resolution bei Cloud-Sync + resolveConflict(localData: any, cloudData: any): any { + // Last-Write-Wins Strategie + const localTime = new Date(localData.updated_at); + const cloudTime = new Date(cloudData.updated_at); + + if (localTime > cloudTime) { + return { ...cloudData, ...localData, conflict_resolved: true }; + } + return { ...localData, ...cloudData, conflict_resolved: true }; + } +} +``` + +--- + +## Edge Deployment + +```yaml +# docker-compose.edge.yml +version: '3.8' +services: + edge-gateway: + build: ./edge-gateway + restart: always + environment: + - GATEWAY_ID=${GATEWAY_ID} + - MQTT_BROKER=mqtt://mosquitto:1883 + - CLOUD_ENDPOINT=${CLOUD_MQTT_URL} + - INFLUX_URL=http://influxdb:8086 + volumes: + - ./models:/app/models + - ./config:/app/config + depends_on: + - mosquitto + - influxdb + + mosquitto: + image: eclipse-mosquitto:2 + ports: + - "1883:1883" + volumes: + - mosquitto-data:/mosquitto/data + + influxdb: + image: influxdb:2.7 + ports: + - "8086:8086" + volumes: + - influxdb-data:/var/lib/influxdb2 + environment: + - DOCKER_INFLUXDB_INIT_MODE=setup + - DOCKER_INFLUXDB_INIT_USERNAME=admin + - DOCKER_INFLUXDB_INIT_PASSWORD=edgepassword + - DOCKER_INFLUXDB_INIT_ORG=edge + - DOCKER_INFLUXDB_INIT_BUCKET=sensors + +volumes: + mosquitto-data: + influxdb-data: +``` + +--- + +## Edge vs Cloud Comparison + +| Aspect | Edge Computing | Cloud Computing | +|--------|---------------|-----------------| +| **Latency** | 1-10 ms | 50-200 ms | +| **Bandwidth** | Minimal (local) | High (all data) | +| **Offline** | Fully operational | Limited | +| **Privacy** | Data stays local | Data in cloud | +| **Compute** | Limited | Unlimited | +| **Storage** | Limited | Unlimited | +| **Cost** | Hardware upfront | Pay-per-use | +| **ML Training** | Not practical | Ideal | +| **ML Inference** | Real-time | Batch | + +--- + +## Fazit + +Edge Computing für IoT bietet: + +1. **Low Latency**: Lokale Entscheidungen in Millisekunden +2. **Offline-Fähigkeit**: Funktioniert ohne Cloud +3. **Datenschutz**: Sensible Daten bleiben lokal +4. **Bandbreite**: Nur aggregierte Daten zur Cloud + +Die optimale Architektur kombiniert Edge und Cloud. + +--- + +## Bildprompts + +1. "Edge computing diagram with local processing near sensors" +2. "IoT gateway device processing data streams locally" +3. "Fog computing layers between devices and cloud" + +--- + +## Quellen + +- [AWS IoT Greengrass](https://aws.amazon.com/greengrass/) +- [Azure IoT Edge](https://azure.microsoft.com/en-us/products/iot-edge) +- [TensorFlow Lite for Edge](https://www.tensorflow.org/lite) +- [InfluxDB Edge Replication](https://www.influxdata.com/blog/simplify-industrial-iot-use-influxdb-edge-replication/) diff --git a/blog-posts/71-threejs-react-three-fiber.md b/blog-posts/71-threejs-react-three-fiber.md new file mode 100644 index 0000000..a7ee6a8 --- /dev/null +++ b/blog-posts/71-threejs-react-three-fiber.md @@ -0,0 +1,612 @@ +# Three.js & React Three Fiber für 3D Web + +**Meta-Description:** 3D Webentwicklung mit Three.js und React Three Fiber. WebGL/WebGPU, interaktive Szenen und Performance-Optimierung. + +**Keywords:** Three.js, React Three Fiber, 3D Web, WebGL, WebGPU, R3F, Drei, Interactive 3D + +--- + +## Einführung + +**Three.js** dominiert die 3D-Webentwicklung mit 270x mehr Downloads als Alternativen. **React Three Fiber** (R3F) bringt die deklarative React-Syntax in die 3D-Welt – perfekt für moderne Web-Projekte mit interaktiven Visualisierungen. + +--- + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ THREE.JS / R3F ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Application Layer: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ React Components / Hooks / State │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ R3F Layer: ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ React Three Fiber (React Renderer for Three.js) │ │ +│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ +│ │ │ Canvas │ │ Hooks │ │ Events │ │ │ +│ │ │ (Root) │ │ (Frame) │ │(Pointer)│ │ │ +│ │ └─────────┘ └─────────┘ └─────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ Drei (Helpers): │ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ OrbitControls | Environment | Text3D | useGLTF │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ Three.js Core: ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Scene | Camera | Renderer | Lights | Geometries │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ Graphics API: ▼ │ +│ ┌───────────────────┬────────────────────┐ │ +│ │ WebGL 2 │ WebGPU │ │ +│ │ (Supported) │ (2026 Ready) │ │ +│ └───────────────────┴────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Setup & Installation + +```bash +# React Three Fiber + Drei (Helpers) +npm install three @react-three/fiber @react-three/drei + +# TypeScript Types +npm install -D @types/three + +# Optional: Post-Processing, Physics +npm install @react-three/postprocessing +npm install @react-three/cannon +``` + +```typescript +// next.config.js (für Next.js) +const nextConfig = { + transpilePackages: ['three'], + webpack: (config) => { + config.externals.push({ + 'sharp': 'commonjs sharp', + 'canvas': 'commonjs canvas', + }); + return config; + } +}; +``` + +--- + +## Basic Scene mit R3F + +```tsx +// components/Scene.tsx +'use client'; + +import { Canvas } from '@react-three/fiber'; +import { OrbitControls, Environment, PerspectiveCamera } from '@react-three/drei'; +import { Suspense } from 'react'; + +export function Scene() { + return ( + + + + + }> + + + + + + + + + + ); +} + +function LoadingBox() { + return ( + + + + + ); +} +``` + +--- + +## Interaktive Objekte + +```tsx +// components/InteractiveObjects.tsx +'use client'; + +import { useRef, useState } from 'react'; +import { useFrame } from '@react-three/fiber'; +import { MeshWobbleMaterial, RoundedBox, Text } from '@react-three/drei'; +import * as THREE from 'three'; + +interface BoxProps { + position: [number, number, number]; +} + +export function RotatingBox({ position }: BoxProps) { + const meshRef = useRef(null); + const [hovered, setHovered] = useState(false); + const [clicked, setClicked] = useState(false); + + useFrame((state, delta) => { + if (meshRef.current) { + meshRef.current.rotation.x += delta * 0.5; + meshRef.current.rotation.y += delta * 0.3; + + // Hover Animation + const scale = hovered ? 1.2 : 1; + meshRef.current.scale.lerp(new THREE.Vector3(scale, scale, scale), 0.1); + } + }); + + return ( + setClicked(!clicked)} + onPointerOver={() => setHovered(true)} + onPointerOut={() => setHovered(false)} + > + + + + ); +} + +export function FloatingSphere({ position }: BoxProps) { + const meshRef = useRef(null); + + useFrame((state) => { + if (meshRef.current) { + // Floating Animation + meshRef.current.position.y = position[1] + Math.sin(state.clock.elapsedTime) * 0.3; + } + }); + + return ( + + + + + ); +} + +export function Floor() { + return ( + + + + + ); +} +``` + +--- + +## 3D Model Loading (GLTF) + +```tsx +// components/Model.tsx +'use client'; + +import { useRef, useEffect } from 'react'; +import { useGLTF, useAnimations, Clone } from '@react-three/drei'; +import { useFrame } from '@react-three/fiber'; +import * as THREE from 'three'; + +interface ModelProps { + url: string; + position?: [number, number, number]; + scale?: number; + animate?: boolean; +} + +export function Model({ url, position = [0, 0, 0], scale = 1, animate = true }: ModelProps) { + const group = useRef(null); + const { scene, animations } = useGLTF(url); + const { actions, names } = useAnimations(animations, group); + + useEffect(() => { + // Erste Animation abspielen + if (animate && names.length > 0) { + actions[names[0]]?.play(); + } + }, [actions, names, animate]); + + // Shadow Setup + useEffect(() => { + scene.traverse((child) => { + if (child instanceof THREE.Mesh) { + child.castShadow = true; + child.receiveShadow = true; + } + }); + }, [scene]); + + return ( + + + + ); +} + +// Preload für bessere Performance +useGLTF.preload('/models/robot.glb'); + +// Mit Draco Compression +export function CompressedModel({ url }: { url: string }) { + const { scene } = useGLTF(url, true); // true = Draco Loader + return ; +} +``` + +--- + +## Custom Shaders + +```tsx +// components/CustomShader.tsx +'use client'; + +import { useRef, useMemo } from 'react'; +import { useFrame, extend } from '@react-three/fiber'; +import { shaderMaterial } from '@react-three/drei'; +import * as THREE from 'three'; + +// Custom Shader Material +const GradientMaterial = shaderMaterial( + // Uniforms + { + uTime: 0, + uColor1: new THREE.Color('#ff0000'), + uColor2: new THREE.Color('#0000ff'), + uIntensity: 1.0 + }, + // Vertex Shader + ` + varying vec2 vUv; + varying vec3 vPosition; + + void main() { + vUv = uv; + vPosition = position; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + // Fragment Shader + ` + uniform float uTime; + uniform vec3 uColor1; + uniform vec3 uColor2; + uniform float uIntensity; + varying vec2 vUv; + varying vec3 vPosition; + + void main() { + // Animated Gradient + float mixValue = sin(vUv.y * 3.14159 + uTime) * 0.5 + 0.5; + vec3 color = mix(uColor1, uColor2, mixValue); + + // Wave Effect + float wave = sin(vPosition.x * 5.0 + uTime * 2.0) * 0.1; + color += wave * uIntensity; + + gl_FragColor = vec4(color, 1.0); + } + ` +); + +// Extend für JSX Support +extend({ GradientMaterial }); + +// TypeScript Declaration +declare global { + namespace JSX { + interface IntrinsicElements { + gradientMaterial: any; + } + } +} + +export function ShaderSphere() { + const materialRef = useRef(null); + + useFrame((state) => { + if (materialRef.current) { + materialRef.current.uTime = state.clock.elapsedTime; + } + }); + + return ( + + + + + ); +} +``` + +--- + +## Post-Processing Effects + +```tsx +// components/PostProcessing.tsx +'use client'; + +import { EffectComposer, Bloom, ChromaticAberration, Vignette } from '@react-three/postprocessing'; +import { BlendFunction } from 'postprocessing'; + +export function Effects() { + return ( + + {/* Bloom für leuchtende Effekte */} + + + {/* Chromatische Aberration */} + + + {/* Vignette */} + + + ); +} + +// Verwendung +function SceneWithEffects() { + return ( + + + + + ); +} +``` + +--- + +## Physics mit Cannon + +```tsx +// components/PhysicsScene.tsx +'use client'; + +import { Physics, usePlane, useBox, useSphere } from '@react-three/cannon'; +import { useRef } from 'react'; +import * as THREE from 'three'; + +export function PhysicsScene() { + return ( + + + + + + + ); +} + +function PhysicsGround() { + const [ref] = usePlane(() => ({ + rotation: [-Math.PI / 2, 0, 0], + position: [0, 0, 0] + })); + + return ( + + + + + ); +} + +function PhysicsBox({ position }: { position: [number, number, number] }) { + const [ref] = useBox(() => ({ + mass: 1, + position, + args: [1, 1, 1] + })); + + return ( + + + + + ); +} + +function PhysicsBall({ position }: { position: [number, number, number] }) { + const [ref] = useSphere(() => ({ + mass: 2, + position, + args: [0.5] + })); + + return ( + + + + + ); +} +``` + +--- + +## Performance Optimierung + +```tsx +// components/OptimizedScene.tsx +'use client'; + +import { useRef, useMemo } from 'react'; +import { useFrame } from '@react-three/fiber'; +import { Instances, Instance, Detailed, useGLTF } from '@react-three/drei'; +import * as THREE from 'three'; + +// Instancing für viele gleiche Objekte +export function InstancedCubes({ count = 1000 }) { + const positions = useMemo(() => { + return Array.from({ length: count }, () => ({ + position: [ + (Math.random() - 0.5) * 20, + (Math.random() - 0.5) * 20, + (Math.random() - 0.5) * 20 + ] as [number, number, number], + rotation: [ + Math.random() * Math.PI, + Math.random() * Math.PI, + Math.random() * Math.PI + ] as [number, number, number], + scale: 0.5 + Math.random() * 0.5 + })); + }, [count]); + + return ( + + + + {positions.map((props, i) => ( + + ))} + + ); +} + +// Level of Detail (LOD) +export function LODModel() { + return ( + + {/* Höchste Qualität (nah) */} + + + + + + {/* Mittlere Qualität */} + + + + + + {/* Niedrige Qualität */} + + + + + + {/* Sehr niedrige Qualität (weit) */} + + + + + + ); +} + +// Lazy Loading +import { lazy, Suspense } from 'react'; + +const HeavyModel = lazy(() => import('./HeavyModel')); + +export function LazyScene() { + return ( + }> + + + ); +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **Geometry** | Instancing für viele gleiche Objekte | +| **Textures** | Compressed (WebP), Power of 2 | +| **Models** | Draco compression, LOD | +| **Shadows** | Nur wo nötig, Shadow Map Size | +| **Animations** | useFrame statt setInterval | +| **State** | Zustand außerhalb des Render Loops | +| **Cleanup** | dispose() für Geometries/Materials | + +--- + +## Fazit + +Three.js + React Three Fiber bietet: + +1. **Deklarative Syntax**: React-Komponenten für 3D +2. **Ecosystem**: Drei, Cannon, Postprocessing +3. **WebGPU Ready**: Zukunftssicher mit WebGPU Support +4. **Performance**: Instancing, LOD, Lazy Loading + +Die beste Kombination für 3D im Web. + +--- + +## Bildprompts + +1. "Interactive 3D scene with rotating objects, React Three Fiber demo" +2. "WebGL shader effect with gradient colors, custom material" +3. "Physics simulation with falling objects, cannon.js integration" + +--- + +## Quellen + +- [React Three Fiber Documentation](https://docs.pmnd.rs/react-three-fiber) +- [Three.js Journey](https://threejs-journey.com/) +- [Drei Helpers](https://github.com/pmndrs/drei) +- [What's New in Three.js 2026](https://www.utsubo.com/blog/threejs-2026-what-changed) diff --git a/blog-posts/72-webgpu-3d-graphics.md b/blog-posts/72-webgpu-3d-graphics.md new file mode 100644 index 0000000..fda9876 --- /dev/null +++ b/blog-posts/72-webgpu-3d-graphics.md @@ -0,0 +1,634 @@ +# WebGPU: Next-Gen Browser Graphics + +**Meta-Description:** WebGPU für 3D-Grafik im Browser. Performance-Vergleich zu WebGL, Compute Shaders und Three.js WebGPU Renderer. + +**Keywords:** WebGPU, WebGL, 3D Graphics, Compute Shaders, GPU Programming, Three.js, Browser Graphics + +--- + +## Einführung + +**WebGPU** ist der Nachfolger von WebGL und bringt moderne GPU-Architektur in den Browser. Mit **Compute Shaders**, besserer Performance und Support in allen Major Browsern (seit 2025) ist WebGPU bereit für Production. + +--- + +## WebGPU vs WebGL + +``` +┌─────────────────────────────────────────────────────────────┐ +│ WEBGPU VS WEBGL COMPARISON │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Architecture: │ +│ │ +│ WebGL (2011): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ JavaScript │ │ +│ │ ↓ │ │ +│ │ OpenGL ES (State Machine) │ │ +│ │ ↓ │ │ +│ │ Driver Translation Layer │ │ +│ │ ↓ │ │ +│ │ GPU │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ WebGPU (2023): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ JavaScript / WASM │ │ +│ │ ↓ │ │ +│ │ WebGPU API (Modern, Low-Level) │ │ +│ │ ↓ │ │ +│ │ Native GPU APIs (Vulkan/Metal/D3D12) │ │ +│ │ ↓ │ │ +│ │ GPU │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Performance Comparison: │ +│ ├── Draw Calls: WebGPU 10x faster (Render Bundles) │ +│ ├── Compute: WebGPU has Compute Shaders (WebGL: none) │ +│ ├── Multi-threading: WebGPU supports parallel encoding │ +│ └── Memory: WebGPU explicit resource management │ +│ │ +│ Browser Support (2026): │ +│ ├── Chrome/Edge: ✅ (since April 2023) │ +│ ├── Firefox: ✅ (since July 2025) │ +│ ├── Safari: ✅ (since June 2025, Safari 26) │ +│ └── Mobile: Partial (Chrome Android, Safari iOS 26) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Basic WebGPU Setup + +```typescript +// lib/webgpu-setup.ts + +async function initWebGPU(): Promise<{ + device: GPUDevice; + context: GPUCanvasContext; + format: GPUTextureFormat; +}> { + // Check Support + if (!navigator.gpu) { + throw new Error('WebGPU not supported'); + } + + // Request Adapter + const adapter = await navigator.gpu.requestAdapter({ + powerPreference: 'high-performance' + }); + + if (!adapter) { + throw new Error('No GPU adapter found'); + } + + // Request Device + const device = await adapter.requestDevice({ + requiredFeatures: [], + requiredLimits: {} + }); + + // Canvas Context + const canvas = document.querySelector('canvas')!; + const context = canvas.getContext('webgpu')!; + + const format = navigator.gpu.getPreferredCanvasFormat(); + + context.configure({ + device, + format, + alphaMode: 'premultiplied' + }); + + return { device, context, format }; +} +``` + +--- + +## Triangle Rendering + +```typescript +// webgpu-triangle.ts + +const vertexShaderCode = /* wgsl */ ` + struct VertexOutput { + @builtin(position) position: vec4f, + @location(0) color: vec4f, + } + + @vertex + fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { + var positions = array( + vec2f( 0.0, 0.5), // Top + vec2f(-0.5, -0.5), // Bottom Left + vec2f( 0.5, -0.5) // Bottom Right + ); + + var colors = array( + vec4f(1.0, 0.0, 0.0, 1.0), // Red + vec4f(0.0, 1.0, 0.0, 1.0), // Green + vec4f(0.0, 0.0, 1.0, 1.0) // Blue + ); + + var output: VertexOutput; + output.position = vec4f(positions[vertexIndex], 0.0, 1.0); + output.color = colors[vertexIndex]; + return output; + } +`; + +const fragmentShaderCode = /* wgsl */ ` + @fragment + fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f { + return color; + } +`; + +async function renderTriangle() { + const { device, context, format } = await initWebGPU(); + + // Shader Module + const shaderModule = device.createShaderModule({ + code: vertexShaderCode + fragmentShaderCode + }); + + // Pipeline + const pipeline = device.createRenderPipeline({ + layout: 'auto', + vertex: { + module: shaderModule, + entryPoint: 'vertexMain' + }, + fragment: { + module: shaderModule, + entryPoint: 'fragmentMain', + targets: [{ format }] + }, + primitive: { + topology: 'triangle-list' + } + }); + + // Render Loop + function frame() { + const commandEncoder = device.createCommandEncoder(); + + const renderPass = commandEncoder.beginRenderPass({ + colorAttachments: [{ + view: context.getCurrentTexture().createView(), + clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1.0 }, + loadOp: 'clear', + storeOp: 'store' + }] + }); + + renderPass.setPipeline(pipeline); + renderPass.draw(3); // 3 vertices + renderPass.end(); + + device.queue.submit([commandEncoder.finish()]); + requestAnimationFrame(frame); + } + + frame(); +} +``` + +--- + +## Compute Shaders + +```typescript +// webgpu-compute.ts + +// Particle Simulation mit Compute Shader +const computeShaderCode = /* wgsl */ ` + struct Particle { + position: vec2f, + velocity: vec2f, + } + + struct SimParams { + deltaTime: f32, + gravity: f32, + } + + @group(0) @binding(0) var particles: array; + @group(0) @binding(1) var params: SimParams; + + @compute @workgroup_size(64) + fn main(@builtin(global_invocation_id) id: vec3u) { + let index = id.x; + if (index >= arrayLength(&particles)) { + return; + } + + var particle = particles[index]; + + // Apply Gravity + particle.velocity.y -= params.gravity * params.deltaTime; + + // Update Position + particle.position += particle.velocity * params.deltaTime; + + // Bounce off boundaries + if (particle.position.y < -1.0) { + particle.position.y = -1.0; + particle.velocity.y *= -0.8; // Energy loss + } + + if (abs(particle.position.x) > 1.0) { + particle.velocity.x *= -1.0; + } + + particles[index] = particle; + } +`; + +async function setupParticleSimulation( + device: GPUDevice, + particleCount: number +) { + // Initialize Particles + const particleData = new Float32Array(particleCount * 4); + for (let i = 0; i < particleCount; i++) { + particleData[i * 4 + 0] = (Math.random() - 0.5) * 2; // x + particleData[i * 4 + 1] = Math.random(); // y + particleData[i * 4 + 2] = (Math.random() - 0.5) * 0.1; // vx + particleData[i * 4 + 3] = 0; // vy + } + + // Particle Buffer + const particleBuffer = device.createBuffer({ + size: particleData.byteLength, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, + mappedAtCreation: true + }); + new Float32Array(particleBuffer.getMappedRange()).set(particleData); + particleBuffer.unmap(); + + // Uniform Buffer + const uniformBuffer = device.createBuffer({ + size: 8, // 2 x float32 + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST + }); + + // Compute Pipeline + const computeModule = device.createShaderModule({ code: computeShaderCode }); + + const computePipeline = device.createComputePipeline({ + layout: 'auto', + compute: { + module: computeModule, + entryPoint: 'main' + } + }); + + // Bind Group + const bindGroup = device.createBindGroup({ + layout: computePipeline.getBindGroupLayout(0), + entries: [ + { binding: 0, resource: { buffer: particleBuffer } }, + { binding: 1, resource: { buffer: uniformBuffer } } + ] + }); + + return { + particleBuffer, + uniformBuffer, + computePipeline, + bindGroup, + particleCount + }; +} + +function runComputePass( + device: GPUDevice, + simulation: Awaited>, + deltaTime: number +) { + const { uniformBuffer, computePipeline, bindGroup, particleCount } = simulation; + + // Update Uniforms + device.queue.writeBuffer( + uniformBuffer, + 0, + new Float32Array([deltaTime, 9.81]) + ); + + // Dispatch Compute + const commandEncoder = device.createCommandEncoder(); + const computePass = commandEncoder.beginComputePass(); + + computePass.setPipeline(computePipeline); + computePass.setBindGroup(0, bindGroup); + computePass.dispatchWorkgroups(Math.ceil(particleCount / 64)); + computePass.end(); + + device.queue.submit([commandEncoder.finish()]); +} +``` + +--- + +## Three.js WebGPU Renderer + +```typescript +// three-webgpu.ts +import * as THREE from 'three'; +import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js'; +import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + +async function initThreeWebGPU() { + // WebGPU Renderer + const renderer = new WebGPURenderer({ antialias: true }); + renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setPixelRatio(window.devicePixelRatio); + document.body.appendChild(renderer.domElement); + + // Initialize WebGPU + await renderer.init(); + + // Scene Setup + const scene = new THREE.Scene(); + scene.background = new THREE.Color(0x111111); + + // Camera + const camera = new THREE.PerspectiveCamera( + 75, + window.innerWidth / window.innerHeight, + 0.1, + 1000 + ); + camera.position.z = 5; + + // Controls + const controls = new OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + + // Lights + const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 1); + directionalLight.position.set(10, 10, 10); + scene.add(directionalLight); + + // Objects + const geometry = new THREE.TorusKnotGeometry(1, 0.3, 100, 16); + const material = new THREE.MeshStandardMaterial({ + color: 0x00ff88, + metalness: 0.5, + roughness: 0.2 + }); + const mesh = new THREE.Mesh(geometry, material); + scene.add(mesh); + + // Animation Loop + function animate() { + requestAnimationFrame(animate); + + mesh.rotation.x += 0.01; + mesh.rotation.y += 0.01; + + controls.update(); + renderer.render(scene, camera); + } + + animate(); + + // Resize Handler + window.addEventListener('resize', () => { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + }); +} +``` + +--- + +## React Three Fiber mit WebGPU + +```tsx +// components/WebGPUScene.tsx +'use client'; + +import { Canvas } from '@react-three/fiber'; +import { OrbitControls, Environment } from '@react-three/drei'; +import { Suspense } from 'react'; + +export function WebGPUScene() { + return ( + { + // WebGPU Renderer wird automatisch verwendet wenn verfügbar + // Fallback auf WebGL + return undefined; + }} + frameloop="demand" + > + + + + + + + + + + + ); +} + +// Feature Detection +export function useWebGPUSupport() { + const [supported, setSupported] = useState(null); + + useEffect(() => { + setSupported('gpu' in navigator); + }, []); + + return supported; +} +``` + +--- + +## Performance Benchmarks + +```typescript +// benchmarks/webgpu-vs-webgl.ts + +interface BenchmarkResult { + name: string; + webgl: number; + webgpu: number; + speedup: number; +} + +const benchmarks: BenchmarkResult[] = [ + { + name: 'Draw Calls (1000 objects)', + webgl: 16.7, // ms + webgpu: 1.5, // ms + speedup: 11.1 + }, + { + name: 'Particle System (100k)', + webgl: 33.3, // ms (CPU-bound) + webgpu: 2.1, // ms (GPU Compute) + speedup: 15.9 + }, + { + name: 'Shadow Mapping', + webgl: 8.2, // ms + webgpu: 3.4, // ms + speedup: 2.4 + }, + { + name: 'Post-Processing (5 passes)', + webgl: 12.5, // ms + webgpu: 4.8, // ms + speedup: 2.6 + }, + { + name: 'ML Inference (ONNX)', + webgl: 45.0, // ms (limited) + webgpu: 8.0, // ms (Compute) + speedup: 5.6 + } +]; + +// Render Bundles für maximale Performance +async function createRenderBundle(device: GPUDevice, pipeline: GPURenderPipeline) { + const encoder = device.createRenderBundleEncoder({ + colorFormats: ['bgra8unorm'] + }); + + encoder.setPipeline(pipeline); + + // Pre-record alle Draw Calls + for (let i = 0; i < 1000; i++) { + encoder.draw(3); + } + + return encoder.finish(); +} +``` + +--- + +## Feature Detection & Fallback + +```typescript +// lib/gpu-detection.ts + +interface GPUCapabilities { + webgpu: boolean; + webgl2: boolean; + webgl: boolean; + computeShaders: boolean; + maxTextureSize: number; +} + +async function detectGPUCapabilities(): Promise { + const capabilities: GPUCapabilities = { + webgpu: false, + webgl2: false, + webgl: false, + computeShaders: false, + maxTextureSize: 0 + }; + + // WebGPU Check + if ('gpu' in navigator) { + try { + const adapter = await navigator.gpu.requestAdapter(); + if (adapter) { + capabilities.webgpu = true; + capabilities.computeShaders = true; + + const limits = adapter.limits; + capabilities.maxTextureSize = limits.maxTextureDimension2D; + } + } catch { + // WebGPU not available + } + } + + // WebGL2 Check + const canvas = document.createElement('canvas'); + const gl2 = canvas.getContext('webgl2'); + if (gl2) { + capabilities.webgl2 = true; + capabilities.maxTextureSize = Math.max( + capabilities.maxTextureSize, + gl2.getParameter(gl2.MAX_TEXTURE_SIZE) + ); + } + + // WebGL Check + const gl = canvas.getContext('webgl'); + if (gl) { + capabilities.webgl = true; + } + + return capabilities; +} + +// Adaptive Rendering +async function createRenderer(canvas: HTMLCanvasElement) { + const caps = await detectGPUCapabilities(); + + if (caps.webgpu) { + console.log('Using WebGPU renderer'); + return createWebGPURenderer(canvas); + } + + if (caps.webgl2) { + console.log('Using WebGL2 renderer'); + return createWebGL2Renderer(canvas); + } + + if (caps.webgl) { + console.log('Using WebGL renderer (limited features)'); + return createWebGLRenderer(canvas); + } + + throw new Error('No GPU rendering available'); +} +``` + +--- + +## Fazit + +WebGPU bietet: + +1. **10x Performance**: Render Bundles, weniger Overhead +2. **Compute Shaders**: GPU für Berechnungen nutzen +3. **Moderne API**: Basiert auf Vulkan/Metal/D3D12 +4. **Browser Support**: Alle Major Browser (2025/2026) + +Die Zukunft der Browser-Grafik ist WebGPU. + +--- + +## Bildprompts + +1. "WebGPU particle simulation with 100k particles, compute shader visualization" +2. "Performance comparison chart WebGL vs WebGPU, bar graph" +3. "Modern GPU architecture diagram, WebGPU pipeline stages" + +--- + +## Quellen + +- [WebGPU MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API) +- [WebGPU Supported in All Browsers](https://web.dev/blog/webgpu-supported-major-browsers) +- [Three.js WebGPU Roadmap](https://threejsroadmap.com/blog/webgl-vs-webgpu-explained) +- [Chrome WebGPU Blog](https://developer.chrome.com/blog/webgpu-io2023) diff --git a/blog-posts/73-gltf-model-optimization.md b/blog-posts/73-gltf-model-optimization.md new file mode 100644 index 0000000..e4380f0 --- /dev/null +++ b/blog-posts/73-gltf-model-optimization.md @@ -0,0 +1,539 @@ +# GLTF Model Optimization für Web + +**Meta-Description:** 3D Modelle für Web optimieren. GLTF/GLB Format, Draco Compression, Texture Optimization und LOD Strategien. + +**Keywords:** GLTF, GLB, Draco, 3D Optimization, Web 3D, Texture Compression, LOD, Three.js + +--- + +## Einführung + +**GLTF** (GL Transmission Format) ist das "JPEG der 3D-Welt". Mit **Draco Compression** können Modelle um bis zu 95% verkleinert werden. Optimierte 3D-Assets sind essentiell für schnelle Ladezeiten und gute User Experience. + +--- + +## GLTF Format Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ GLTF/GLB FORMAT STRUCTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ GLTF (Text + Binary): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ model.gltf (JSON - Scene Description) │ │ +│ │ model.bin (Binary - Geometry Data) │ │ +│ │ textures/ │ │ +│ │ ├── diffuse.png (Texture Files) │ │ +│ │ ├── normal.png │ │ +│ │ └── metallic.png │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ GLB (Single Binary): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ model.glb │ │ +│ │ ┌─────────────────────────────────────────────┐ │ │ +│ │ │ Header (12 bytes) │ │ │ +│ │ │ JSON Chunk (Scene + Materials) │ │ │ +│ │ │ Binary Chunk (Geometry + Textures) │ │ │ +│ │ └─────────────────────────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ File Size Breakdown (typical): │ +│ ├── Textures: 80% │ +│ ├── Geometry: 15% │ +│ └── Metadata: 5% │ +│ │ +│ Supported Extensions: │ +│ ├── KHR_draco_mesh_compression (Geometry) │ +│ ├── KHR_texture_basisu (Textures) │ +│ ├── KHR_mesh_quantization (Vertices) │ +│ └── EXT_meshopt_compression (Alternative) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## gltf-transform CLI + +```bash +# Installation +npm install -g @gltf-transform/cli + +# Basic Optimization +gltf-transform optimize input.glb output.glb + +# Mit Draco Compression +gltf-transform draco input.glb output.glb + +# Texture Optimization (WebP) +gltf-transform webp input.glb output.glb --quality 80 + +# Full Optimization Pipeline +gltf-transform optimize input.glb output.glb \ + --compress draco \ + --texture-compress webp \ + --texture-size 1024 + +# Modell analysieren +gltf-transform inspect input.glb + +# Mesh vereinfachen +gltf-transform simplify input.glb output.glb --ratio 0.5 + +# Texturen resizen +gltf-transform resize input.glb output.glb --width 1024 --height 1024 +``` + +--- + +## Node.js Optimization Script + +```typescript +// scripts/optimize-model.ts +import { Document, NodeIO, Transform } from '@gltf-transform/core'; +import { dedup, draco, textureCompress, weld, simplify } from '@gltf-transform/functions'; +import { KHRDracoMeshCompression, KHRTextureBasisu } from '@gltf-transform/extensions'; +import draco3d from 'draco3dgltf'; +import sharp from 'sharp'; + +interface OptimizationOptions { + dracoCompression: boolean; + textureFormat: 'webp' | 'jpeg' | 'png'; + textureQuality: number; + maxTextureSize: number; + simplifyRatio: number; + weldVertices: boolean; +} + +async function optimizeModel( + inputPath: string, + outputPath: string, + options: OptimizationOptions +): Promise<{ originalSize: number; optimizedSize: number; reduction: number }> { + const io = new NodeIO() + .registerExtensions([KHRDracoMeshCompression, KHRTextureBasisu]) + .registerDependencies({ + 'draco3d.decoder': await draco3d.createDecoderModule(), + 'draco3d.encoder': await draco3d.createEncoderModule() + }); + + // Modell laden + const document = await io.read(inputPath); + const originalSize = (await io.writeBinary(document)).byteLength; + + // Transformationen Pipeline + const transforms: Transform[] = []; + + // 1. Duplikate entfernen + transforms.push(dedup()); + + // 2. Vertices zusammenführen + if (options.weldVertices) { + transforms.push(weld({ tolerance: 0.0001 })); + } + + // 3. Mesh vereinfachen + if (options.simplifyRatio < 1) { + transforms.push(simplify({ + ratio: options.simplifyRatio, + error: 0.001 + })); + } + + // 4. Texturen optimieren + transforms.push(textureCompress({ + encoder: sharp, + targetFormat: options.textureFormat, + resize: [options.maxTextureSize, options.maxTextureSize], + quality: options.textureQuality + })); + + // 5. Draco Compression + if (options.dracoCompression) { + transforms.push(draco({ + quantizePosition: 14, + quantizeNormal: 10, + quantizeTexcoord: 12, + quantizeColor: 8 + })); + } + + // Transformationen anwenden + await document.transform(...transforms); + + // Speichern + await io.write(outputPath, document); + + const optimizedSize = (await io.readBinary(outputPath)).byteLength; + const reduction = ((originalSize - optimizedSize) / originalSize) * 100; + + return { + originalSize, + optimizedSize, + reduction + }; +} + +// Verwendung +const result = await optimizeModel('./models/robot.glb', './models/robot-optimized.glb', { + dracoCompression: true, + textureFormat: 'webp', + textureQuality: 80, + maxTextureSize: 1024, + simplifyRatio: 0.75, + weldVertices: true +}); + +console.log(`Original: ${(result.originalSize / 1024 / 1024).toFixed(2)} MB`); +console.log(`Optimized: ${(result.optimizedSize / 1024 / 1024).toFixed(2)} MB`); +console.log(`Reduction: ${result.reduction.toFixed(1)}%`); +``` + +--- + +## Batch Optimization + +```typescript +// scripts/batch-optimize.ts +import { glob } from 'glob'; +import path from 'path'; + +async function batchOptimize( + inputDir: string, + outputDir: string, + options: OptimizationOptions +) { + const files = await glob(`${inputDir}/**/*.{glb,gltf}`); + + console.log(`Found ${files.length} models to optimize`); + + const results: Array<{ + file: string; + originalSize: number; + optimizedSize: number; + reduction: number; + }> = []; + + for (const file of files) { + const relativePath = path.relative(inputDir, file); + const outputPath = path.join(outputDir, relativePath.replace(/\.gltf$/, '.glb')); + + // Output Verzeichnis erstellen + await fs.mkdir(path.dirname(outputPath), { recursive: true }); + + console.log(`Optimizing: ${relativePath}`); + + try { + const result = await optimizeModel(file, outputPath, options); + results.push({ file: relativePath, ...result }); + } catch (error) { + console.error(`Failed: ${relativePath}`, error); + } + } + + // Report + const totalOriginal = results.reduce((sum, r) => sum + r.originalSize, 0); + const totalOptimized = results.reduce((sum, r) => sum + r.optimizedSize, 0); + + console.log('\n=== Optimization Report ==='); + console.log(`Total Original: ${(totalOriginal / 1024 / 1024).toFixed(2)} MB`); + console.log(`Total Optimized: ${(totalOptimized / 1024 / 1024).toFixed(2)} MB`); + console.log(`Total Reduction: ${((1 - totalOptimized / totalOriginal) * 100).toFixed(1)}%`); + + return results; +} +``` + +--- + +## LOD Generation + +```typescript +// scripts/generate-lod.ts +import { Document, NodeIO } from '@gltf-transform/core'; +import { simplify, dedup, weld } from '@gltf-transform/functions'; + +interface LODConfig { + levels: Array<{ + suffix: string; + ratio: number; + distance: number; // Für Three.js LOD + }>; +} + +const defaultLODConfig: LODConfig = { + levels: [ + { suffix: '_lod0', ratio: 1.0, distance: 0 }, // Original + { suffix: '_lod1', ratio: 0.5, distance: 10 }, // 50% + { suffix: '_lod2', ratio: 0.25, distance: 25 }, // 25% + { suffix: '_lod3', ratio: 0.1, distance: 50 } // 10% + ] +}; + +async function generateLODs( + inputPath: string, + outputDir: string, + config: LODConfig = defaultLODConfig +) { + const io = new NodeIO(); + const originalDocument = await io.read(inputPath); + + const baseName = path.basename(inputPath, path.extname(inputPath)); + const results: string[] = []; + + for (const level of config.levels) { + const document = originalDocument.clone(); + + if (level.ratio < 1) { + await document.transform( + dedup(), + weld(), + simplify({ ratio: level.ratio, error: 0.001 }) + ); + } + + const outputPath = path.join(outputDir, `${baseName}${level.suffix}.glb`); + await io.write(outputPath, document); + + results.push(outputPath); + console.log(`Generated ${level.suffix}: ${level.ratio * 100}%`); + } + + // LOD Manifest + const manifest = { + baseName, + levels: config.levels.map((level, i) => ({ + file: `${baseName}${level.suffix}.glb`, + ratio: level.ratio, + distance: level.distance + })) + }; + + await fs.writeFile( + path.join(outputDir, `${baseName}_lod_manifest.json`), + JSON.stringify(manifest, null, 2) + ); + + return results; +} + +// Three.js LOD Loader +import * as THREE from 'three'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +async function loadLODModel(manifestUrl: string): Promise { + const manifest = await fetch(manifestUrl).then(r => r.json()); + const loader = new GLTFLoader(); + + const lod = new THREE.LOD(); + + for (const level of manifest.levels) { + const gltf = await loader.loadAsync(level.file); + lod.addLevel(gltf.scene, level.distance); + } + + return lod; +} +``` + +--- + +## Texture Optimization + +```typescript +// scripts/optimize-textures.ts +import sharp from 'sharp'; +import { Document, NodeIO, Texture } from '@gltf-transform/core'; + +interface TextureOptimizationConfig { + maxSize: number; + format: 'webp' | 'jpeg' | 'png' | 'basis'; + quality: number; + normalMapQuality: number; + generateMipmaps: boolean; +} + +async function optimizeTextures( + document: Document, + config: TextureOptimizationConfig +): Promise { + const textures = document.getRoot().listTextures(); + + for (const texture of textures) { + const image = texture.getImage(); + if (!image) continue; + + const mimeType = texture.getMimeType(); + const name = texture.getName() || 'unnamed'; + + // Textur-Typ erkennen + const isNormalMap = name.toLowerCase().includes('normal'); + const quality = isNormalMap ? config.normalMapQuality : config.quality; + + // Mit Sharp verarbeiten + let pipeline = sharp(image); + + // Metadaten für Größenberechnung + const metadata = await pipeline.metadata(); + const originalWidth = metadata.width || 0; + const originalHeight = metadata.height || 0; + + // Resize wenn nötig + if (originalWidth > config.maxSize || originalHeight > config.maxSize) { + const scale = config.maxSize / Math.max(originalWidth, originalHeight); + pipeline = pipeline.resize( + Math.round(originalWidth * scale), + Math.round(originalHeight * scale), + { fit: 'inside' } + ); + } + + // Format konvertieren + let outputBuffer: Buffer; + + switch (config.format) { + case 'webp': + outputBuffer = await pipeline + .webp({ quality, lossless: isNormalMap }) + .toBuffer(); + texture.setMimeType('image/webp'); + break; + + case 'jpeg': + outputBuffer = await pipeline + .jpeg({ quality, mozjpeg: true }) + .toBuffer(); + texture.setMimeType('image/jpeg'); + break; + + case 'png': + outputBuffer = await pipeline + .png({ compressionLevel: 9 }) + .toBuffer(); + texture.setMimeType('image/png'); + break; + } + + texture.setImage(new Uint8Array(outputBuffer)); + + console.log(`Optimized texture: ${name} (${originalWidth}x${originalHeight} → compressed)`); + } +} + +// Power-of-Two Konvertierung (für ältere GPUs) +function toPowerOfTwo(value: number): number { + let result = 1; + while (result < value) { + result *= 2; + } + return result; +} + +async function ensurePowerOfTwo(document: Document): Promise { + const textures = document.getRoot().listTextures(); + + for (const texture of textures) { + const image = texture.getImage(); + if (!image) continue; + + const metadata = await sharp(image).metadata(); + const width = metadata.width || 0; + const height = metadata.height || 0; + + const pot2Width = toPowerOfTwo(width); + const pot2Height = toPowerOfTwo(height); + + if (width !== pot2Width || height !== pot2Height) { + const resized = await sharp(image) + .resize(pot2Width, pot2Height, { fit: 'fill' }) + .toBuffer(); + + texture.setImage(new Uint8Array(resized)); + console.log(`Resized to Power-of-2: ${width}x${height} → ${pot2Width}x${pot2Height}`); + } + } +} +``` + +--- + +## Compression Comparison + +| Method | Geometry Reduction | Texture Reduction | Decompression | +|--------|-------------------|-------------------|---------------| +| **Draco** | 90-95% | - | WASM (~300KB) | +| **Meshopt** | 70-80% | - | Fast, <50KB | +| **Quantization** | 30-50% | - | None needed | +| **WebP** | - | 50-70% | Native browser | +| **Basis/KTX2** | - | 70-85% | GPU direct | + +--- + +## React Three Fiber Integration + +```tsx +// components/OptimizedModel.tsx +'use client'; + +import { useGLTF, useProgress, Html } from '@react-three/drei'; +import { Suspense } from 'react'; + +// Mit Draco Loader +export function OptimizedModel({ url }: { url: string }) { + const { scene } = useGLTF(url, true); // true = Draco Loader + return ; +} + +// Preload für bessere UX +useGLTF.preload('/models/robot-optimized.glb', true); + +// Loading Progress +function LoadingProgress() { + const { progress } = useProgress(); + + return ( + +
+ Loading... {progress.toFixed(0)}% +
+ + ); +} + +// Lazy Loading mit LOD +export function LODModel({ manifest }: { manifest: string }) { + // Implement LOD loading... +} +``` + +--- + +## Fazit + +GLTF Optimization bietet: + +1. **Draco**: 90%+ Geometry Reduction +2. **WebP Textures**: 50-70% Texture Savings +3. **LOD**: Adaptive Quality nach Distanz +4. **gltf-transform**: Powerful CLI & API + +Essentiell für performante 3D Web-Experiences. + +--- + +## Bildprompts + +1. "3D model optimization pipeline diagram, before and after comparison" +2. "Texture compression comparison, WebP vs PNG quality" +3. "LOD levels visualization, low to high poly transition" + +--- + +## Quellen + +- [glTF Transform Documentation](https://gltf-transform.dev/) +- [Draco 3D Compression](https://google.github.io/draco/) +- [GLTF Specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html) +- [OptimizeGLB](https://optimizeglb.com/) diff --git a/blog-posts/74-nextjs-seo-metadata.md b/blog-posts/74-nextjs-seo-metadata.md new file mode 100644 index 0000000..aa504f8 --- /dev/null +++ b/blog-posts/74-nextjs-seo-metadata.md @@ -0,0 +1,661 @@ +# Next.js SEO & Metadata Optimization + +**Meta-Description:** SEO für Next.js 15. Metadata API, Open Graph, Twitter Cards und dynamische Meta-Tags für bessere Suchmaschinenplatzierung. + +**Keywords:** Next.js SEO, Metadata API, Open Graph, Twitter Cards, Meta Tags, generateMetadata, SEO Optimization + +--- + +## Einführung + +**Next.js 15** bietet eine revolutionäre **Metadata API** für SEO. Type-safe, automatisch optimiert und perfekt für dynamische Inhalte – von Blogs bis E-Commerce. + +--- + +## Metadata API Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ NEXT.JS METADATA SYSTEM │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Static Metadata: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ export const metadata = { │ │ +│ │ title: 'Page Title', │ │ +│ │ description: 'Page description' │ │ +│ │ } │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Dynamic Metadata: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ export async function generateMetadata({ params }) │ │ +│ │ return { title: await getTitle(params.id) } │ │ +│ │ } │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Inheritance: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ layout.tsx (Base) │ │ +│ │ ↓ │ │ +│ │ page.tsx (Override/Extend) │ │ +│ │ ↓ │ │ +│ │ Final (Merged & Deduplicated) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Generated: │ +│ ├── │ +│ ├── <meta name="description"> │ +│ ├── <meta property="og:*"> │ +│ ├── <meta name="twitter:*"> │ +│ ├── <link rel="canonical"> │ +│ └── <script type="application/ld+json"> │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Static Metadata + +```typescript +// app/layout.tsx +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + // Basis + title: { + default: 'Meine Website', + template: '%s | Meine Website' // Für Child Pages + }, + description: 'Die beste Website für ...', + + // Keywords (weniger wichtig für Google, aber für andere) + keywords: ['Next.js', 'React', 'SEO'], + + // Autoren + authors: [{ name: 'Max Mustermann', url: 'https://example.com' }], + creator: 'Max Mustermann', + publisher: 'Meine Firma', + + // Robots + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + 'max-video-preview': -1, + 'max-image-preview': 'large', + 'max-snippet': -1 + } + }, + + // Icons + icons: { + icon: '/favicon.ico', + shortcut: '/favicon-16x16.png', + apple: '/apple-touch-icon.png' + }, + + // Manifest + manifest: '/site.webmanifest', + + // Open Graph + openGraph: { + type: 'website', + locale: 'de_DE', + url: 'https://example.com', + siteName: 'Meine Website', + title: 'Meine Website', + description: 'Die beste Website für ...', + images: [ + { + url: 'https://example.com/og-image.jpg', + width: 1200, + height: 630, + alt: 'Meine Website' + } + ] + }, + + // Twitter + twitter: { + card: 'summary_large_image', + title: 'Meine Website', + description: 'Die beste Website für ...', + images: ['https://example.com/twitter-image.jpg'], + creator: '@username' + }, + + // Verification + verification: { + google: 'google-site-verification-code', + yandex: 'yandex-verification-code', + other: { + 'msvalidate.01': 'bing-verification-code' + } + }, + + // Alternates (für i18n) + alternates: { + canonical: 'https://example.com', + languages: { + 'de-DE': 'https://example.com/de', + 'en-US': 'https://example.com/en' + } + }, + + // Category + category: 'technology' +}; + +export default function RootLayout({ + children +}: { + children: React.ReactNode; +}) { + return ( + <html lang="de"> + <body>{children}</body> + </html> + ); +} +``` + +--- + +## Dynamic Metadata + +```typescript +// app/blog/[slug]/page.tsx +import type { Metadata, ResolvingMetadata } from 'next'; +import { getBlogPost, getAllPosts } from '@/lib/blog'; +import { notFound } from 'next/navigation'; + +interface PageProps { + params: Promise<{ slug: string }>; +} + +// Dynamische Metadata Generation +export async function generateMetadata( + { params }: PageProps, + parent: ResolvingMetadata +): Promise<Metadata> { + const { slug } = await params; + const post = await getBlogPost(slug); + + if (!post) { + return { + title: 'Not Found' + }; + } + + // Parent Metadata für Merge + const previousImages = (await parent).openGraph?.images || []; + + return { + title: post.title, + description: post.excerpt, + authors: [{ name: post.author.name }], + + openGraph: { + title: post.title, + description: post.excerpt, + type: 'article', + publishedTime: post.publishedAt, + modifiedTime: post.updatedAt, + authors: [post.author.name], + tags: post.tags, + images: [ + { + url: post.coverImage, + width: 1200, + height: 630, + alt: post.title + }, + ...previousImages + ] + }, + + twitter: { + card: 'summary_large_image', + title: post.title, + description: post.excerpt, + images: [post.coverImage] + }, + + alternates: { + canonical: `https://example.com/blog/${slug}` + } + }; +} + +// Static Params für Build-Zeit Generation +export async function generateStaticParams() { + const posts = await getAllPosts(); + return posts.map(post => ({ slug: post.slug })); +} + +export default async function BlogPost({ params }: PageProps) { + const { slug } = await params; + const post = await getBlogPost(slug); + + if (!post) { + notFound(); + } + + return ( + <article> + <h1>{post.title}</h1> + <div dangerouslySetInnerHTML={{ __html: post.content }} /> + </article> + ); +} +``` + +--- + +## Dynamic OG Images + +```typescript +// app/blog/[slug]/opengraph-image.tsx +import { ImageResponse } from 'next/og'; +import { getBlogPost } from '@/lib/blog'; + +export const runtime = 'edge'; +export const alt = 'Blog Post Image'; +export const size = { width: 1200, height: 630 }; +export const contentType = 'image/png'; + +export default async function OGImage({ + params +}: { + params: { slug: string }; +}) { + const post = await getBlogPost(params.slug); + + // Font laden + const interBold = fetch( + new URL('./Inter-Bold.ttf', import.meta.url) + ).then(res => res.arrayBuffer()); + + return new ImageResponse( + ( + <div + style={{ + width: '100%', + height: '100%', + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-start', + justifyContent: 'flex-end', + backgroundColor: '#1a1a2e', + padding: 60, + backgroundImage: 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)' + }} + > + {/* Logo */} + <div + style={{ + position: 'absolute', + top: 60, + left: 60, + display: 'flex', + alignItems: 'center', + gap: 16 + }} + > + <div + style={{ + width: 48, + height: 48, + backgroundColor: '#00d9ff', + borderRadius: 8 + }} + /> + <span style={{ color: '#ffffff', fontSize: 24 }}> + Meine Website + </span> + </div> + + {/* Title */} + <div + style={{ + fontSize: 64, + fontWeight: 'bold', + color: '#ffffff', + lineHeight: 1.2, + maxWidth: '80%' + }} + > + {post?.title || 'Blog Post'} + </div> + + {/* Meta */} + <div + style={{ + marginTop: 24, + display: 'flex', + gap: 24, + color: '#888888', + fontSize: 24 + }} + > + <span>{post?.author.name}</span> + <span>•</span> + <span>{post?.readingTime} min read</span> + </div> + </div> + ), + { + ...size, + fonts: [ + { + name: 'Inter', + data: await interBold, + style: 'normal', + weight: 700 + } + ] + } + ); +} +``` + +--- + +## Sitemap Generation + +```typescript +// app/sitemap.ts +import { MetadataRoute } from 'next'; +import { getAllPosts } from '@/lib/blog'; +import { getAllProducts } from '@/lib/products'; + +export default async function sitemap(): Promise<MetadataRoute.Sitemap> { + const baseUrl = 'https://example.com'; + + // Static Pages + const staticPages: MetadataRoute.Sitemap = [ + { + url: baseUrl, + lastModified: new Date(), + changeFrequency: 'daily', + priority: 1.0 + }, + { + url: `${baseUrl}/about`, + lastModified: new Date(), + changeFrequency: 'monthly', + priority: 0.8 + }, + { + url: `${baseUrl}/contact`, + lastModified: new Date(), + changeFrequency: 'yearly', + priority: 0.5 + } + ]; + + // Blog Posts + const posts = await getAllPosts(); + const blogPages: MetadataRoute.Sitemap = posts.map(post => ({ + url: `${baseUrl}/blog/${post.slug}`, + lastModified: new Date(post.updatedAt), + changeFrequency: 'weekly', + priority: 0.7 + })); + + // Products + const products = await getAllProducts(); + const productPages: MetadataRoute.Sitemap = products.map(product => ({ + url: `${baseUrl}/products/${product.slug}`, + lastModified: new Date(product.updatedAt), + changeFrequency: 'daily', + priority: 0.9 + })); + + return [...staticPages, ...blogPages, ...productPages]; +} +``` + +--- + +## Robots.txt + +```typescript +// app/robots.ts +import { MetadataRoute } from 'next'; + +export default function robots(): MetadataRoute.Robots { + const baseUrl = 'https://example.com'; + + return { + rules: [ + { + userAgent: '*', + allow: '/', + disallow: ['/admin/', '/api/', '/private/'] + }, + { + userAgent: 'Googlebot', + allow: '/', + disallow: ['/admin/'] + } + ], + sitemap: `${baseUrl}/sitemap.xml`, + host: baseUrl + }; +} +``` + +--- + +## JSON-LD Structured Data + +```typescript +// components/JsonLd.tsx +import Script from 'next/script'; + +interface ArticleJsonLdProps { + title: string; + description: string; + publishedTime: string; + modifiedTime: string; + authorName: string; + images: string[]; + url: string; +} + +export function ArticleJsonLd({ + title, + description, + publishedTime, + modifiedTime, + authorName, + images, + url +}: ArticleJsonLdProps) { + const jsonLd = { + '@context': 'https://schema.org', + '@type': 'Article', + headline: title, + description, + image: images, + datePublished: publishedTime, + dateModified: modifiedTime, + author: { + '@type': 'Person', + name: authorName + }, + publisher: { + '@type': 'Organization', + name: 'Meine Website', + logo: { + '@type': 'ImageObject', + url: 'https://example.com/logo.png' + } + }, + mainEntityOfPage: { + '@type': 'WebPage', + '@id': url + } + }; + + return ( + <Script + id="article-jsonld" + type="application/ld+json" + dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} + /> + ); +} + +// Verwendung in Page +export default function BlogPost({ post }) { + return ( + <> + <ArticleJsonLd + title={post.title} + description={post.excerpt} + publishedTime={post.publishedAt} + modifiedTime={post.updatedAt} + authorName={post.author.name} + images={[post.coverImage]} + url={`https://example.com/blog/${post.slug}`} + /> + <article>...</article> + </> + ); +} +``` + +--- + +## SEO Komponenten Library + +```typescript +// lib/seo.ts +import { Metadata } from 'next'; + +interface SEOConfig { + title: string; + description: string; + canonical?: string; + image?: string; + type?: 'website' | 'article' | 'product'; + publishedTime?: string; + modifiedTime?: string; + author?: string; + tags?: string[]; + noindex?: boolean; +} + +export function generateSEO(config: SEOConfig): Metadata { + const { + title, + description, + canonical, + image = 'https://example.com/default-og.jpg', + type = 'website', + publishedTime, + modifiedTime, + author, + tags, + noindex = false + } = config; + + return { + title, + description, + + robots: noindex ? { + index: false, + follow: false + } : { + index: true, + follow: true + }, + + alternates: canonical ? { + canonical + } : undefined, + + openGraph: { + title, + description, + type, + url: canonical, + images: [{ url: image, width: 1200, height: 630 }], + ...(publishedTime && { publishedTime }), + ...(modifiedTime && { modifiedTime }), + ...(author && { authors: [author] }), + ...(tags && { tags }) + }, + + twitter: { + card: 'summary_large_image', + title, + description, + images: [image] + } + }; +} + +// Verwendung +export const metadata = generateSEO({ + title: 'Mein Artikel', + description: 'Beschreibung...', + canonical: 'https://example.com/artikel', + type: 'article', + author: 'Max Mustermann' +}); +``` + +--- + +## SEO Checklist + +| Element | Empfehlung | +|---------|------------| +| **Title** | 50-60 Zeichen, Keyword am Anfang | +| **Description** | 150-160 Zeichen, CTA | +| **H1** | Nur eine pro Seite | +| **Images** | Alt-Text, optimierte Größe | +| **URLs** | Kurz, beschreibend, Bindestrich | +| **Canonical** | Immer setzen | +| **OG Image** | 1200x630px | +| **Sitemap** | Alle wichtigen Seiten | +| **robots.txt** | Korrekt konfiguriert | +| **JSON-LD** | Strukturierte Daten | + +--- + +## Fazit + +Next.js SEO bietet: + +1. **Metadata API**: Type-safe, automatisch optimiert +2. **Dynamic OG Images**: Generiert on-the-fly +3. **Sitemap/Robots**: File-based Generation +4. **JSON-LD**: Strukturierte Daten Support + +SEO-Optimierung war nie einfacher. + +--- + +## Bildprompts + +1. "SEO dashboard showing metadata configuration, Next.js interface" +2. "Open Graph preview on social media, rich snippet display" +3. "Sitemap visualization with page hierarchy, SEO structure" + +--- + +## Quellen + +- [Next.js Metadata Documentation](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) +- [Strapi Next.js SEO Guide](https://strapi.io/blog/nextjs-seo) +- [Medium SEO Guide](https://medium.com/@thomasaugot/the-complete-guide-to-seo-optimization-in-next-js-15-1bdb118cffd7) +- [Google Search Central](https://developers.google.com/search/docs) diff --git a/blog-posts/75-core-web-vitals-optimization.md b/blog-posts/75-core-web-vitals-optimization.md new file mode 100644 index 0000000..c146f84 --- /dev/null +++ b/blog-posts/75-core-web-vitals-optimization.md @@ -0,0 +1,694 @@ +# Core Web Vitals Optimization + +**Meta-Description:** Core Web Vitals 2026 optimieren. LCP, INP, CLS verbessern mit praktischen Strategien für Next.js und React Anwendungen. + +**Keywords:** Core Web Vitals, LCP, INP, CLS, Web Performance, Page Speed, User Experience, Next.js Performance + +--- + +## Einführung + +**Core Web Vitals** sind Googles wichtigste Metriken für User Experience. Mit dem **INP Update 2024** (ersetzt FID) und neuen **2026 Benchmarks** ist Performance-Optimierung wichtiger denn je. Dieser Guide zeigt praktische Strategien für moderne Web-Apps. + +--- + +## Core Web Vitals Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CORE WEB VITALS 2026 │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ LCP (Largest Contentful Paint): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Loading Performance │ │ +│ │ ├── Good: ≤ 2.5s │ │ +│ │ ├── Needs Improvement: 2.5s - 4.0s │ │ +│ │ └── Poor: > 4.0s │ │ +│ │ │ │ +│ │ Typical LCP Elements: │ │ +│ │ ├── <img> (Hero Images) │ │ +│ │ ├── <video> poster │ │ +│ │ ├── Background images (CSS) │ │ +│ │ └── Block-level text elements │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ INP (Interaction to Next Paint): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Interactivity (replaced FID in 2024) │ │ +│ │ ├── Good: ≤ 200ms │ │ +│ │ ├── Needs Improvement: 200ms - 500ms │ │ +│ │ └── Poor: > 500ms │ │ +│ │ │ │ +│ │ Measured Interactions: │ │ +│ │ ├── Click / Tap │ │ +│ │ ├── Keyboard input │ │ +│ │ └── Touch interactions │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ CLS (Cumulative Layout Shift): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Visual Stability │ │ +│ │ ├── Good: ≤ 0.1 │ │ +│ │ ├── Needs Improvement: 0.1 - 0.25 │ │ +│ │ └── Poor: > 0.25 │ │ +│ │ │ │ +│ │ Common Causes: │ │ +│ │ ├── Images without dimensions │ │ +│ │ ├── Ads, embeds, iframes │ │ +│ │ ├── Dynamic content injection │ │ +│ │ └── Web fonts (FOUT/FOIT) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## LCP Optimization + +```typescript +// next.config.js - Image Optimization +/** @type {import('next').NextConfig} */ +const nextConfig = { + images: { + // Moderne Formate priorisieren + formats: ['image/avif', 'image/webp'], + + // Device Sizes für responsive images + deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048], + + // Image Sizes für kleinere Bilder + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + + // Remote Patterns + remotePatterns: [ + { + protocol: 'https', + hostname: 'images.example.com', + pathname: '/**' + } + ], + + // Minimale Cache-Zeit + minimumCacheTTL: 60 * 60 * 24 * 30 // 30 Tage + } +}; + +module.exports = nextConfig; +``` + +```tsx +// components/HeroImage.tsx - Optimized LCP Element +import Image from 'next/image'; + +interface HeroImageProps { + src: string; + alt: string; +} + +export function HeroImage({ src, alt }: HeroImageProps) { + return ( + <div className="relative w-full h-[60vh]"> + <Image + src={src} + alt={alt} + fill + priority // Kritisch für LCP! + sizes="100vw" + style={{ objectFit: 'cover' }} + // Placeholder für bessere UX + placeholder="blur" + blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..." + /> + </div> + ); +} + +// Preload kritischer Ressourcen +// app/layout.tsx +export default function RootLayout({ children }) { + return ( + <html> + <head> + {/* LCP Image Preload */} + <link + rel="preload" + as="image" + href="/hero-image.webp" + imageSrcSet="/hero-640.webp 640w, /hero-1200.webp 1200w" + imageSizes="100vw" + /> + + {/* Critical Font Preload */} + <link + rel="preload" + as="font" + type="font/woff2" + href="/fonts/inter-var.woff2" + crossOrigin="anonymous" + /> + </head> + <body>{children}</body> + </html> + ); +} +``` + +```typescript +// lib/critical-css.ts - Critical CSS Extraction +import { PurgeCSS } from 'purgecss'; +import critical from 'critical'; + +async function generateCriticalCSS(html: string, css: string): Promise<string> { + const result = await critical.generate({ + html, + css, + width: 1300, + height: 900, + inline: false, + extract: true, + penthouse: { + blockJSRequests: false + } + }); + + return result.css; +} + +// Inline Critical CSS +export function InlineCriticalCSS({ css }: { css: string }) { + return ( + <style + dangerouslySetInnerHTML={{ __html: css }} + data-critical="true" + /> + ); +} +``` + +--- + +## INP Optimization + +```typescript +// hooks/useOptimizedCallback.ts - Debounced Interactions +import { useCallback, useRef } from 'react'; + +export function useOptimizedCallback<T extends (...args: any[]) => void>( + callback: T, + delay: number = 100 +) { + const timeoutRef = useRef<NodeJS.Timeout>(); + const lastCallRef = useRef<number>(0); + + return useCallback((...args: Parameters<T>) => { + const now = Date.now(); + + // Sofortige Ausführung wenn genug Zeit vergangen + if (now - lastCallRef.current >= delay) { + lastCallRef.current = now; + callback(...args); + return; + } + + // Debounce für schnelle Aufrufe + clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => { + lastCallRef.current = Date.now(); + callback(...args); + }, delay - (now - lastCallRef.current)); + }, [callback, delay]); +} + +// hooks/useIdleCallback.ts - Non-blocking Operations +export function useIdleCallback() { + const scheduleIdleTask = useCallback(( + task: () => void, + options?: IdleRequestOptions + ) => { + if ('requestIdleCallback' in window) { + return requestIdleCallback(() => task(), options); + } + // Fallback + return setTimeout(task, 1) as unknown as number; + }, []); + + const cancelIdleTask = useCallback((id: number) => { + if ('cancelIdleCallback' in window) { + cancelIdleCallback(id); + } else { + clearTimeout(id); + } + }, []); + + return { scheduleIdleTask, cancelIdleTask }; +} +``` + +```tsx +// components/OptimizedList.tsx - Virtualized List für große Datenmengen +'use client'; + +import { useVirtualizer } from '@tanstack/react-virtual'; +import { useRef, useCallback, useTransition } from 'react'; + +interface Item { + id: string; + title: string; + description: string; +} + +interface OptimizedListProps { + items: Item[]; + onItemClick: (item: Item) => void; +} + +export function OptimizedList({ items, onItemClick }: OptimizedListProps) { + const parentRef = useRef<HTMLDivElement>(null); + const [isPending, startTransition] = useTransition(); + + const virtualizer = useVirtualizer({ + count: items.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 80, // Geschätzte Item-Höhe + overscan: 5 // Extra Items für smooth scrolling + }); + + // Non-blocking click handler + const handleClick = useCallback((item: Item) => { + // Sofortiges visuelles Feedback + startTransition(() => { + onItemClick(item); + }); + }, [onItemClick]); + + return ( + <div + ref={parentRef} + className="h-[600px] overflow-auto" + style={{ contain: 'strict' }} // CSS Containment für Performance + > + <div + style={{ + height: `${virtualizer.getTotalSize()}px`, + width: '100%', + position: 'relative' + }} + > + {virtualizer.getVirtualItems().map((virtualItem) => { + const item = items[virtualItem.index]; + + return ( + <div + key={item.id} + onClick={() => handleClick(item)} + className="absolute top-0 left-0 w-full p-4 border-b cursor-pointer hover:bg-gray-50" + style={{ + height: `${virtualItem.size}px`, + transform: `translateY(${virtualItem.start}px)`, + // Vermeidet Layout Thrashing + contain: 'layout style paint' + }} + > + <h3 className="font-semibold">{item.title}</h3> + <p className="text-gray-600 truncate">{item.description}</p> + </div> + ); + })} + </div> + + {isPending && ( + <div className="absolute top-2 right-2"> + <span className="text-sm text-gray-500">Loading...</span> + </div> + )} + </div> + ); +} +``` + +```typescript +// lib/web-worker.ts - Offload Heavy Computation +// workers/calculation.worker.ts +self.onmessage = (event: MessageEvent<{ data: number[] }>) => { + const { data } = event.data; + + // Schwere Berechnung im Worker + const result = data.reduce((sum, val) => { + // Simuliere komplexe Berechnung + for (let i = 0; i < 1000; i++) { + sum += Math.sqrt(val * i); + } + return sum; + }, 0); + + self.postMessage({ result }); +}; + +// hooks/useWebWorker.ts +import { useEffect, useRef, useCallback, useState } from 'react'; + +export function useCalculationWorker() { + const workerRef = useRef<Worker>(); + const [result, setResult] = useState<number | null>(null); + const [isCalculating, setIsCalculating] = useState(false); + + useEffect(() => { + workerRef.current = new Worker( + new URL('../workers/calculation.worker.ts', import.meta.url) + ); + + workerRef.current.onmessage = (event) => { + setResult(event.data.result); + setIsCalculating(false); + }; + + return () => workerRef.current?.terminate(); + }, []); + + const calculate = useCallback((data: number[]) => { + setIsCalculating(true); + workerRef.current?.postMessage({ data }); + }, []); + + return { calculate, result, isCalculating }; +} +``` + +--- + +## CLS Optimization + +```tsx +// components/ResponsiveImage.tsx - Prevent Layout Shift +import Image from 'next/image'; + +interface ResponsiveImageProps { + src: string; + alt: string; + width: number; + height: number; +} + +export function ResponsiveImage({ src, alt, width, height }: ResponsiveImageProps) { + // Aspect Ratio für Layout Reservierung + const aspectRatio = (height / width) * 100; + + return ( + <div + className="relative w-full" + style={{ paddingBottom: `${aspectRatio}%` }} + > + <Image + src={src} + alt={alt} + fill + sizes="(max-width: 768px) 100vw, 50vw" + style={{ objectFit: 'cover' }} + /> + </div> + ); +} + +// components/Skeleton.tsx - Content Placeholders +interface SkeletonProps { + className?: string; + variant?: 'text' | 'rectangular' | 'circular'; + width?: string | number; + height?: string | number; +} + +export function Skeleton({ + className = '', + variant = 'rectangular', + width, + height +}: SkeletonProps) { + const baseStyles = 'animate-pulse bg-gray-200'; + + const variantStyles = { + text: 'rounded', + rectangular: 'rounded-md', + circular: 'rounded-full' + }; + + return ( + <div + className={`${baseStyles} ${variantStyles[variant]} ${className}`} + style={{ width, height }} + aria-hidden="true" + /> + ); +} + +// Verwendung für Layout Stability +export function ArticleCardSkeleton() { + return ( + <div className="p-4 border rounded-lg"> + <Skeleton variant="rectangular" height={200} className="mb-4" /> + <Skeleton variant="text" height={24} className="mb-2" /> + <Skeleton variant="text" height={16} width="60%" /> + </div> + ); +} +``` + +```tsx +// components/FontOptimization.tsx - Prevent FOUT/FOIT +// next.config.js +const nextConfig = { + optimizeFonts: true +}; + +// app/layout.tsx - Font Preloading +import { Inter, Roboto_Mono } from 'next/font/google'; + +const inter = Inter({ + subsets: ['latin'], + display: 'swap', // Verhindert FOIT + variable: '--font-inter', + preload: true, + fallback: ['system-ui', 'arial'] +}); + +const robotoMono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', + variable: '--font-roboto-mono', + preload: true +}); + +export default function RootLayout({ children }) { + return ( + <html lang="de" className={`${inter.variable} ${robotoMono.variable}`}> + <body className="font-sans">{children}</body> + </html> + ); +} + +// CSS Variable Usage +// globals.css +/* +:root { + --font-sans: var(--font-inter), system-ui, sans-serif; + --font-mono: var(--font-roboto-mono), monospace; +} + +body { + font-family: var(--font-sans); + /* Font-size-adjust verhindert Layout Shift */ + font-size-adjust: 0.5; +} +*/ +``` + +```tsx +// components/DynamicContent.tsx - Reservierter Platz +'use client'; + +import { useState, useEffect } from 'react'; + +interface DynamicContentProps { + fetchContent: () => Promise<string>; + minHeight: number; // Minimale Höhe reservieren +} + +export function DynamicContent({ fetchContent, minHeight }: DynamicContentProps) { + const [content, setContent] = useState<string | null>(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + fetchContent() + .then(setContent) + .finally(() => setIsLoading(false)); + }, [fetchContent]); + + return ( + <div + style={{ + minHeight, + // CSS Containment für Layout Isolation + contain: 'layout' + }} + > + {isLoading ? ( + <Skeleton height={minHeight} /> + ) : ( + <div dangerouslySetInnerHTML={{ __html: content || '' }} /> + )} + </div> + ); +} + +// Ad Container mit reserviertem Platz +export function AdContainer({ + width, + height +}: { + width: number; + height: number +}) { + return ( + <div + style={{ + width, + height, + minWidth: width, + minHeight: height, + contain: 'strict', + backgroundColor: '#f0f0f0' + }} + data-ad-slot="123456" + > + {/* Ad loads here */} + </div> + ); +} +``` + +--- + +## Performance Monitoring + +```typescript +// lib/web-vitals.ts - Real User Monitoring +import { onLCP, onINP, onCLS, Metric } from 'web-vitals'; + +interface VitalsReport { + metric: string; + value: number; + rating: 'good' | 'needs-improvement' | 'poor'; + navigationType: string; + url: string; +} + +function sendToAnalytics(report: VitalsReport) { + // An Analytics senden + fetch('/api/vitals', { + method: 'POST', + body: JSON.stringify(report), + headers: { 'Content-Type': 'application/json' } + }); + + // Oder an Google Analytics + if (typeof gtag !== 'undefined') { + gtag('event', report.metric, { + value: Math.round(report.value), + event_category: 'Web Vitals', + event_label: report.rating, + non_interaction: true + }); + } +} + +function getRating(metric: Metric): 'good' | 'needs-improvement' | 'poor' { + const thresholds = { + LCP: [2500, 4000], + INP: [200, 500], + CLS: [0.1, 0.25] + }; + + const [good, poor] = thresholds[metric.name as keyof typeof thresholds] || [0, 0]; + + if (metric.value <= good) return 'good'; + if (metric.value <= poor) return 'needs-improvement'; + return 'poor'; +} + +export function initWebVitals() { + const reportMetric = (metric: Metric) => { + sendToAnalytics({ + metric: metric.name, + value: metric.value, + rating: getRating(metric), + navigationType: metric.navigationType || 'unknown', + url: window.location.href + }); + }; + + onLCP(reportMetric); + onINP(reportMetric); + onCLS(reportMetric); +} + +// app/providers.tsx +'use client'; + +import { useEffect } from 'react'; +import { initWebVitals } from '@/lib/web-vitals'; + +export function WebVitalsProvider({ children }: { children: React.ReactNode }) { + useEffect(() => { + initWebVitals(); + }, []); + + return <>{children}</>; +} +``` + +--- + +## Optimization Checklist + +| Metrik | Optimierung | Impact | +|--------|-------------|--------| +| **LCP** | Image priority, preload, responsive | Hoch | +| **LCP** | Critical CSS inline | Mittel | +| **LCP** | Server-side rendering | Hoch | +| **INP** | Event handler optimization | Hoch | +| **INP** | Web Workers für heavy tasks | Mittel | +| **INP** | useTransition für non-blocking | Mittel | +| **CLS** | Feste Dimensionen für Media | Hoch | +| **CLS** | Font display: swap | Mittel | +| **CLS** | Skeleton Placeholders | Mittel | + +--- + +## Fazit + +Core Web Vitals Optimization erfordert: + +1. **LCP**: Prioritize above-fold content, optimize images +2. **INP**: Non-blocking interactions, Web Workers +3. **CLS**: Reserved space, stable layouts +4. **Monitoring**: Real User Monitoring für kontinuierliche Verbesserung + +Performance ist kein Feature, sondern Grundvoraussetzung. + +--- + +## Bildprompts + +1. "Core Web Vitals dashboard showing green metrics, performance monitoring" +2. "Page loading timeline visualization, LCP element highlighted" +3. "Layout shift animation showing before and after optimization" + +--- + +## Quellen + +- [Web.dev Core Web Vitals](https://web.dev/vitals/) +- [Google Search Central Page Experience](https://developers.google.com/search/docs/appearance/page-experience) +- [Next.js Performance Optimization](https://nextjs.org/docs/app/building-your-application/optimizing) +- [Chrome DevTools Performance](https://developer.chrome.com/docs/devtools/performance/) diff --git a/blog-posts/76-structured-data-json-ld.md b/blog-posts/76-structured-data-json-ld.md new file mode 100644 index 0000000..090cb92 --- /dev/null +++ b/blog-posts/76-structured-data-json-ld.md @@ -0,0 +1,978 @@ +# Structured Data & JSON-LD + +**Meta-Description:** Strukturierte Daten mit JSON-LD für SEO. Schema.org Typen, Rich Snippets und Google Rich Results für bessere Sichtbarkeit. + +**Keywords:** JSON-LD, Structured Data, Schema.org, Rich Snippets, SEO, Google Rich Results, Semantic Web + +--- + +## Einführung + +**JSON-LD** (JavaScript Object Notation for Linked Data) ist Googles bevorzugtes Format für **strukturierte Daten**. Es ermöglicht **Rich Snippets** in Suchergebnissen – von Sternebewertungen bis FAQs. Dieser Guide zeigt die wichtigsten Schema-Typen für moderne Websites. + +--- + +## JSON-LD Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ STRUCTURED DATA ECOSYSTEM │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Schema.org Hierarchy: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Thing (Base Type) │ │ +│ │ ├── CreativeWork │ │ +│ │ │ ├── Article │ │ +│ │ │ ├── BlogPosting │ │ +│ │ │ ├── WebPage │ │ +│ │ │ └── SoftwareApplication │ │ +│ │ ├── Organization │ │ +│ │ │ ├── LocalBusiness │ │ +│ │ │ └── Corporation │ │ +│ │ ├── Person │ │ +│ │ ├── Product │ │ +│ │ ├── Event │ │ +│ │ └── Place │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Rich Results Types: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ⭐ Review Snippets (Sternebewertungen) │ │ +│ │ ❓ FAQ Snippets (Accordion Q&A) │ │ +│ │ 📝 How-To Snippets (Schritt-für-Schritt) │ │ +│ │ 🛒 Product Snippets (Preis, Verfügbarkeit) │ │ +│ │ 📅 Event Snippets (Datum, Ort) │ │ +│ │ 👤 Person Knowledge Panel │ │ +│ │ 🏢 Organization Knowledge Panel │ │ +│ │ 🍳 Recipe Cards (Kochzeit, Kalorien) │ │ +│ │ 💼 Job Postings (Gehalt, Standort) │ │ +│ │ 🔍 Sitelinks Searchbox │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Implementation: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ <script type="application/ld+json"> │ │ +│ │ { │ │ +│ │ "@context": "https://schema.org", │ │ +│ │ "@type": "Article", │ │ +│ │ "headline": "...", │ │ +│ │ "author": { "@type": "Person", ... } │ │ +│ │ } │ │ +│ │ </script> │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## TypeScript JSON-LD Library + +```typescript +// lib/jsonld/types.ts - Type Definitions +export interface Thing { + '@context': 'https://schema.org'; + '@type': string; + name?: string; + description?: string; + url?: string; + image?: string | ImageObject; +} + +export interface ImageObject { + '@type': 'ImageObject'; + url: string; + width?: number; + height?: number; + caption?: string; +} + +export interface Person { + '@type': 'Person'; + name: string; + url?: string; + image?: string; + jobTitle?: string; + worksFor?: Organization; + sameAs?: string[]; +} + +export interface Organization { + '@type': 'Organization' | 'LocalBusiness' | 'Corporation'; + name: string; + url?: string; + logo?: string | ImageObject; + sameAs?: string[]; + contactPoint?: ContactPoint[]; + address?: PostalAddress; +} + +export interface ContactPoint { + '@type': 'ContactPoint'; + telephone: string; + contactType: string; + availableLanguage?: string[]; +} + +export interface PostalAddress { + '@type': 'PostalAddress'; + streetAddress?: string; + addressLocality?: string; + addressRegion?: string; + postalCode?: string; + addressCountry?: string; +} + +export interface Article { + '@context': 'https://schema.org'; + '@type': 'Article' | 'BlogPosting' | 'NewsArticle' | 'TechArticle'; + headline: string; + description?: string; + image?: string | string[]; + datePublished: string; + dateModified?: string; + author: Person | Person[]; + publisher: Organization; + mainEntityOfPage?: WebPage; + articleBody?: string; + wordCount?: number; + keywords?: string[]; +} + +export interface WebPage { + '@type': 'WebPage'; + '@id': string; +} + +export interface Product { + '@context': 'https://schema.org'; + '@type': 'Product'; + name: string; + description?: string; + image?: string | string[]; + brand?: Brand; + offers?: Offer | Offer[]; + aggregateRating?: AggregateRating; + review?: Review[]; + sku?: string; + gtin13?: string; +} + +export interface Brand { + '@type': 'Brand'; + name: string; +} + +export interface Offer { + '@type': 'Offer'; + price: number; + priceCurrency: string; + availability: 'https://schema.org/InStock' | 'https://schema.org/OutOfStock' | 'https://schema.org/PreOrder'; + url?: string; + priceValidUntil?: string; + seller?: Organization; +} + +export interface AggregateRating { + '@type': 'AggregateRating'; + ratingValue: number; + reviewCount: number; + bestRating?: number; + worstRating?: number; +} + +export interface Review { + '@type': 'Review'; + author: Person; + datePublished: string; + reviewBody?: string; + reviewRating: Rating; +} + +export interface Rating { + '@type': 'Rating'; + ratingValue: number; + bestRating?: number; + worstRating?: number; +} + +export interface FAQPage { + '@context': 'https://schema.org'; + '@type': 'FAQPage'; + mainEntity: Question[]; +} + +export interface Question { + '@type': 'Question'; + name: string; + acceptedAnswer: Answer; +} + +export interface Answer { + '@type': 'Answer'; + text: string; +} + +export interface BreadcrumbList { + '@context': 'https://schema.org'; + '@type': 'BreadcrumbList'; + itemListElement: ListItem[]; +} + +export interface ListItem { + '@type': 'ListItem'; + position: number; + name: string; + item?: string; +} + +export interface HowTo { + '@context': 'https://schema.org'; + '@type': 'HowTo'; + name: string; + description?: string; + image?: string; + totalTime?: string; // ISO 8601 duration + estimatedCost?: MonetaryAmount; + supply?: HowToSupply[]; + tool?: HowToTool[]; + step: HowToStep[]; +} + +export interface HowToStep { + '@type': 'HowToStep'; + name: string; + text: string; + image?: string; + url?: string; +} + +export interface HowToSupply { + '@type': 'HowToSupply'; + name: string; +} + +export interface HowToTool { + '@type': 'HowToTool'; + name: string; +} + +export interface MonetaryAmount { + '@type': 'MonetaryAmount'; + currency: string; + value: number; +} + +export interface Event { + '@context': 'https://schema.org'; + '@type': 'Event' | 'BusinessEvent' | 'MusicEvent' | 'SportsEvent'; + name: string; + description?: string; + startDate: string; + endDate?: string; + location: Place | VirtualLocation; + organizer?: Organization | Person; + performer?: Person | Organization; + offers?: Offer[]; + eventStatus?: 'https://schema.org/EventScheduled' | 'https://schema.org/EventCancelled' | 'https://schema.org/EventPostponed'; + eventAttendanceMode?: 'https://schema.org/OnlineEventAttendanceMode' | 'https://schema.org/OfflineEventAttendanceMode' | 'https://schema.org/MixedEventAttendanceMode'; +} + +export interface Place { + '@type': 'Place'; + name: string; + address: PostalAddress; +} + +export interface VirtualLocation { + '@type': 'VirtualLocation'; + url: string; +} +``` + +--- + +## JSON-LD Components + +```tsx +// components/jsonld/JsonLd.tsx - Base Component +import Script from 'next/script'; + +interface JsonLdProps<T> { + data: T; + id?: string; +} + +export function JsonLd<T extends object>({ data, id }: JsonLdProps<T>) { + return ( + <Script + id={id || 'json-ld'} + type="application/ld+json" + dangerouslySetInnerHTML={{ + __html: JSON.stringify(data, null, 0) + }} + strategy="beforeInteractive" + /> + ); +} + +// components/jsonld/ArticleJsonLd.tsx +import { JsonLd } from './JsonLd'; +import type { Article, Person, Organization } from '@/lib/jsonld/types'; + +interface ArticleJsonLdProps { + title: string; + description: string; + publishedTime: string; + modifiedTime?: string; + authorName: string; + authorUrl?: string; + images: string[]; + url: string; + siteName: string; + logoUrl: string; +} + +export function ArticleJsonLd({ + title, + description, + publishedTime, + modifiedTime, + authorName, + authorUrl, + images, + url, + siteName, + logoUrl +}: ArticleJsonLdProps) { + const author: Person = { + '@type': 'Person', + name: authorName, + ...(authorUrl && { url: authorUrl }) + }; + + const publisher: Organization = { + '@type': 'Organization', + name: siteName, + logo: { + '@type': 'ImageObject', + url: logoUrl, + width: 600, + height: 60 + } + }; + + const data: Article = { + '@context': 'https://schema.org', + '@type': 'Article', + headline: title, + description, + image: images, + datePublished: publishedTime, + ...(modifiedTime && { dateModified: modifiedTime }), + author, + publisher, + mainEntityOfPage: { + '@type': 'WebPage', + '@id': url + } + }; + + return <JsonLd data={data} id="article-jsonld" />; +} + +// components/jsonld/ProductJsonLd.tsx +import { JsonLd } from './JsonLd'; +import type { Product, Offer, AggregateRating } from '@/lib/jsonld/types'; + +interface ProductJsonLdProps { + name: string; + description: string; + images: string[]; + brand: string; + sku: string; + price: number; + currency: string; + availability: 'InStock' | 'OutOfStock' | 'PreOrder'; + ratingValue?: number; + reviewCount?: number; + url: string; +} + +export function ProductJsonLd({ + name, + description, + images, + brand, + sku, + price, + currency, + availability, + ratingValue, + reviewCount, + url +}: ProductJsonLdProps) { + const offer: Offer = { + '@type': 'Offer', + price, + priceCurrency: currency, + availability: `https://schema.org/${availability}`, + url + }; + + const data: Product = { + '@context': 'https://schema.org', + '@type': 'Product', + name, + description, + image: images, + brand: { + '@type': 'Brand', + name: brand + }, + sku, + offers: offer, + ...(ratingValue && reviewCount && { + aggregateRating: { + '@type': 'AggregateRating', + ratingValue, + reviewCount, + bestRating: 5, + worstRating: 1 + } as AggregateRating + }) + }; + + return <JsonLd data={data} id="product-jsonld" />; +} +``` + +```tsx +// components/jsonld/FAQJsonLd.tsx +import { JsonLd } from './JsonLd'; +import type { FAQPage, Question } from '@/lib/jsonld/types'; + +interface FAQItem { + question: string; + answer: string; +} + +interface FAQJsonLdProps { + items: FAQItem[]; +} + +export function FAQJsonLd({ items }: FAQJsonLdProps) { + const questions: Question[] = items.map(item => ({ + '@type': 'Question', + name: item.question, + acceptedAnswer: { + '@type': 'Answer', + text: item.answer + } + })); + + const data: FAQPage = { + '@context': 'https://schema.org', + '@type': 'FAQPage', + mainEntity: questions + }; + + return <JsonLd data={data} id="faq-jsonld" />; +} + +// components/jsonld/BreadcrumbJsonLd.tsx +import { JsonLd } from './JsonLd'; +import type { BreadcrumbList, ListItem } from '@/lib/jsonld/types'; + +interface BreadcrumbItem { + name: string; + url?: string; +} + +interface BreadcrumbJsonLdProps { + items: BreadcrumbItem[]; +} + +export function BreadcrumbJsonLd({ items }: BreadcrumbJsonLdProps) { + const listItems: ListItem[] = items.map((item, index) => ({ + '@type': 'ListItem', + position: index + 1, + name: item.name, + ...(item.url && { item: item.url }) + })); + + const data: BreadcrumbList = { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + itemListElement: listItems + }; + + return <JsonLd data={data} id="breadcrumb-jsonld" />; +} + +// components/jsonld/OrganizationJsonLd.tsx +import { JsonLd } from './JsonLd'; +import type { Organization, ContactPoint, PostalAddress } from '@/lib/jsonld/types'; + +interface OrganizationJsonLdProps { + name: string; + url: string; + logo: string; + description?: string; + email?: string; + telephone?: string; + address?: { + street: string; + city: string; + region: string; + postalCode: string; + country: string; + }; + socialProfiles?: string[]; +} + +export function OrganizationJsonLd({ + name, + url, + logo, + description, + email, + telephone, + address, + socialProfiles +}: OrganizationJsonLdProps) { + const contactPoints: ContactPoint[] = []; + + if (telephone) { + contactPoints.push({ + '@type': 'ContactPoint', + telephone, + contactType: 'customer service', + availableLanguage: ['German', 'English'] + }); + } + + const postalAddress: PostalAddress | undefined = address ? { + '@type': 'PostalAddress', + streetAddress: address.street, + addressLocality: address.city, + addressRegion: address.region, + postalCode: address.postalCode, + addressCountry: address.country + } : undefined; + + const data: Organization & { '@context': string; description?: string } = { + '@context': 'https://schema.org', + '@type': 'Organization', + name, + url, + logo: { + '@type': 'ImageObject', + url: logo, + width: 600, + height: 60 + }, + ...(description && { description }), + ...(contactPoints.length > 0 && { contactPoint: contactPoints }), + ...(postalAddress && { address: postalAddress }), + ...(socialProfiles && { sameAs: socialProfiles }) + }; + + return <JsonLd data={data} id="organization-jsonld" />; +} +``` + +--- + +## Practical Examples + +```tsx +// app/blog/[slug]/page.tsx - Blog Article with JSON-LD +import { ArticleJsonLd } from '@/components/jsonld/ArticleJsonLd'; +import { BreadcrumbJsonLd } from '@/components/jsonld/BreadcrumbJsonLd'; +import { getBlogPost } from '@/lib/blog'; + +export default async function BlogPost({ + params +}: { + params: Promise<{ slug: string }> +}) { + const { slug } = await params; + const post = await getBlogPost(slug); + + const baseUrl = 'https://example.com'; + + return ( + <> + <ArticleJsonLd + title={post.title} + description={post.excerpt} + publishedTime={post.publishedAt} + modifiedTime={post.updatedAt} + authorName={post.author.name} + authorUrl={`${baseUrl}/author/${post.author.slug}`} + images={[post.coverImage]} + url={`${baseUrl}/blog/${slug}`} + siteName="Meine Website" + logoUrl={`${baseUrl}/logo.png`} + /> + + <BreadcrumbJsonLd + items={[ + { name: 'Home', url: baseUrl }, + { name: 'Blog', url: `${baseUrl}/blog` }, + { name: post.title } + ]} + /> + + <article> + <h1>{post.title}</h1> + <div dangerouslySetInnerHTML={{ __html: post.content }} /> + </article> + </> + ); +} + +// app/products/[slug]/page.tsx - Product with JSON-LD +import { ProductJsonLd } from '@/components/jsonld/ProductJsonLd'; +import { BreadcrumbJsonLd } from '@/components/jsonld/BreadcrumbJsonLd'; +import { FAQJsonLd } from '@/components/jsonld/FAQJsonLd'; +import { getProduct } from '@/lib/products'; + +export default async function ProductPage({ + params +}: { + params: Promise<{ slug: string }> +}) { + const { slug } = await params; + const product = await getProduct(slug); + + const baseUrl = 'https://example.com'; + + // Produkt-FAQs + const faqs = [ + { + question: 'Wie lange ist die Lieferzeit?', + answer: 'Die Lieferzeit beträgt 2-3 Werktage innerhalb Deutschlands.' + }, + { + question: 'Gibt es eine Garantie?', + answer: 'Ja, alle Produkte haben 2 Jahre Herstellergarantie.' + } + ]; + + return ( + <> + <ProductJsonLd + name={product.name} + description={product.description} + images={product.images} + brand={product.brand} + sku={product.sku} + price={product.price} + currency="EUR" + availability={product.inStock ? 'InStock' : 'OutOfStock'} + ratingValue={product.rating} + reviewCount={product.reviewCount} + url={`${baseUrl}/products/${slug}`} + /> + + <BreadcrumbJsonLd + items={[ + { name: 'Home', url: baseUrl }, + { name: 'Produkte', url: `${baseUrl}/products` }, + { name: product.category, url: `${baseUrl}/products?category=${product.categorySlug}` }, + { name: product.name } + ]} + /> + + <FAQJsonLd items={faqs} /> + + <main> + <h1>{product.name}</h1> + {/* Product content */} + </main> + </> + ); +} +``` + +--- + +## Advanced Schemas + +```tsx +// components/jsonld/HowToJsonLd.tsx +import { JsonLd } from './JsonLd'; +import type { HowTo, HowToStep } from '@/lib/jsonld/types'; + +interface Step { + name: string; + text: string; + image?: string; +} + +interface HowToJsonLdProps { + name: string; + description: string; + image?: string; + totalTime?: string; // e.g., "PT30M" for 30 minutes + steps: Step[]; +} + +export function HowToJsonLd({ + name, + description, + image, + totalTime, + steps +}: HowToJsonLdProps) { + const howToSteps: HowToStep[] = steps.map((step, index) => ({ + '@type': 'HowToStep', + name: step.name, + text: step.text, + ...(step.image && { image: step.image }), + url: `#step-${index + 1}` + })); + + const data: HowTo = { + '@context': 'https://schema.org', + '@type': 'HowTo', + name, + description, + ...(image && { image }), + ...(totalTime && { totalTime }), + step: howToSteps + }; + + return <JsonLd data={data} id="howto-jsonld" />; +} + +// components/jsonld/EventJsonLd.tsx +import { JsonLd } from './JsonLd'; +import type { Event, Place, VirtualLocation, Offer } from '@/lib/jsonld/types'; + +interface EventJsonLdProps { + name: string; + description: string; + startDate: string; + endDate?: string; + location: { + type: 'physical' | 'virtual' | 'hybrid'; + name?: string; + address?: { + street: string; + city: string; + region: string; + postalCode: string; + country: string; + }; + url?: string; + }; + organizer: { + name: string; + url: string; + }; + image?: string; + offers?: { + price: number; + currency: string; + url: string; + validFrom?: string; + }; + status?: 'scheduled' | 'cancelled' | 'postponed'; +} + +export function EventJsonLd({ + name, + description, + startDate, + endDate, + location, + organizer, + image, + offers, + status = 'scheduled' +}: EventJsonLdProps) { + const eventLocation = location.type === 'virtual' ? { + '@type': 'VirtualLocation' as const, + url: location.url! + } : { + '@type': 'Place' as const, + name: location.name!, + address: { + '@type': 'PostalAddress' as const, + streetAddress: location.address!.street, + addressLocality: location.address!.city, + addressRegion: location.address!.region, + postalCode: location.address!.postalCode, + addressCountry: location.address!.country + } + }; + + const attendanceMode = { + physical: 'https://schema.org/OfflineEventAttendanceMode', + virtual: 'https://schema.org/OnlineEventAttendanceMode', + hybrid: 'https://schema.org/MixedEventAttendanceMode' + }[location.type] as Event['eventAttendanceMode']; + + const eventStatus = { + scheduled: 'https://schema.org/EventScheduled', + cancelled: 'https://schema.org/EventCancelled', + postponed: 'https://schema.org/EventPostponed' + }[status] as Event['eventStatus']; + + const data: Event & { image?: string } = { + '@context': 'https://schema.org', + '@type': 'Event', + name, + description, + startDate, + ...(endDate && { endDate }), + location: eventLocation, + organizer: { + '@type': 'Organization', + name: organizer.name, + url: organizer.url + }, + eventAttendanceMode: attendanceMode, + eventStatus, + ...(image && { image }), + ...(offers && { + offers: { + '@type': 'Offer', + price: offers.price, + priceCurrency: offers.currency, + url: offers.url, + availability: 'https://schema.org/InStock', + ...(offers.validFrom && { validFrom: offers.validFrom }) + } + }) + }; + + return <JsonLd data={data} id="event-jsonld" />; +} +``` + +--- + +## Validation & Testing + +```typescript +// lib/jsonld/validator.ts - Schema Validation +import Ajv from 'ajv'; +import addFormats from 'ajv-formats'; + +const ajv = new Ajv({ allErrors: true }); +addFormats(ajv); + +// Article Schema +const articleSchema = { + type: 'object', + required: ['@context', '@type', 'headline', 'datePublished', 'author', 'publisher'], + properties: { + '@context': { const: 'https://schema.org' }, + '@type': { enum: ['Article', 'BlogPosting', 'NewsArticle', 'TechArticle'] }, + headline: { type: 'string', maxLength: 110 }, + description: { type: 'string', maxLength: 320 }, + image: { + oneOf: [ + { type: 'string', format: 'uri' }, + { type: 'array', items: { type: 'string', format: 'uri' } } + ] + }, + datePublished: { type: 'string', format: 'date-time' }, + dateModified: { type: 'string', format: 'date-time' }, + author: { + type: 'object', + required: ['@type', 'name'], + properties: { + '@type': { const: 'Person' }, + name: { type: 'string' }, + url: { type: 'string', format: 'uri' } + } + }, + publisher: { + type: 'object', + required: ['@type', 'name', 'logo'], + properties: { + '@type': { const: 'Organization' }, + name: { type: 'string' }, + logo: { type: 'object' } + } + } + } +}; + +export function validateArticleSchema(data: unknown): { valid: boolean; errors?: string[] } { + const validate = ajv.compile(articleSchema); + const valid = validate(data); + + return { + valid, + errors: validate.errors?.map(e => `${e.instancePath} ${e.message}`) + }; +} + +// Test in Development +// app/api/validate-jsonld/route.ts +import { NextResponse } from 'next/server'; +import { validateArticleSchema } from '@/lib/jsonld/validator'; + +export async function POST(request: Request) { + const data = await request.json(); + const result = validateArticleSchema(data); + + return NextResponse.json(result); +} +``` + +--- + +## Schema Type Reference + +| Schema Type | Rich Result | Verwendung | +|-------------|-------------|------------| +| **Article** | Article snippet | Blog, News | +| **Product** | Product snippet | E-Commerce | +| **FAQPage** | FAQ accordion | Support, Info | +| **HowTo** | Step-by-step | Tutorials | +| **Event** | Event listing | Veranstaltungen | +| **LocalBusiness** | Knowledge panel | Lokale Firmen | +| **BreadcrumbList** | Breadcrumb trail | Navigation | +| **Organization** | Knowledge panel | Unternehmen | +| **Person** | Knowledge panel | Personen | +| **Recipe** | Recipe card | Kochrezepte | +| **JobPosting** | Job listing | Stellenanzeigen | +| **Course** | Course info | Online-Kurse | + +--- + +## Fazit + +Strukturierte Daten mit JSON-LD bieten: + +1. **Rich Snippets**: Bessere Sichtbarkeit in Suchergebnissen +2. **Type Safety**: TypeScript Definitionen für Schema.org +3. **Reusable Components**: Wiederverwendbare React Komponenten +4. **Validation**: Schema-Validierung für Qualitätssicherung + +Strukturierte Daten sind essentiell für moderne SEO. + +--- + +## Bildprompts + +1. "Google search results showing rich snippets, FAQ accordion, product ratings" +2. "Schema.org hierarchy diagram, connected nodes visualization" +3. "JSON-LD code in browser developer tools, structured data testing" + +--- + +## Quellen + +- [Schema.org Documentation](https://schema.org/) +- [Google Structured Data Guidelines](https://developers.google.com/search/docs/appearance/structured-data) +- [Google Rich Results Test](https://search.google.com/test/rich-results) +- [JSON-LD Specification](https://json-ld.org/) diff --git a/blog-posts/77-image-optimization-web.md b/blog-posts/77-image-optimization-web.md new file mode 100644 index 0000000..5b730e3 --- /dev/null +++ b/blog-posts/77-image-optimization-web.md @@ -0,0 +1,756 @@ +# Image Optimization für Web + +**Meta-Description:** Bilder für Web optimieren. Next.js Image, AVIF/WebP, Responsive Images und Lazy Loading für beste Performance. + +**Keywords:** Image Optimization, WebP, AVIF, Next.js Image, Responsive Images, Lazy Loading, srcset, Performance + +--- + +## Einführung + +**Bilder** machen oft 50-70% der Seitengröße aus. Mit **Next.js Image Component**, **modernen Formaten** (AVIF, WebP) und **Responsive Images** kann die Ladezeit drastisch reduziert werden. Dieser Guide zeigt Best Practices für optimale Bildperformance. + +--- + +## Image Optimization Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ IMAGE OPTIMIZATION PIPELINE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Format Comparison: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Original PNG (1200x800): 2.4 MB │ │ +│ │ ├── JPEG (quality 80): 180 KB (-92%) │ │ +│ │ ├── WebP (quality 80): 120 KB (-95%) │ │ +│ │ └── AVIF (quality 70): 80 KB (-97%) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Browser Support (2026): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ AVIF: Chrome ✓ Firefox ✓ Safari ✓ Edge ✓ │ │ +│ │ WebP: Chrome ✓ Firefox ✓ Safari ✓ Edge ✓ │ │ +│ │ JPEG: Universal ✓ │ │ +│ │ PNG: Universal ✓ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Responsive Strategy: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Mobile (< 640px): 640w - 40KB │ │ +│ │ Tablet (< 1024px): 1024w - 80KB │ │ +│ │ Desktop (< 1920px): 1920w - 150KB │ │ +│ │ Retina (2x): 2x scaling │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Loading Strategies: │ +│ ├── eager: Above-the-fold, LCP images │ +│ ├── lazy: Below-the-fold, defer loading │ +│ └── blur: Placeholder während Loading │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Next.js Image Component + +```tsx +// components/OptimizedImage.tsx +import Image from 'next/image'; + +interface OptimizedImageProps { + src: string; + alt: string; + width?: number; + height?: number; + priority?: boolean; + fill?: boolean; + sizes?: string; + className?: string; + quality?: number; +} + +export function OptimizedImage({ + src, + alt, + width, + height, + priority = false, + fill = false, + sizes, + className, + quality = 80 +}: OptimizedImageProps) { + // Blur Placeholder generieren (optional) + const blurDataURL = `data:image/svg+xml;base64,${toBase64(shimmer(width || 700, height || 475))}`; + + return ( + <Image + src={src} + alt={alt} + width={fill ? undefined : width} + height={fill ? undefined : height} + fill={fill} + priority={priority} + quality={quality} + sizes={sizes || '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw'} + className={className} + placeholder="blur" + blurDataURL={blurDataURL} + style={fill ? { objectFit: 'cover' } : undefined} + /> + ); +} + +// Shimmer Placeholder Generator +const shimmer = (w: number, h: number) => ` +<svg width="${w}" height="${h}" xmlns="http://www.w3.org/2000/svg"> + <defs> + <linearGradient id="g"> + <stop stop-color="#f6f7f8" offset="0%"/> + <stop stop-color="#edeef1" offset="20%"/> + <stop stop-color="#f6f7f8" offset="40%"/> + <stop stop-color="#f6f7f8" offset="100%"/> + </linearGradient> + </defs> + <rect width="${w}" height="${h}" fill="#f6f7f8"/> + <rect width="${w}" height="${h}" fill="url(#g)"> + <animate attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite"/> + </rect> +</svg>`; + +const toBase64 = (str: string) => + typeof window === 'undefined' + ? Buffer.from(str).toString('base64') + : window.btoa(str); +``` + +```tsx +// components/ResponsiveHero.tsx - Hero Image mit verschiedenen Breakpoints +import Image from 'next/image'; + +interface ResponsiveHeroProps { + desktopSrc: string; + mobileSrc: string; + alt: string; +} + +export function ResponsiveHero({ desktopSrc, mobileSrc, alt }: ResponsiveHeroProps) { + return ( + <div className="relative w-full h-[50vh] md:h-[70vh]"> + {/* Mobile Image */} + <Image + src={mobileSrc} + alt={alt} + fill + priority + sizes="100vw" + className="object-cover md:hidden" + /> + + {/* Desktop Image */} + <Image + src={desktopSrc} + alt={alt} + fill + priority + sizes="100vw" + className="hidden md:block object-cover" + /> + </div> + ); +} + +// components/ArtDirectedImage.tsx - Art Direction mit Picture Element +export function ArtDirectedImage({ + sources, + fallback, + alt +}: { + sources: Array<{ srcSet: string; media: string }>; + fallback: string; + alt: string; +}) { + return ( + <picture> + {sources.map((source, i) => ( + <source key={i} srcSet={source.srcSet} media={source.media} /> + ))} + <img src={fallback} alt={alt} loading="lazy" /> + </picture> + ); +} +``` + +--- + +## Next.js Image Configuration + +```javascript +// next.config.js +/** @type {import('next').NextConfig} */ +const nextConfig = { + images: { + // Moderne Formate (AVIF hat Priorität) + formats: ['image/avif', 'image/webp'], + + // Device Sizes für srcset + deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + + // Image Sizes für kleinere Bilder (Icons, Thumbnails) + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + + // Remote Patterns für externe Bilder + remotePatterns: [ + { + protocol: 'https', + hostname: 'images.unsplash.com', + pathname: '/**' + }, + { + protocol: 'https', + hostname: 'cdn.example.com', + pathname: '/images/**' + }, + { + protocol: 'https', + hostname: '*.cloudinary.com' + } + ], + + // Minimum Cache Time (30 Tage) + minimumCacheTTL: 60 * 60 * 24 * 30, + + // Deaktiviert static imports für Images + disableStaticImages: false, + + // Content-Disposition Header + contentDispositionType: 'inline', + + // Dangerously Allow SVG (Vorsicht!) + dangerouslyAllowSVG: false, + + // Unoptimized für Static Export + // unoptimized: true + } +}; + +module.exports = nextConfig; +``` + +--- + +## Image Processing with Sharp + +```typescript +// scripts/optimize-images.ts +import sharp from 'sharp'; +import { glob } from 'glob'; +import path from 'path'; +import fs from 'fs/promises'; + +interface OptimizationOptions { + quality: number; + formats: ('webp' | 'avif' | 'jpeg')[]; + sizes: number[]; + outputDir: string; +} + +async function optimizeImage( + inputPath: string, + options: OptimizationOptions +) { + const image = sharp(inputPath); + const metadata = await image.metadata(); + const baseName = path.basename(inputPath, path.extname(inputPath)); + + const results: string[] = []; + + for (const format of options.formats) { + for (const width of options.sizes) { + // Skip wenn größer als Original + if (metadata.width && width > metadata.width) continue; + + const outputName = `${baseName}-${width}w.${format}`; + const outputPath = path.join(options.outputDir, outputName); + + let pipeline = image.clone().resize(width, null, { + withoutEnlargement: true, + fit: 'inside' + }); + + switch (format) { + case 'webp': + pipeline = pipeline.webp({ + quality: options.quality, + effort: 6 // 0-6, higher = slower + better + }); + break; + case 'avif': + pipeline = pipeline.avif({ + quality: options.quality, + effort: 6 // 0-9 + }); + break; + case 'jpeg': + pipeline = pipeline.jpeg({ + quality: options.quality, + mozjpeg: true + }); + break; + } + + await pipeline.toFile(outputPath); + results.push(outputPath); + } + } + + return results; +} + +// Batch Processing +async function batchOptimize(inputDir: string, outputDir: string) { + const files = await glob(`${inputDir}/**/*.{jpg,jpeg,png,webp}`); + + await fs.mkdir(outputDir, { recursive: true }); + + const options: OptimizationOptions = { + quality: 80, + formats: ['avif', 'webp', 'jpeg'], + sizes: [640, 1024, 1920, 2560], + outputDir + }; + + console.log(`Processing ${files.length} images...`); + + for (const file of files) { + const results = await optimizeImage(file, options); + console.log(`${path.basename(file)}: Generated ${results.length} variants`); + } +} + +// Blur Placeholder Generation +async function generateBlurPlaceholder(imagePath: string): Promise<string> { + const buffer = await sharp(imagePath) + .resize(10, 10, { fit: 'inside' }) + .blur() + .toBuffer(); + + return `data:image/jpeg;base64,${buffer.toString('base64')}`; +} + +// LQIP (Low Quality Image Placeholder) +async function generateLQIP(imagePath: string): Promise<{ + base64: string; + width: number; + height: number; +}> { + const metadata = await sharp(imagePath).metadata(); + + const buffer = await sharp(imagePath) + .resize(20, null, { fit: 'inside' }) + .jpeg({ quality: 20 }) + .toBuffer(); + + return { + base64: `data:image/jpeg;base64,${buffer.toString('base64')}`, + width: metadata.width || 0, + height: metadata.height || 0 + }; +} +``` + +--- + +## Responsive Images Pattern + +```tsx +// components/ResponsiveImage.tsx +'use client'; + +import { useState } from 'react'; + +interface ImageSource { + src: string; + width: number; + format: 'avif' | 'webp' | 'jpeg'; +} + +interface ResponsiveImageProps { + sources: ImageSource[]; + alt: string; + sizes: string; + priority?: boolean; + className?: string; + blurDataURL?: string; +} + +export function ResponsiveImage({ + sources, + alt, + sizes, + priority = false, + className, + blurDataURL +}: ResponsiveImageProps) { + const [isLoaded, setIsLoaded] = useState(false); + + // Gruppiere nach Format + const avifSources = sources.filter(s => s.format === 'avif'); + const webpSources = sources.filter(s => s.format === 'webp'); + const jpegSources = sources.filter(s => s.format === 'jpeg'); + + const generateSrcSet = (sources: ImageSource[]) => + sources.map(s => `${s.src} ${s.width}w`).join(', '); + + const fallbackSrc = jpegSources[jpegSources.length - 1]?.src || ''; + + return ( + <div className={`relative ${className}`}> + {/* Blur Placeholder */} + {blurDataURL && !isLoaded && ( + <img + src={blurDataURL} + alt="" + aria-hidden="true" + className="absolute inset-0 w-full h-full object-cover blur-lg scale-110" + /> + )} + + <picture> + {/* AVIF Sources */} + {avifSources.length > 0 && ( + <source + type="image/avif" + srcSet={generateSrcSet(avifSources)} + sizes={sizes} + /> + )} + + {/* WebP Sources */} + {webpSources.length > 0 && ( + <source + type="image/webp" + srcSet={generateSrcSet(webpSources)} + sizes={sizes} + /> + )} + + {/* JPEG Fallback */} + <img + src={fallbackSrc} + srcSet={generateSrcSet(jpegSources)} + sizes={sizes} + alt={alt} + loading={priority ? 'eager' : 'lazy'} + decoding="async" + onLoad={() => setIsLoaded(true)} + className={`w-full h-full object-cover transition-opacity duration-300 ${ + isLoaded ? 'opacity-100' : 'opacity-0' + }`} + /> + </picture> + </div> + ); +} +``` + +--- + +## Lazy Loading Strategies + +```tsx +// components/LazyImage.tsx - Intersection Observer +'use client'; + +import { useState, useRef, useEffect } from 'react'; + +interface LazyImageProps { + src: string; + alt: string; + width: number; + height: number; + threshold?: number; + rootMargin?: string; +} + +export function LazyImage({ + src, + alt, + width, + height, + threshold = 0.1, + rootMargin = '100px' +}: LazyImageProps) { + const [isInView, setIsInView] = useState(false); + const [isLoaded, setIsLoaded] = useState(false); + const imgRef = useRef<HTMLDivElement>(null); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsInView(true); + observer.disconnect(); + } + }, + { threshold, rootMargin } + ); + + if (imgRef.current) { + observer.observe(imgRef.current); + } + + return () => observer.disconnect(); + }, [threshold, rootMargin]); + + const aspectRatio = (height / width) * 100; + + return ( + <div + ref={imgRef} + className="relative overflow-hidden bg-gray-100" + style={{ paddingBottom: `${aspectRatio}%` }} + > + {isInView && ( + <img + src={src} + alt={alt} + onLoad={() => setIsLoaded(true)} + className={`absolute inset-0 w-full h-full object-cover transition-opacity duration-500 ${ + isLoaded ? 'opacity-100' : 'opacity-0' + }`} + /> + )} + + {!isLoaded && ( + <div className="absolute inset-0 animate-pulse bg-gray-200" /> + )} + </div> + ); +} + +// hooks/useLazyImages.ts - Batch Lazy Loading +export function useLazyImages() { + useEffect(() => { + if ('loading' in HTMLImageElement.prototype) { + // Native lazy loading supported + const images = document.querySelectorAll<HTMLImageElement>('img[data-src]'); + images.forEach(img => { + img.src = img.dataset.src!; + img.removeAttribute('data-src'); + }); + } else { + // Fallback to Intersection Observer + const observer = new IntersectionObserver( + (entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const img = entry.target as HTMLImageElement; + img.src = img.dataset.src!; + img.removeAttribute('data-src'); + observer.unobserve(img); + } + }); + }, + { rootMargin: '50px' } + ); + + document.querySelectorAll('img[data-src]').forEach(img => { + observer.observe(img); + }); + } + }, []); +} +``` + +--- + +## Image Gallery with Lightbox + +```tsx +// components/ImageGallery.tsx +'use client'; + +import { useState } from 'react'; +import Image from 'next/image'; + +interface GalleryImage { + src: string; + alt: string; + width: number; + height: number; + thumbnail?: string; +} + +interface ImageGalleryProps { + images: GalleryImage[]; + columns?: 2 | 3 | 4; +} + +export function ImageGallery({ images, columns = 3 }: ImageGalleryProps) { + const [selectedIndex, setSelectedIndex] = useState<number | null>(null); + + const columnClass = { + 2: 'grid-cols-2', + 3: 'grid-cols-2 md:grid-cols-3', + 4: 'grid-cols-2 md:grid-cols-3 lg:grid-cols-4' + }[columns]; + + return ( + <> + <div className={`grid ${columnClass} gap-4`}> + {images.map((image, index) => ( + <button + key={index} + onClick={() => setSelectedIndex(index)} + className="relative aspect-square overflow-hidden rounded-lg group" + > + <Image + src={image.thumbnail || image.src} + alt={image.alt} + fill + sizes="(max-width: 768px) 50vw, 33vw" + className="object-cover transition-transform group-hover:scale-105" + /> + </button> + ))} + </div> + + {/* Lightbox */} + {selectedIndex !== null && ( + <Lightbox + images={images} + currentIndex={selectedIndex} + onClose={() => setSelectedIndex(null)} + onNavigate={setSelectedIndex} + /> + )} + </> + ); +} + +// Lightbox Component +function Lightbox({ + images, + currentIndex, + onClose, + onNavigate +}: { + images: GalleryImage[]; + currentIndex: number; + onClose: () => void; + onNavigate: (index: number) => void; +}) { + const current = images[currentIndex]; + + const handlePrev = () => { + onNavigate(currentIndex === 0 ? images.length - 1 : currentIndex - 1); + }; + + const handleNext = () => { + onNavigate(currentIndex === images.length - 1 ? 0 : currentIndex + 1); + }; + + // Keyboard Navigation + useEffect(() => { + const handleKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + if (e.key === 'ArrowLeft') handlePrev(); + if (e.key === 'ArrowRight') handleNext(); + }; + + window.addEventListener('keydown', handleKey); + return () => window.removeEventListener('keydown', handleKey); + }, [currentIndex]); + + return ( + <div + className="fixed inset-0 z-50 bg-black/90 flex items-center justify-center" + onClick={onClose} + > + <button + onClick={(e) => { e.stopPropagation(); handlePrev(); }} + className="absolute left-4 p-2 text-white hover:bg-white/10 rounded" + > + ← + </button> + + <div + className="relative max-w-4xl max-h-[80vh]" + onClick={(e) => e.stopPropagation()} + > + <Image + src={current.src} + alt={current.alt} + width={current.width} + height={current.height} + className="max-h-[80vh] w-auto" + priority + /> + </div> + + <button + onClick={(e) => { e.stopPropagation(); handleNext(); }} + className="absolute right-4 p-2 text-white hover:bg-white/10 rounded" + > + → + </button> + + <button + onClick={onClose} + className="absolute top-4 right-4 p-2 text-white hover:bg-white/10 rounded" + > + ✕ + </button> + + <div className="absolute bottom-4 text-white"> + {currentIndex + 1} / {images.length} + </div> + </div> + ); +} +``` + +--- + +## Format Comparison + +| Format | Compression | Quality | Browser Support | Best For | +|--------|-------------|---------|-----------------|----------| +| **AVIF** | Beste | Excellent | Modern browsers | Photos, Complex | +| **WebP** | Sehr gut | Very Good | All modern | General use | +| **JPEG** | Gut | Good | Universal | Photos fallback | +| **PNG** | Keine | Lossless | Universal | Transparenz | +| **SVG** | N/A | Vector | Universal | Icons, Logos | + +--- + +## Fazit + +Image Optimization umfasst: + +1. **Moderne Formate**: AVIF/WebP mit Fallbacks +2. **Responsive Images**: srcset und sizes +3. **Lazy Loading**: Native oder Intersection Observer +4. **Blur Placeholders**: Bessere UX während Loading + +Optimierte Bilder sind der Schlüssel zu schnellen Websites. + +--- + +## Bildprompts + +1. "Image optimization pipeline diagram, format conversion flow" +2. "Before and after image compression comparison, file size reduction" +3. "Responsive images loading on different devices, mobile to desktop" + +--- + +## Quellen + +- [Next.js Image Optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) +- [Web.dev Image Optimization](https://web.dev/fast/use-imagemin-to-compress-images) +- [Sharp Documentation](https://sharp.pixelplumbing.com/) +- [MDN Responsive Images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images) diff --git a/blog-posts/78-technical-seo-checklist.md b/blog-posts/78-technical-seo-checklist.md new file mode 100644 index 0000000..7b79a22 --- /dev/null +++ b/blog-posts/78-technical-seo-checklist.md @@ -0,0 +1,584 @@ +# Technical SEO Checklist + +**Meta-Description:** Komplette Technical SEO Checklist für 2026. Crawling, Indexierung, Site Structure und Performance-Optimierung für bessere Rankings. + +**Keywords:** Technical SEO, SEO Checklist, Crawling, Indexierung, Site Structure, Page Speed, Mobile SEO, SEO Audit + +--- + +## Einführung + +**Technical SEO** bildet das Fundament für erfolgreiche Suchmaschinenoptimierung. Ohne saubere technische Basis nützt der beste Content wenig. Diese Checklist deckt alle wichtigen Aspekte ab – von **Crawling** bis **Performance**. + +--- + +## Technical SEO Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ TECHNICAL SEO PYRAMID │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────┐ │ +│ │ Content │ │ +│ │ & UX │ │ +│ └────┬────┘ │ +│ ┌─────────┴─────────┐ │ +│ │ On-Page SEO │ │ +│ │ (Title, Meta,H1) │ │ +│ └─────────┬─────────┘ │ +│ ┌──────────────┴──────────────┐ │ +│ │ Site Architecture │ │ +│ │ (URLs, Internal Links) │ │ +│ └──────────────┬──────────────┘ │ +│ ┌───────────────────┴───────────────────┐ │ +│ │ Crawling & Indexing │ │ +│ │ (robots.txt, sitemap, canonicals) │ │ +│ └───────────────────┬───────────────────┘ │ +│ ┌──────────────────────┴──────────────────────┐ │ +│ │ Server & Performance │ │ +│ │ (HTTPS, Speed, Core Web Vitals, Mobile) │ │ +│ └─────────────────────────────────────────────┘ │ +│ │ +│ Priority Levels: │ +│ 🔴 Critical - Must fix immediately │ +│ 🟡 Important - Fix soon │ +│ 🟢 Nice to have - Optimize when possible │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Server & Security + +```typescript +// lib/seo/audit/server.ts +interface ServerAuditResult { + https: boolean; + httpToHttpsRedirect: boolean; + wwwRedirect: 'www' | 'non-www' | 'inconsistent'; + responseTime: number; + serverHeaders: Record<string, string>; + issues: string[]; +} + +async function auditServer(url: string): Promise<ServerAuditResult> { + const issues: string[] = []; + + // HTTPS Check + const https = url.startsWith('https://'); + if (!https) { + issues.push('🔴 Site is not using HTTPS'); + } + + // HTTP to HTTPS Redirect + const httpUrl = url.replace('https://', 'http://'); + const httpResponse = await fetch(httpUrl, { redirect: 'manual' }); + const httpToHttpsRedirect = httpResponse.status === 301 && + httpResponse.headers.get('location')?.startsWith('https://'); + + if (!httpToHttpsRedirect) { + issues.push('🔴 HTTP does not redirect to HTTPS'); + } + + // WWW Consistency + const withWww = url.includes('www.'); + const testUrl = withWww + ? url.replace('www.', '') + : url.replace('://', '://www.'); + + const wwwResponse = await fetch(testUrl, { redirect: 'manual' }); + const wwwRedirect = wwwResponse.status === 301 ? (withWww ? 'www' : 'non-www') : 'inconsistent'; + + if (wwwRedirect === 'inconsistent') { + issues.push('🟡 WWW and non-WWW versions not properly redirected'); + } + + // Response Time + const start = Date.now(); + await fetch(url); + const responseTime = Date.now() - start; + + if (responseTime > 500) { + issues.push(`🟡 Server response time is slow: ${responseTime}ms`); + } + + // Security Headers + const response = await fetch(url); + const serverHeaders: Record<string, string> = {}; + response.headers.forEach((value, key) => { + serverHeaders[key] = value; + }); + + const requiredHeaders = [ + 'strict-transport-security', + 'x-content-type-options', + 'x-frame-options' + ]; + + requiredHeaders.forEach(header => { + if (!serverHeaders[header]) { + issues.push(`🟢 Missing security header: ${header}`); + } + }); + + return { + https, + httpToHttpsRedirect, + wwwRedirect, + responseTime, + serverHeaders, + issues + }; +} +``` + +```typescript +// middleware.ts - Security Headers +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export function middleware(request: NextRequest) { + const response = NextResponse.next(); + + // Security Headers + response.headers.set('X-DNS-Prefetch-Control', 'on'); + response.headers.set('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload'); + response.headers.set('X-Content-Type-Options', 'nosniff'); + response.headers.set('X-Frame-Options', 'SAMEORIGIN'); + response.headers.set('X-XSS-Protection', '1; mode=block'); + response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin'); + + // Permissions Policy + response.headers.set( + 'Permissions-Policy', + 'camera=(), microphone=(), geolocation=(self), interest-cohort=()' + ); + + return response; +} +``` + +--- + +## Crawling & Indexing + +```typescript +// app/robots.ts - Optimized robots.txt +import { MetadataRoute } from 'next'; + +export default function robots(): MetadataRoute.Robots { + const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com'; + + return { + rules: [ + { + userAgent: '*', + allow: '/', + disallow: [ + '/api/', + '/admin/', + '/_next/', + '/private/', + '/*.json$', + '/*?*', // Query Parameter + '/search?*' + ] + }, + { + userAgent: 'Googlebot', + allow: '/', + disallow: ['/admin/', '/api/'] + }, + { + // Block bad bots + userAgent: ['AhrefsBot', 'SemrushBot', 'DotBot'], + disallow: '/' + } + ], + sitemap: `${baseUrl}/sitemap.xml`, + host: baseUrl + }; +} + +// app/sitemap.ts - Dynamic Sitemap +import { MetadataRoute } from 'next'; +import { getAllPages, getAllPosts, getAllProducts } from '@/lib/content'; + +export default async function sitemap(): Promise<MetadataRoute.Sitemap> { + const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com'; + + // Static Pages + const staticPages: MetadataRoute.Sitemap = [ + { + url: baseUrl, + lastModified: new Date(), + changeFrequency: 'daily', + priority: 1.0 + }, + { + url: `${baseUrl}/about`, + lastModified: new Date(), + changeFrequency: 'monthly', + priority: 0.8 + }, + { + url: `${baseUrl}/contact`, + lastModified: new Date(), + changeFrequency: 'yearly', + priority: 0.5 + }, + { + url: `${baseUrl}/blog`, + lastModified: new Date(), + changeFrequency: 'daily', + priority: 0.9 + } + ]; + + // Dynamic Pages + const pages = await getAllPages(); + const dynamicPages: MetadataRoute.Sitemap = pages.map(page => ({ + url: `${baseUrl}/${page.slug}`, + lastModified: new Date(page.updatedAt), + changeFrequency: 'monthly', + priority: 0.7 + })); + + // Blog Posts + const posts = await getAllPosts(); + const blogPosts: MetadataRoute.Sitemap = posts.map(post => ({ + url: `${baseUrl}/blog/${post.slug}`, + lastModified: new Date(post.updatedAt), + changeFrequency: 'weekly', + priority: 0.8 + })); + + // Products + const products = await getAllProducts(); + const productPages: MetadataRoute.Sitemap = products.map(product => ({ + url: `${baseUrl}/products/${product.slug}`, + lastModified: new Date(product.updatedAt), + changeFrequency: 'daily', + priority: 0.9 + })); + + return [...staticPages, ...dynamicPages, ...blogPosts, ...productPages]; +} +``` + +```typescript +// lib/seo/canonical.ts - Canonical URL Helper +export function getCanonicalUrl(path: string, locale?: string): string { + const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com'; + + // Remove trailing slash + const cleanPath = path.replace(/\/$/, ''); + + // Remove query parameters for canonical + const pathWithoutQuery = cleanPath.split('?')[0]; + + // Add locale if needed + if (locale && locale !== 'de') { + return `${baseUrl}/${locale}${pathWithoutQuery}`; + } + + return `${baseUrl}${pathWithoutQuery}`; +} + +// Usage in page +export async function generateMetadata({ params }: PageProps): Promise<Metadata> { + return { + alternates: { + canonical: getCanonicalUrl(`/blog/${params.slug}`) + } + }; +} +``` + +--- + +## URL Structure + +```typescript +// lib/seo/url-utils.ts +export function createSEOFriendlyUrl(title: string): string { + return title + .toLowerCase() + .trim() + // Umlaute ersetzen + .replace(/ä/g, 'ae') + .replace(/ö/g, 'oe') + .replace(/ü/g, 'ue') + .replace(/ß/g, 'ss') + // Sonderzeichen entfernen + .replace(/[^a-z0-9\s-]/g, '') + // Mehrere Leerzeichen zu einem + .replace(/\s+/g, ' ') + // Leerzeichen zu Bindestrich + .replace(/\s/g, '-') + // Mehrere Bindestriche zu einem + .replace(/-+/g, '-') + // Max Länge + .substring(0, 60); +} + +// URL Audit +interface URLAuditResult { + url: string; + issues: string[]; + suggestions: string[]; +} + +export function auditURL(url: string): URLAuditResult { + const issues: string[] = []; + const suggestions: string[] = []; + + // Length check + if (url.length > 75) { + issues.push('🟡 URL is too long (> 75 characters)'); + } + + // Uppercase check + if (url !== url.toLowerCase()) { + issues.push('🟡 URL contains uppercase characters'); + } + + // Underscore check + if (url.includes('_')) { + issues.push('🟡 URL contains underscores (use hyphens instead)'); + } + + // Double slashes + if (url.includes('//') && !url.startsWith('http')) { + issues.push('🔴 URL contains double slashes'); + } + + // Query parameters + if (url.includes('?')) { + suggestions.push('Consider using clean URLs without query parameters'); + } + + // Trailing slash consistency + if (!url.endsWith('/') && !url.includes('.')) { + suggestions.push('Consider adding trailing slash for consistency'); + } + + return { url, issues, suggestions }; +} +``` + +--- + +## On-Page SEO + +```typescript +// lib/seo/audit/onpage.ts +interface OnPageAuditResult { + title: { value: string; length: number; issues: string[] }; + metaDescription: { value: string; length: number; issues: string[] }; + h1: { count: number; value: string; issues: string[] }; + headings: { h2: number; h3: number; h4: number; issues: string[] }; + images: { total: number; withoutAlt: number; issues: string[] }; + links: { internal: number; external: number; broken: number; issues: string[] }; +} + +async function auditOnPage(html: string, url: string): Promise<OnPageAuditResult> { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + + // Title + const title = doc.querySelector('title')?.textContent || ''; + const titleIssues: string[] = []; + if (!title) titleIssues.push('🔴 Missing title tag'); + else if (title.length < 30) titleIssues.push('🟡 Title too short (< 30 chars)'); + else if (title.length > 60) titleIssues.push('🟡 Title too long (> 60 chars)'); + + // Meta Description + const metaDesc = doc.querySelector('meta[name="description"]')?.getAttribute('content') || ''; + const metaDescIssues: string[] = []; + if (!metaDesc) metaDescIssues.push('🔴 Missing meta description'); + else if (metaDesc.length < 120) metaDescIssues.push('🟡 Meta description too short'); + else if (metaDesc.length > 160) metaDescIssues.push('🟡 Meta description too long'); + + // H1 + const h1Elements = doc.querySelectorAll('h1'); + const h1Issues: string[] = []; + if (h1Elements.length === 0) h1Issues.push('🔴 Missing H1 tag'); + else if (h1Elements.length > 1) h1Issues.push('🟡 Multiple H1 tags found'); + + // Heading Structure + const h2Count = doc.querySelectorAll('h2').length; + const h3Count = doc.querySelectorAll('h3').length; + const h4Count = doc.querySelectorAll('h4').length; + const headingIssues: string[] = []; + if (h2Count === 0) headingIssues.push('🟡 No H2 headings found'); + + // Images + const images = doc.querySelectorAll('img'); + const imagesWithoutAlt = Array.from(images).filter( + img => !img.getAttribute('alt') + ).length; + const imageIssues: string[] = []; + if (imagesWithoutAlt > 0) { + imageIssues.push(`🟡 ${imagesWithoutAlt} images missing alt text`); + } + + // Links + const links = doc.querySelectorAll('a[href]'); + const baseUrl = new URL(url); + let internalLinks = 0; + let externalLinks = 0; + const linkIssues: string[] = []; + + links.forEach(link => { + const href = link.getAttribute('href') || ''; + if (href.startsWith('/') || href.includes(baseUrl.hostname)) { + internalLinks++; + } else if (href.startsWith('http')) { + externalLinks++; + + // Check for nofollow on external links + const rel = link.getAttribute('rel') || ''; + if (!rel.includes('nofollow') && !rel.includes('sponsored')) { + // Suggestion, not issue + } + } + }); + + return { + title: { value: title, length: title.length, issues: titleIssues }, + metaDescription: { value: metaDesc, length: metaDesc.length, issues: metaDescIssues }, + h1: { count: h1Elements.length, value: h1Elements[0]?.textContent || '', issues: h1Issues }, + headings: { h2: h2Count, h3: h3Count, h4: h4Count, issues: headingIssues }, + images: { total: images.length, withoutAlt: imagesWithoutAlt, issues: imageIssues }, + links: { internal: internalLinks, external: externalLinks, broken: 0, issues: linkIssues } + }; +} +``` + +--- + +## Complete SEO Checklist + +```typescript +// lib/seo/checklist.ts +export const technicalSEOChecklist = { + server: [ + { id: 'https', priority: 'critical', item: 'HTTPS enabled', automated: true }, + { id: 'http-redirect', priority: 'critical', item: 'HTTP redirects to HTTPS (301)', automated: true }, + { id: 'www-redirect', priority: 'important', item: 'WWW/non-WWW consistency', automated: true }, + { id: 'response-time', priority: 'important', item: 'Server response < 500ms', automated: true }, + { id: 'security-headers', priority: 'nice', item: 'Security headers configured', automated: true } + ], + + crawling: [ + { id: 'robots-txt', priority: 'critical', item: 'robots.txt exists and valid', automated: true }, + { id: 'sitemap', priority: 'critical', item: 'XML Sitemap exists', automated: true }, + { id: 'sitemap-submitted', priority: 'important', item: 'Sitemap submitted to Search Console', automated: false }, + { id: 'no-crawl-errors', priority: 'critical', item: 'No crawl errors in Search Console', automated: false }, + { id: 'canonical-tags', priority: 'critical', item: 'Canonical tags on all pages', automated: true } + ], + + indexing: [ + { id: 'indexable', priority: 'critical', item: 'Important pages are indexable', automated: true }, + { id: 'noindex-correct', priority: 'important', item: 'noindex only on non-important pages', automated: true }, + { id: 'no-duplicate-content', priority: 'critical', item: 'No duplicate content issues', automated: true }, + { id: 'hreflang', priority: 'important', item: 'hreflang tags for multilingual sites', automated: true } + ], + + urlStructure: [ + { id: 'url-readable', priority: 'important', item: 'URLs are human-readable', automated: true }, + { id: 'url-lowercase', priority: 'important', item: 'URLs are lowercase', automated: true }, + { id: 'url-hyphens', priority: 'nice', item: 'URLs use hyphens (not underscores)', automated: true }, + { id: 'url-length', priority: 'nice', item: 'URLs under 75 characters', automated: true }, + { id: 'no-parameters', priority: 'nice', item: 'Clean URLs without parameters', automated: true } + ], + + onPage: [ + { id: 'title-tag', priority: 'critical', item: 'Unique title tag (50-60 chars)', automated: true }, + { id: 'meta-description', priority: 'critical', item: 'Meta description (150-160 chars)', automated: true }, + { id: 'h1-tag', priority: 'critical', item: 'Single H1 per page', automated: true }, + { id: 'heading-hierarchy', priority: 'important', item: 'Proper heading hierarchy', automated: true }, + { id: 'image-alt', priority: 'important', item: 'All images have alt text', automated: true }, + { id: 'internal-links', priority: 'important', item: 'Internal linking structure', automated: false } + ], + + performance: [ + { id: 'lcp', priority: 'critical', item: 'LCP < 2.5s', automated: true }, + { id: 'inp', priority: 'critical', item: 'INP < 200ms', automated: true }, + { id: 'cls', priority: 'critical', item: 'CLS < 0.1', automated: true }, + { id: 'mobile-friendly', priority: 'critical', item: 'Mobile-friendly design', automated: true }, + { id: 'page-size', priority: 'important', item: 'Page size < 3MB', automated: true } + ], + + structuredData: [ + { id: 'jsonld', priority: 'important', item: 'JSON-LD structured data', automated: true }, + { id: 'valid-schema', priority: 'important', item: 'Schema validates in Rich Results Test', automated: false }, + { id: 'breadcrumbs', priority: 'nice', item: 'Breadcrumb schema', automated: true } + ] +}; + +// Generate Audit Report +export function generateSEOReport(results: Record<string, boolean>): string { + let report = '# Technical SEO Audit Report\n\n'; + + Object.entries(technicalSEOChecklist).forEach(([category, items]) => { + report += `## ${category.charAt(0).toUpperCase() + category.slice(1)}\n\n`; + + items.forEach(item => { + const status = results[item.id]; + const emoji = status ? '✅' : (item.priority === 'critical' ? '❌' : '⚠️'); + report += `${emoji} ${item.item}\n`; + }); + + report += '\n'; + }); + + return report; +} +``` + +--- + +## Checklist Summary + +| Category | Critical | Important | Nice to Have | +|----------|----------|-----------|--------------| +| **Server** | HTTPS, Redirects | Response time | Security headers | +| **Crawling** | robots.txt, Sitemap | Search Console | Crawl budget | +| **Indexing** | Canonical, No duplicates | hreflang | Coverage report | +| **URLs** | - | Readable, lowercase | Short URLs | +| **On-Page** | Title, Meta, H1 | Alt text, Headings | Schema | +| **Performance** | Core Web Vitals | Page size | Resource hints | + +--- + +## Fazit + +Technical SEO Checklist erfordert: + +1. **Server-Basics**: HTTPS, Redirects, Security +2. **Crawling**: robots.txt, Sitemap, Canonical +3. **On-Page**: Title, Meta, Headings, Images +4. **Performance**: Core Web Vitals optimieren + +Regelmäßige Audits sichern die technische Basis. + +--- + +## Bildprompts + +1. "SEO audit dashboard showing checklist items, green checkmarks" +2. "Technical SEO pyramid diagram, layered structure" +3. "Search Console crawl stats visualization, data charts" + +--- + +## Quellen + +- [Google Search Central](https://developers.google.com/search/docs) +- [Ahrefs Technical SEO Guide](https://ahrefs.com/blog/technical-seo/) +- [Moz Technical SEO](https://moz.com/learn/seo/technical-seo) +- [Web.dev SEO](https://web.dev/learn/seo/) diff --git a/blog-posts/79-international-seo-i18n.md b/blog-posts/79-international-seo-i18n.md new file mode 100644 index 0000000..19fae3b --- /dev/null +++ b/blog-posts/79-international-seo-i18n.md @@ -0,0 +1,691 @@ +# International SEO & i18n + +**Meta-Description:** Internationales SEO mit Next.js. hreflang Tags, URL-Strukturen, Content-Lokalisierung und Multi-Language Routing. + +**Keywords:** International SEO, i18n, hreflang, Multilingual, Localization, Next.js i18n, URL Structure, Language Targeting + +--- + +## Einführung + +**Internationales SEO** erfordert mehr als nur Übersetzung. Richtige **hreflang-Implementation**, **URL-Strukturen** und **Lokalisierung** sind entscheidend. Dieser Guide zeigt Best Practices für mehrsprachige Websites mit Next.js 15. + +--- + +## International SEO Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ INTERNATIONAL SEO ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ URL Strategies: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 1. Subdirectories (Recommended): │ │ +│ │ example.com/de/ │ │ +│ │ example.com/en/ │ │ +│ │ example.com/fr/ │ │ +│ │ ✓ Easy to set up │ │ +│ │ ✓ Single domain authority │ │ +│ │ ✓ Clear language indication │ │ +│ │ │ │ +│ │ 2. Subdomains: │ │ +│ │ de.example.com │ │ +│ │ en.example.com │ │ +│ │ ✗ Separate domain authority │ │ +│ │ ✓ Easy geo-targeting │ │ +│ │ │ │ +│ │ 3. ccTLDs (Country-Specific): │ │ +│ │ example.de │ │ +│ │ example.co.uk │ │ +│ │ ✓ Strong geo-signal │ │ +│ │ ✗ Higher cost, separate SEO │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ hreflang Implementation: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ <link rel="alternate" hreflang="de" │ │ +│ │ href="https://example.com/de/page" /> │ │ +│ │ <link rel="alternate" hreflang="en" │ │ +│ │ href="https://example.com/en/page" /> │ │ +│ │ <link rel="alternate" hreflang="x-default" │ │ +│ │ href="https://example.com/page" /> │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Content Strategy: │ +│ ├── Translation (Basic) │ +│ ├── Localization (Cultural Adaptation) │ +│ └── Transcreation (Full Adaptation) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Next.js i18n Setup + +```typescript +// next.config.js - i18n Configuration +/** @type {import('next').NextConfig} */ +const nextConfig = { + // Built-in i18n (für Pages Router, App Router nutzt Middleware) + // i18n: { + // locales: ['de', 'en', 'fr', 'es'], + // defaultLocale: 'de', + // localeDetection: true + // } +}; + +module.exports = nextConfig; + +// middleware.ts - App Router i18n +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { match } from '@formatjs/intl-localematcher'; +import Negotiator from 'negotiator'; + +const locales = ['de', 'en', 'fr', 'es']; +const defaultLocale = 'de'; + +function getLocale(request: NextRequest): string { + // Check cookie first + const cookieLocale = request.cookies.get('NEXT_LOCALE')?.value; + if (cookieLocale && locales.includes(cookieLocale)) { + return cookieLocale; + } + + // Then check Accept-Language header + const headers = { 'accept-language': request.headers.get('accept-language') || '' }; + const languages = new Negotiator({ headers }).languages(); + + try { + return match(languages, locales, defaultLocale); + } catch { + return defaultLocale; + } +} + +export function middleware(request: NextRequest) { + const pathname = request.nextUrl.pathname; + + // Skip for static files and API routes + if ( + pathname.startsWith('/_next') || + pathname.startsWith('/api') || + pathname.startsWith('/static') || + pathname.includes('.') // files with extensions + ) { + return NextResponse.next(); + } + + // Check if pathname has locale + const pathnameHasLocale = locales.some( + locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}` + ); + + if (pathnameHasLocale) { + return NextResponse.next(); + } + + // Redirect to locale + const locale = getLocale(request); + const newUrl = new URL(`/${locale}${pathname}`, request.url); + + return NextResponse.redirect(newUrl); +} + +export const config = { + matcher: ['/((?!_next|api|static|.*\\..*).*)', '/'] +}; +``` + +--- + +## App Router Structure + +```typescript +// app/[locale]/layout.tsx - Locale Layout +import { notFound } from 'next/navigation'; +import { NextIntlClientProvider } from 'next-intl'; +import { getMessages, getTranslations } from 'next-intl/server'; + +const locales = ['de', 'en', 'fr', 'es']; + +export function generateStaticParams() { + return locales.map(locale => ({ locale })); +} + +export async function generateMetadata({ + params +}: { + params: Promise<{ locale: string }> +}) { + const { locale } = await params; + const t = await getTranslations({ locale, namespace: 'Metadata' }); + + return { + title: { + template: `%s | ${t('siteName')}`, + default: t('siteName') + }, + description: t('siteDescription'), + alternates: { + canonical: `https://example.com/${locale}`, + languages: { + 'de': 'https://example.com/de', + 'en': 'https://example.com/en', + 'fr': 'https://example.com/fr', + 'es': 'https://example.com/es', + 'x-default': 'https://example.com' + } + } + }; +} + +export default async function LocaleLayout({ + children, + params +}: { + children: React.ReactNode; + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + + if (!locales.includes(locale)) { + notFound(); + } + + const messages = await getMessages(); + + return ( + <html lang={locale} dir={locale === 'ar' ? 'rtl' : 'ltr'}> + <body> + <NextIntlClientProvider messages={messages}> + {children} + </NextIntlClientProvider> + </body> + </html> + ); +} +``` + +```typescript +// app/[locale]/page.tsx - Localized Page +import { getTranslations } from 'next-intl/server'; +import { Link } from '@/navigation'; + +interface PageProps { + params: Promise<{ locale: string }>; +} + +export default async function HomePage({ params }: PageProps) { + const { locale } = await params; + const t = await getTranslations('Home'); + + return ( + <main> + <h1>{t('title')}</h1> + <p>{t('description')}</p> + + <Link href="/about">{t('aboutLink')}</Link> + </main> + ); +} +``` + +--- + +## hreflang Implementation + +```typescript +// lib/i18n/hreflang.ts - hreflang Generator +interface HreflangConfig { + locales: string[]; + defaultLocale: string; + baseUrl: string; +} + +interface HreflangLink { + hrefLang: string; + href: string; +} + +export function generateHreflangLinks( + path: string, + config: HreflangConfig +): HreflangLink[] { + const { locales, defaultLocale, baseUrl } = config; + + const links: HreflangLink[] = locales.map(locale => ({ + hrefLang: locale, + href: `${baseUrl}/${locale}${path}` + })); + + // x-default für Sprachauswahl / Default + links.push({ + hrefLang: 'x-default', + href: `${baseUrl}/${defaultLocale}${path}` + }); + + return links; +} + +// components/Hreflang.tsx - Hreflang Component +import { headers } from 'next/headers'; + +const config: HreflangConfig = { + locales: ['de', 'en', 'fr', 'es'], + defaultLocale: 'de', + baseUrl: 'https://example.com' +}; + +export async function Hreflang() { + const headersList = await headers(); + const pathname = headersList.get('x-pathname') || '/'; + + // Remove locale from pathname + const path = pathname.replace(/^\/(de|en|fr|es)/, ''); + + const links = generateHreflangLinks(path, config); + + return ( + <> + {links.map(link => ( + <link + key={link.hrefLang} + rel="alternate" + hrefLang={link.hrefLang} + href={link.href} + /> + ))} + </> + ); +} + +// Usage in layout.tsx +export default function RootLayout({ children }) { + return ( + <html> + <head> + <Hreflang /> + </head> + <body>{children}</body> + </html> + ); +} +``` + +```typescript +// app/[locale]/[...slug]/page.tsx - Dynamic hreflang +import { Metadata } from 'next'; + +interface PageProps { + params: Promise<{ locale: string; slug: string[] }>; +} + +export async function generateMetadata({ params }: PageProps): Promise<Metadata> { + const { locale, slug } = await params; + const path = `/${slug.join('/')}`; + + const locales = ['de', 'en', 'fr', 'es']; + const baseUrl = 'https://example.com'; + + // Generate language alternates + const languages: Record<string, string> = {}; + locales.forEach(loc => { + languages[loc] = `${baseUrl}/${loc}${path}`; + }); + languages['x-default'] = `${baseUrl}/de${path}`; + + return { + alternates: { + canonical: `${baseUrl}/${locale}${path}`, + languages + } + }; +} +``` + +--- + +## Translation Management + +```typescript +// messages/de.json +{ + "Metadata": { + "siteName": "Meine Website", + "siteDescription": "Die beste Website für..." + }, + "Home": { + "title": "Willkommen", + "description": "Entdecken Sie unsere Produkte und Dienstleistungen.", + "aboutLink": "Über uns" + }, + "Navigation": { + "home": "Startseite", + "about": "Über uns", + "products": "Produkte", + "contact": "Kontakt" + }, + "Common": { + "readMore": "Mehr lesen", + "learnMore": "Mehr erfahren", + "submit": "Absenden", + "cancel": "Abbrechen" + } +} + +// messages/en.json +{ + "Metadata": { + "siteName": "My Website", + "siteDescription": "The best website for..." + }, + "Home": { + "title": "Welcome", + "description": "Discover our products and services.", + "aboutLink": "About us" + }, + "Navigation": { + "home": "Home", + "about": "About", + "products": "Products", + "contact": "Contact" + }, + "Common": { + "readMore": "Read more", + "learnMore": "Learn more", + "submit": "Submit", + "cancel": "Cancel" + } +} +``` + +```typescript +// lib/i18n/index.ts - Translation Utilities +import { getRequestConfig } from 'next-intl/server'; + +export default getRequestConfig(async ({ locale }) => ({ + messages: (await import(`@/messages/${locale}.json`)).default +})); + +// i18n.ts - Configuration +export const locales = ['de', 'en', 'fr', 'es'] as const; +export type Locale = (typeof locales)[number]; + +export const localeNames: Record<Locale, string> = { + de: 'Deutsch', + en: 'English', + fr: 'Français', + es: 'Español' +}; + +export const localeFlags: Record<Locale, string> = { + de: '🇩🇪', + en: '🇬🇧', + fr: '🇫🇷', + es: '🇪🇸' +}; + +export const defaultLocale: Locale = 'de'; + +// Check if locale is valid +export function isValidLocale(locale: string): locale is Locale { + return locales.includes(locale as Locale); +} +``` + +--- + +## Language Switcher + +```tsx +// components/LanguageSwitcher.tsx +'use client'; + +import { useLocale } from 'next-intl'; +import { usePathname, useRouter } from '@/navigation'; +import { locales, localeNames, localeFlags, Locale } from '@/lib/i18n'; + +export function LanguageSwitcher() { + const locale = useLocale(); + const pathname = usePathname(); + const router = useRouter(); + + const handleChange = (newLocale: Locale) => { + // Set cookie for persistence + document.cookie = `NEXT_LOCALE=${newLocale}; path=/; max-age=31536000`; + + // Navigate to new locale + router.replace(pathname, { locale: newLocale }); + }; + + return ( + <div className="relative"> + <select + value={locale} + onChange={(e) => handleChange(e.target.value as Locale)} + className="appearance-none bg-white border rounded-md px-4 py-2 pr-8" + aria-label="Select language" + > + {locales.map((loc) => ( + <option key={loc} value={loc}> + {localeFlags[loc]} {localeNames[loc]} + </option> + ))} + </select> + </div> + ); +} + +// Alternative: Link-based Switcher +export function LanguageSwitcherLinks() { + const locale = useLocale(); + const pathname = usePathname(); + + return ( + <nav aria-label="Language selection"> + <ul className="flex gap-2"> + {locales.map((loc) => ( + <li key={loc}> + <Link + href={pathname} + locale={loc} + className={`px-3 py-1 rounded ${ + locale === loc + ? 'bg-blue-600 text-white' + : 'bg-gray-100 hover:bg-gray-200' + }`} + aria-current={locale === loc ? 'true' : undefined} + > + {localeFlags[loc]} {loc.toUpperCase()} + </Link> + </li> + ))} + </ul> + </nav> + ); +} +``` + +--- + +## Localized Sitemap + +```typescript +// app/sitemap.ts - Multilingual Sitemap +import { MetadataRoute } from 'next'; +import { locales, defaultLocale } from '@/lib/i18n'; +import { getAllPages } from '@/lib/content'; + +export default async function sitemap(): Promise<MetadataRoute.Sitemap> { + const baseUrl = 'https://example.com'; + const pages = await getAllPages(); + + const sitemap: MetadataRoute.Sitemap = []; + + // Static pages for each locale + const staticPaths = ['', '/about', '/contact', '/products']; + + for (const path of staticPaths) { + for (const locale of locales) { + const alternates: Record<string, string> = {}; + + locales.forEach(loc => { + alternates[loc] = `${baseUrl}/${loc}${path}`; + }); + alternates['x-default'] = `${baseUrl}/${defaultLocale}${path}`; + + sitemap.push({ + url: `${baseUrl}/${locale}${path}`, + lastModified: new Date(), + changeFrequency: path === '' ? 'daily' : 'weekly', + priority: path === '' ? 1.0 : 0.8, + alternates: { + languages: alternates + } + }); + } + } + + // Dynamic pages (blog posts, products) + for (const page of pages) { + for (const locale of locales) { + // Check if translation exists + const hasTranslation = page.translations?.includes(locale); + if (!hasTranslation && locale !== defaultLocale) continue; + + const alternates: Record<string, string> = {}; + const availableLocales = page.translations || [defaultLocale]; + + availableLocales.forEach(loc => { + alternates[loc] = `${baseUrl}/${loc}/${page.slug}`; + }); + alternates['x-default'] = `${baseUrl}/${defaultLocale}/${page.slug}`; + + sitemap.push({ + url: `${baseUrl}/${locale}/${page.slug}`, + lastModified: new Date(page.updatedAt), + changeFrequency: 'weekly', + priority: 0.7, + alternates: { + languages: alternates + } + }); + } + } + + return sitemap; +} +``` + +--- + +## SEO Best Practices + +```typescript +// lib/i18n/seo.ts - i18n SEO Helpers +import { Metadata } from 'next'; + +interface LocalizedSEOConfig { + title: Record<string, string>; + description: Record<string, string>; + keywords: Record<string, string[]>; + ogImage?: Record<string, string>; +} + +export function generateLocalizedMetadata( + config: LocalizedSEOConfig, + locale: string, + path: string +): Metadata { + const baseUrl = 'https://example.com'; + const locales = ['de', 'en', 'fr', 'es']; + + // Language alternates + const languages: Record<string, string> = {}; + locales.forEach(loc => { + languages[loc] = `${baseUrl}/${loc}${path}`; + }); + languages['x-default'] = `${baseUrl}/de${path}`; + + return { + title: config.title[locale], + description: config.description[locale], + keywords: config.keywords[locale], + + alternates: { + canonical: `${baseUrl}/${locale}${path}`, + languages + }, + + openGraph: { + title: config.title[locale], + description: config.description[locale], + url: `${baseUrl}/${locale}${path}`, + siteName: locale === 'de' ? 'Meine Website' : 'My Website', + locale: getOGLocale(locale), + alternateLocales: locales + .filter(l => l !== locale) + .map(getOGLocale), + images: config.ogImage?.[locale] ? [config.ogImage[locale]] : undefined + } + }; +} + +function getOGLocale(locale: string): string { + const map: Record<string, string> = { + de: 'de_DE', + en: 'en_US', + fr: 'fr_FR', + es: 'es_ES' + }; + return map[locale] || 'en_US'; +} +``` + +--- + +## Checklist + +| Aspect | Implementation | Priority | +|--------|---------------|----------| +| **URL Structure** | Subdirectories (/de/, /en/) | Critical | +| **hreflang** | All pages, x-default | Critical | +| **Canonical** | Per locale | Critical | +| **Sitemap** | Multilingual with alternates | Important | +| **Language Detection** | Accept-Language header | Important | +| **Switcher** | Visible, accessible | Important | +| **Content** | Fully translated, not auto | Important | +| **Local Keywords** | Research per market | Important | + +--- + +## Fazit + +International SEO erfordert: + +1. **Klare URL-Struktur**: Subdirectories für beste Balance +2. **hreflang Tags**: Korrekt auf allen Seiten +3. **Lokalisierung**: Mehr als nur Übersetzung +4. **Technische Basis**: Sitemaps, Canonical, Meta + +Internationales SEO öffnet globale Märkte. + +--- + +## Bildprompts + +1. "World map with language flags, multilingual website concept" +2. "hreflang tag implementation diagram, connected language versions" +3. "Language switcher UI designs, dropdown and flags" + +--- + +## Quellen + +- [Google i18n SEO Guide](https://developers.google.com/search/docs/specialty/international) +- [Next.js Internationalization](https://nextjs.org/docs/app/building-your-application/routing/internationalization) +- [next-intl Documentation](https://next-intl-docs.vercel.app/) +- [Ahrefs International SEO](https://ahrefs.com/blog/international-seo/) diff --git a/blog-posts/80-performance-monitoring-analytics.md b/blog-posts/80-performance-monitoring-analytics.md new file mode 100644 index 0000000..c92f455 --- /dev/null +++ b/blog-posts/80-performance-monitoring-analytics.md @@ -0,0 +1,804 @@ +# Performance Monitoring & Analytics + +**Meta-Description:** Web Performance Monitoring mit Next.js. Real User Monitoring, Analytics Integration, Error Tracking und Dashboards. + +**Keywords:** Performance Monitoring, RUM, Analytics, Error Tracking, Web Vitals, Sentry, Google Analytics, Vercel Analytics + +--- + +## Einführung + +**Performance Monitoring** geht über einmalige Tests hinaus. **Real User Monitoring (RUM)** erfasst echte Nutzerdaten, **Error Tracking** findet Probleme früh. Dieser Guide zeigt moderne Monitoring-Strategien für Next.js Anwendungen. + +--- + +## Monitoring Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ PERFORMANCE MONITORING STACK │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Data Collection: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Browser │ │ +│ │ ├── Web Vitals (LCP, INP, CLS) │ │ +│ │ ├── Resource Timing │ │ +│ │ ├── Navigation Timing │ │ +│ │ ├── Long Tasks │ │ +│ │ └── JS Errors │ │ +│ │ │ │ +│ │ Server │ │ +│ │ ├── Response Times │ │ +│ │ ├── Server Errors │ │ +│ │ ├── Database Queries │ │ +│ │ └── External API Calls │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Tools & Services: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Analytics: │ │ +│ │ ├── Vercel Analytics / Speed Insights │ │ +│ │ ├── Google Analytics 4 │ │ +│ │ └── Plausible / Fathom (Privacy-focused) │ │ +│ │ │ │ +│ │ Error Tracking: │ │ +│ │ ├── Sentry │ │ +│ │ ├── Bugsnag │ │ +│ │ └── LogRocket │ │ +│ │ │ │ +│ │ APM (Application Performance): │ │ +│ │ ├── Datadog │ │ +│ │ ├── New Relic │ │ +│ │ └── Grafana Cloud │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Alerting: │ +│ ├── Slack / Discord notifications │ +│ ├── Email alerts │ +│ ├── PagerDuty integration │ +│ └── Custom webhooks │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Web Vitals Monitoring + +```typescript +// lib/monitoring/web-vitals.ts +import { onLCP, onINP, onCLS, onFCP, onTTFB, Metric } from 'web-vitals'; + +interface VitalsData { + name: string; + value: number; + rating: 'good' | 'needs-improvement' | 'poor'; + delta: number; + id: string; + navigationType: string; + url: string; + timestamp: number; +} + +type ReportCallback = (data: VitalsData) => void; + +const thresholds: Record<string, [number, number]> = { + LCP: [2500, 4000], + INP: [200, 500], + CLS: [0.1, 0.25], + FCP: [1800, 3000], + TTFB: [800, 1800] +}; + +function getRating(name: string, value: number): 'good' | 'needs-improvement' | 'poor' { + const [good, poor] = thresholds[name] || [0, 0]; + if (value <= good) return 'good'; + if (value <= poor) return 'needs-improvement'; + return 'poor'; +} + +function createReporter(callback: ReportCallback) { + return (metric: Metric) => { + callback({ + name: metric.name, + value: metric.value, + rating: getRating(metric.name, metric.value), + delta: metric.delta, + id: metric.id, + navigationType: metric.navigationType || 'unknown', + url: window.location.href, + timestamp: Date.now() + }); + }; +} + +export function initWebVitals(callback: ReportCallback) { + const reporter = createReporter(callback); + + onLCP(reporter); + onINP(reporter); + onCLS(reporter); + onFCP(reporter); + onTTFB(reporter); +} + +// Aggregated reporting (batch) +let vitalsBuffer: VitalsData[] = []; +let flushTimeout: NodeJS.Timeout | null = null; + +export function initBatchedWebVitals(endpoint: string) { + const flush = () => { + if (vitalsBuffer.length === 0) return; + + const data = [...vitalsBuffer]; + vitalsBuffer = []; + + // Use sendBeacon for reliability + if (navigator.sendBeacon) { + navigator.sendBeacon(endpoint, JSON.stringify(data)); + } else { + fetch(endpoint, { + method: 'POST', + body: JSON.stringify(data), + headers: { 'Content-Type': 'application/json' }, + keepalive: true + }); + } + }; + + initWebVitals((data) => { + vitalsBuffer.push(data); + + // Flush after 5 seconds or when buffer is full + if (flushTimeout) clearTimeout(flushTimeout); + if (vitalsBuffer.length >= 10) { + flush(); + } else { + flushTimeout = setTimeout(flush, 5000); + } + }); + + // Flush on page unload + window.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'hidden') { + flush(); + } + }); +} +``` + +```typescript +// app/api/vitals/route.ts - Vitals API Endpoint +import { NextResponse } from 'next/server'; + +interface VitalsPayload { + name: string; + value: number; + rating: string; + url: string; + timestamp: number; +} + +export async function POST(request: Request) { + try { + const data: VitalsPayload[] = await request.json(); + + // Validate data + const validData = data.filter(item => + item.name && typeof item.value === 'number' + ); + + // Store in database or forward to analytics + await storeVitals(validData); + + // Check for poor performance and alert + const poorVitals = validData.filter(v => v.rating === 'poor'); + if (poorVitals.length > 0) { + await sendAlert(poorVitals); + } + + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Vitals API error:', error); + return NextResponse.json({ error: 'Failed to process vitals' }, { status: 500 }); + } +} + +async function storeVitals(data: VitalsPayload[]) { + // Example: Store in InfluxDB, PostgreSQL, or analytics service + // await db.vitals.createMany({ data }); +} + +async function sendAlert(poorVitals: VitalsPayload[]) { + // Example: Send Slack notification + const message = poorVitals.map(v => + `${v.name}: ${v.value.toFixed(2)} (${v.rating}) on ${v.url}` + ).join('\n'); + + // await fetch(process.env.SLACK_WEBHOOK_URL, { + // method: 'POST', + // body: JSON.stringify({ text: `Poor Web Vitals detected:\n${message}` }) + // }); +} +``` + +--- + +## Vercel Analytics Integration + +```typescript +// app/layout.tsx - Vercel Analytics +import { Analytics } from '@vercel/analytics/react'; +import { SpeedInsights } from '@vercel/speed-insights/next'; + +export default function RootLayout({ + children +}: { + children: React.ReactNode +}) { + return ( + <html lang="de"> + <body> + {children} + <Analytics /> + <SpeedInsights /> + </body> + </html> + ); +} + +// Custom event tracking +// components/Button.tsx +'use client'; + +import { track } from '@vercel/analytics'; + +export function CTAButton({ label, action }: { label: string; action: string }) { + const handleClick = () => { + track('cta_click', { + label, + action, + page: window.location.pathname + }); + }; + + return ( + <button onClick={handleClick} className="btn-primary"> + {label} + </button> + ); +} +``` + +--- + +## Sentry Error Tracking + +```typescript +// sentry.client.config.ts +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + + // Performance Monitoring + tracesSampleRate: 0.1, // 10% of transactions + + // Session Replay + replaysSessionSampleRate: 0.1, + replaysOnErrorSampleRate: 1.0, + + integrations: [ + Sentry.replayIntegration({ + maskAllText: true, + blockAllMedia: true + }), + Sentry.browserTracingIntegration() + ], + + // Environment + environment: process.env.NODE_ENV, + + // Release tracking + release: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA, + + // Ignore specific errors + ignoreErrors: [ + 'ResizeObserver loop limit exceeded', + 'Non-Error exception captured' + ], + + // Before send hook + beforeSend(event, hint) { + // Filter out sensitive data + if (event.request?.headers) { + delete event.request.headers['authorization']; + delete event.request.headers['cookie']; + } + return event; + } +}); + +// sentry.server.config.ts +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.SENTRY_DSN, + tracesSampleRate: 0.1, + environment: process.env.NODE_ENV +}); + +// sentry.edge.config.ts +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.SENTRY_DSN, + tracesSampleRate: 0.1 +}); +``` + +```typescript +// lib/monitoring/sentry.ts - Custom Sentry Helpers +import * as Sentry from '@sentry/nextjs'; + +// Capture custom error with context +export function captureError( + error: Error, + context?: Record<string, unknown> +) { + Sentry.withScope(scope => { + if (context) { + Object.entries(context).forEach(([key, value]) => { + scope.setExtra(key, value); + }); + } + Sentry.captureException(error); + }); +} + +// Set user context +export function setUser(user: { id: string; email?: string; username?: string }) { + Sentry.setUser(user); +} + +// Clear user on logout +export function clearUser() { + Sentry.setUser(null); +} + +// Custom breadcrumb +export function addBreadcrumb( + category: string, + message: string, + data?: Record<string, unknown> +) { + Sentry.addBreadcrumb({ + category, + message, + data, + level: 'info' + }); +} + +// Performance transaction +export function startTransaction(name: string, op: string) { + return Sentry.startInactiveSpan({ + name, + op, + forceTransaction: true + }); +} + +// Usage example +export async function fetchDataWithMonitoring<T>( + url: string, + options?: RequestInit +): Promise<T> { + const span = startTransaction(`fetch ${url}`, 'http.client'); + + try { + addBreadcrumb('http', `Fetching ${url}`); + + const response = await fetch(url, options); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + captureError(error as Error, { url, options }); + throw error; + } finally { + span?.end(); + } +} +``` + +--- + +## Google Analytics 4 + +```typescript +// lib/analytics/gtag.ts +declare global { + interface Window { + gtag: (...args: unknown[]) => void; + dataLayer: unknown[]; + } +} + +export const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_ID; + +// Initialize GA4 +export function initGA() { + if (!GA_MEASUREMENT_ID) return; + + window.dataLayer = window.dataLayer || []; + window.gtag = function gtag() { + window.dataLayer.push(arguments); + }; + + window.gtag('js', new Date()); + window.gtag('config', GA_MEASUREMENT_ID, { + page_path: window.location.pathname, + anonymize_ip: true + }); +} + +// Page view +export function pageview(url: string) { + if (!GA_MEASUREMENT_ID) return; + + window.gtag('config', GA_MEASUREMENT_ID, { + page_path: url + }); +} + +// Custom event +export function event( + action: string, + category: string, + label?: string, + value?: number +) { + if (!GA_MEASUREMENT_ID) return; + + window.gtag('event', action, { + event_category: category, + event_label: label, + value: value + }); +} + +// E-commerce events +export function purchaseEvent(transaction: { + id: string; + value: number; + currency: string; + items: Array<{ + id: string; + name: string; + price: number; + quantity: number; + }>; +}) { + if (!GA_MEASUREMENT_ID) return; + + window.gtag('event', 'purchase', { + transaction_id: transaction.id, + value: transaction.value, + currency: transaction.currency, + items: transaction.items + }); +} + +// Web Vitals to GA4 +export function sendWebVitalToGA(metric: { + name: string; + value: number; + id: string; +}) { + if (!GA_MEASUREMENT_ID) return; + + window.gtag('event', metric.name, { + event_category: 'Web Vitals', + value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value), + event_label: metric.id, + non_interaction: true + }); +} +``` + +```tsx +// components/GoogleAnalytics.tsx +'use client'; + +import Script from 'next/script'; +import { usePathname, useSearchParams } from 'next/navigation'; +import { useEffect, Suspense } from 'react'; +import { GA_MEASUREMENT_ID, pageview } from '@/lib/analytics/gtag'; + +function GAPageTracker() { + const pathname = usePathname(); + const searchParams = useSearchParams(); + + useEffect(() => { + if (pathname) { + const url = pathname + (searchParams?.toString() ? `?${searchParams}` : ''); + pageview(url); + } + }, [pathname, searchParams]); + + return null; +} + +export function GoogleAnalytics() { + if (!GA_MEASUREMENT_ID) return null; + + return ( + <> + <Script + strategy="afterInteractive" + src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`} + /> + <Script + id="gtag-init" + strategy="afterInteractive" + dangerouslySetInnerHTML={{ + __html: ` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '${GA_MEASUREMENT_ID}', { + page_path: window.location.pathname, + anonymize_ip: true + }); + ` + }} + /> + <Suspense fallback={null}> + <GAPageTracker /> + </Suspense> + </> + ); +} +``` + +--- + +## Custom Analytics Dashboard + +```typescript +// lib/analytics/dashboard.ts +interface AnalyticsData { + pageViews: number; + uniqueVisitors: number; + avgSessionDuration: number; + bounceRate: number; + topPages: Array<{ path: string; views: number }>; + vitals: { + lcp: { avg: number; p75: number }; + inp: { avg: number; p75: number }; + cls: { avg: number; p75: number }; + }; + errors: Array<{ + message: string; + count: number; + lastSeen: Date; + }>; +} + +export async function getAnalyticsDashboardData( + startDate: Date, + endDate: Date +): Promise<AnalyticsData> { + // Aggregate data from various sources + const [vitals, pageViews, errors] = await Promise.all([ + getVitalsData(startDate, endDate), + getPageViewsData(startDate, endDate), + getErrorsData(startDate, endDate) + ]); + + return { + pageViews: pageViews.total, + uniqueVisitors: pageViews.unique, + avgSessionDuration: pageViews.avgDuration, + bounceRate: pageViews.bounceRate, + topPages: pageViews.topPages, + vitals, + errors + }; +} + +// API Route for dashboard +// app/api/analytics/dashboard/route.ts +import { NextResponse } from 'next/server'; +import { getAnalyticsDashboardData } from '@/lib/analytics/dashboard'; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const startDate = new Date(searchParams.get('start') || Date.now() - 7 * 24 * 60 * 60 * 1000); + const endDate = new Date(searchParams.get('end') || Date.now()); + + const data = await getAnalyticsDashboardData(startDate, endDate); + + return NextResponse.json(data); +} +``` + +```tsx +// app/admin/analytics/page.tsx - Analytics Dashboard +'use client'; + +import { useState, useEffect } from 'react'; +import { Card } from '@/components/ui/Card'; + +interface DashboardData { + pageViews: number; + uniqueVisitors: number; + vitals: { + lcp: { avg: number; p75: number }; + inp: { avg: number; p75: number }; + cls: { avg: number; p75: number }; + }; +} + +export default function AnalyticsDashboard() { + const [data, setData] = useState<DashboardData | null>(null); + const [loading, setLoading] = useState(true); + const [dateRange, setDateRange] = useState('7d'); + + useEffect(() => { + fetchData(); + }, [dateRange]); + + const fetchData = async () => { + setLoading(true); + const start = getStartDate(dateRange); + const res = await fetch(`/api/analytics/dashboard?start=${start.toISOString()}`); + const data = await res.json(); + setData(data); + setLoading(false); + }; + + if (loading) return <div>Loading...</div>; + if (!data) return <div>No data available</div>; + + return ( + <div className="p-6 space-y-6"> + <h1 className="text-2xl font-bold">Analytics Dashboard</h1> + + {/* Date Range Selector */} + <select + value={dateRange} + onChange={(e) => setDateRange(e.target.value)} + className="border rounded px-3 py-2" + > + <option value="24h">Last 24 hours</option> + <option value="7d">Last 7 days</option> + <option value="30d">Last 30 days</option> + </select> + + {/* Overview Cards */} + <div className="grid grid-cols-1 md:grid-cols-4 gap-4"> + <MetricCard + title="Page Views" + value={data.pageViews.toLocaleString()} + trend="+12%" + /> + <MetricCard + title="Unique Visitors" + value={data.uniqueVisitors.toLocaleString()} + trend="+8%" + /> + <MetricCard + title="LCP (p75)" + value={`${data.vitals.lcp.p75}ms`} + status={getVitalStatus('LCP', data.vitals.lcp.p75)} + /> + <MetricCard + title="INP (p75)" + value={`${data.vitals.inp.p75}ms`} + status={getVitalStatus('INP', data.vitals.inp.p75)} + /> + </div> + + {/* Web Vitals Chart */} + <Card title="Core Web Vitals"> + <VitalsChart data={data.vitals} /> + </Card> + </div> + ); +} + +function MetricCard({ + title, + value, + trend, + status +}: { + title: string; + value: string; + trend?: string; + status?: 'good' | 'needs-improvement' | 'poor'; +}) { + const statusColors = { + good: 'bg-green-100 text-green-800', + 'needs-improvement': 'bg-yellow-100 text-yellow-800', + poor: 'bg-red-100 text-red-800' + }; + + return ( + <div className="bg-white rounded-lg shadow p-4"> + <h3 className="text-sm text-gray-500">{title}</h3> + <p className="text-2xl font-bold mt-1">{value}</p> + {trend && ( + <span className="text-sm text-green-600">{trend}</span> + )} + {status && ( + <span className={`text-xs px-2 py-1 rounded ${statusColors[status]}`}> + {status} + </span> + )} + </div> + ); +} + +function getVitalStatus(name: string, value: number): 'good' | 'needs-improvement' | 'poor' { + const thresholds: Record<string, [number, number]> = { + LCP: [2500, 4000], + INP: [200, 500], + CLS: [0.1, 0.25] + }; + + const [good, poor] = thresholds[name] || [0, 0]; + if (value <= good) return 'good'; + if (value <= poor) return 'needs-improvement'; + return 'poor'; +} +``` + +--- + +## Monitoring Tools Comparison + +| Tool | Type | Pricing | Best For | +|------|------|---------|----------| +| **Vercel Analytics** | RUM | Free tier | Vercel deployments | +| **Google Analytics** | Analytics | Free | General analytics | +| **Sentry** | Error Tracking | Free tier | Error monitoring | +| **Datadog** | APM | Paid | Enterprise | +| **Plausible** | Analytics | Paid | Privacy-focused | +| **Grafana Cloud** | Observability | Free tier | Custom dashboards | + +--- + +## Fazit + +Performance Monitoring umfasst: + +1. **Web Vitals**: Kontinuierliche RUM-Erfassung +2. **Error Tracking**: Sentry für schnelle Problemlösung +3. **Analytics**: User Behavior verstehen +4. **Alerting**: Proaktive Benachrichtigungen + +Monitoring ist kein einmaliges Setup, sondern kontinuierlicher Prozess. + +--- + +## Bildprompts + +1. "Performance monitoring dashboard, real-time charts and metrics" +2. "Error tracking interface showing stack traces and user sessions" +3. "Web Vitals visualization, LCP/INP/CLS gauges with colors" + +--- + +## Quellen + +- [Vercel Analytics](https://vercel.com/docs/analytics) +- [Sentry Next.js](https://docs.sentry.io/platforms/javascript/guides/nextjs/) +- [Google Analytics 4](https://developers.google.com/analytics/devguides/collection/ga4) +- [Web Vitals Library](https://github.com/GoogleChrome/web-vitals) diff --git a/blog-posts/81-ab-testing-implementation.md b/blog-posts/81-ab-testing-implementation.md new file mode 100644 index 0000000..9291bc2 --- /dev/null +++ b/blog-posts/81-ab-testing-implementation.md @@ -0,0 +1,728 @@ +# A/B Testing Implementation + +**Meta-Description:** A/B Testing für Web-Anwendungen. Feature Flags, statistische Signifikanz und Testing-Frameworks mit Next.js. + +**Keywords:** A/B Testing, Split Testing, Feature Flags, Experimentation, Statistical Significance, Conversion Optimization + +--- + +## Einführung + +**A/B Testing** ist essentiell für datengetriebene Entscheidungen. Mit **Feature Flags**, **statistischer Auswertung** und modernen Testing-Tools können Hypothesen validiert und Conversions optimiert werden. Dieser Guide zeigt Implementation mit Next.js. + +--- + +## A/B Testing Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ A/B TESTING ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Experiment Flow: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ User Visit │ │ +│ │ ↓ │ │ +│ │ Random Assignment (Hash-based) │ │ +│ │ ↓ │ │ +│ │ ┌──────────────┬──────────────┐ │ │ +│ │ │ Control (A) │ Variant (B) │ │ │ +│ │ │ 50% │ 50% │ │ │ +│ │ └──────┬───────┴──────┬───────┘ │ │ +│ │ ↓ ↓ │ │ +│ │ Track Events Track Events │ │ +│ │ ↓ ↓ │ │ +│ │ ┌──────────────────────────┐ │ │ +│ │ │ Statistical Analysis │ │ │ +│ │ │ (Significance Test) │ │ │ +│ │ └──────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Key Metrics: │ +│ ├── Conversion Rate │ +│ ├── Click-Through Rate (CTR) │ +│ ├── Revenue per Visitor │ +│ ├── Time on Page │ +│ └── Bounce Rate │ +│ │ +│ Statistical Requirements: │ +│ ├── Sample Size: Min 1000 per variant │ +│ ├── Significance Level: 95% (p < 0.05) │ +│ ├── Power: 80% minimum │ +│ └── Test Duration: Min 1-2 weeks │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Custom A/B Testing System + +```typescript +// lib/ab-testing/types.ts +export interface Experiment { + id: string; + name: string; + description: string; + variants: Variant[]; + targetingRules?: TargetingRule[]; + status: 'draft' | 'running' | 'paused' | 'completed'; + startDate?: Date; + endDate?: Date; + metrics: string[]; +} + +export interface Variant { + id: string; + name: string; + weight: number; // 0-100 + config: Record<string, unknown>; +} + +export interface TargetingRule { + type: 'percentage' | 'userProperty' | 'url' | 'device'; + operator: 'equals' | 'contains' | 'greaterThan' | 'lessThan'; + value: string | number; +} + +export interface ExperimentAssignment { + experimentId: string; + variantId: string; + userId: string; + assignedAt: Date; +} + +export interface ExperimentEvent { + experimentId: string; + variantId: string; + userId: string; + eventName: string; + eventValue?: number; + timestamp: Date; + metadata?: Record<string, unknown>; +} +``` + +```typescript +// lib/ab-testing/assignment.ts +import { createHash } from 'crypto'; + +export function assignVariant( + userId: string, + experiment: Experiment +): Variant { + // Deterministic hash-based assignment + const hash = createHash('md5') + .update(`${experiment.id}:${userId}`) + .digest('hex'); + + // Convert first 8 chars to number (0-100) + const hashValue = parseInt(hash.substring(0, 8), 16) % 100; + + // Assign based on weights + let cumulativeWeight = 0; + for (const variant of experiment.variants) { + cumulativeWeight += variant.weight; + if (hashValue < cumulativeWeight) { + return variant; + } + } + + // Fallback to first variant + return experiment.variants[0]; +} + +// Check if user matches targeting rules +export function matchesTargeting( + user: { id: string; properties?: Record<string, unknown> }, + rules: TargetingRule[] +): boolean { + if (!rules || rules.length === 0) return true; + + return rules.every(rule => { + switch (rule.type) { + case 'percentage': + const hash = createHash('md5').update(user.id).digest('hex'); + const value = parseInt(hash.substring(0, 8), 16) % 100; + return value < (rule.value as number); + + case 'userProperty': + const propValue = user.properties?.[rule.operator]; + return propValue === rule.value; + + default: + return true; + } + }); +} +``` + +```typescript +// lib/ab-testing/client.ts +import { Experiment, Variant, ExperimentEvent } from './types'; +import { assignVariant, matchesTargeting } from './assignment'; + +class ABTestingClient { + private experiments: Map<string, Experiment> = new Map(); + private assignments: Map<string, Variant> = new Map(); + private userId: string; + private eventQueue: ExperimentEvent[] = []; + + constructor(userId: string) { + this.userId = userId; + } + + async loadExperiments(): Promise<void> { + const response = await fetch('/api/experiments'); + const experiments: Experiment[] = await response.json(); + + experiments + .filter(exp => exp.status === 'running') + .forEach(exp => this.experiments.set(exp.id, exp)); + } + + getVariant(experimentId: string): Variant | null { + // Check cache first + const cached = this.assignments.get(experimentId); + if (cached) return cached; + + const experiment = this.experiments.get(experimentId); + if (!experiment) return null; + + // Check targeting + if (!matchesTargeting({ id: this.userId }, experiment.targetingRules || [])) { + return null; + } + + // Assign variant + const variant = assignVariant(this.userId, experiment); + this.assignments.set(experimentId, variant); + + // Track assignment + this.trackEvent(experimentId, 'experiment_viewed'); + + return variant; + } + + trackEvent( + experimentId: string, + eventName: string, + eventValue?: number, + metadata?: Record<string, unknown> + ): void { + const variant = this.assignments.get(experimentId); + if (!variant) return; + + const event: ExperimentEvent = { + experimentId, + variantId: variant.id, + userId: this.userId, + eventName, + eventValue, + timestamp: new Date(), + metadata + }; + + this.eventQueue.push(event); + this.flushEvents(); + } + + private flushTimeout: NodeJS.Timeout | null = null; + + private flushEvents(): void { + if (this.flushTimeout) return; + + this.flushTimeout = setTimeout(async () => { + if (this.eventQueue.length === 0) return; + + const events = [...this.eventQueue]; + this.eventQueue = []; + + try { + await fetch('/api/experiments/events', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(events) + }); + } catch (error) { + // Re-queue on failure + this.eventQueue.unshift(...events); + } + + this.flushTimeout = null; + }, 1000); + } +} + +// Singleton +let client: ABTestingClient | null = null; + +export function getABClient(userId: string): ABTestingClient { + if (!client || client['userId'] !== userId) { + client = new ABTestingClient(userId); + } + return client; +} +``` + +--- + +## React Integration + +```tsx +// contexts/ABTestingContext.tsx +'use client'; + +import { createContext, useContext, useEffect, useState, ReactNode } from 'react'; +import { getABClient } from '@/lib/ab-testing/client'; +import { Variant } from '@/lib/ab-testing/types'; + +interface ABTestingContextValue { + getVariant: (experimentId: string) => Variant | null; + trackConversion: (experimentId: string, eventName: string, value?: number) => void; + isReady: boolean; +} + +const ABTestingContext = createContext<ABTestingContextValue | null>(null); + +export function ABTestingProvider({ + children, + userId +}: { + children: ReactNode; + userId: string; +}) { + const [isReady, setIsReady] = useState(false); + const [client, setClient] = useState(() => getABClient(userId)); + + useEffect(() => { + client.loadExperiments().then(() => setIsReady(true)); + }, [client]); + + const getVariant = (experimentId: string) => { + return client.getVariant(experimentId); + }; + + const trackConversion = (experimentId: string, eventName: string, value?: number) => { + client.trackEvent(experimentId, eventName, value); + }; + + return ( + <ABTestingContext.Provider value={{ getVariant, trackConversion, isReady }}> + {children} + </ABTestingContext.Provider> + ); +} + +export function useABTesting() { + const context = useContext(ABTestingContext); + if (!context) { + throw new Error('useABTesting must be used within ABTestingProvider'); + } + return context; +} + +// hooks/useExperiment.ts +export function useExperiment<T = unknown>( + experimentId: string, + defaultValue: T +): { variant: T; isLoading: boolean; trackConversion: (event: string, value?: number) => void } { + const { getVariant, trackConversion, isReady } = useABTesting(); + + const variant = getVariant(experimentId); + const value = variant?.config as T ?? defaultValue; + + return { + variant: value, + isLoading: !isReady, + trackConversion: (event: string, value?: number) => + trackConversion(experimentId, event, value) + }; +} +``` + +```tsx +// components/ABTest.tsx +'use client'; + +import { useExperiment } from '@/hooks/useExperiment'; +import { ReactNode } from 'react'; + +interface ABTestProps { + experimentId: string; + control: ReactNode; + variants: Record<string, ReactNode>; + fallback?: ReactNode; +} + +export function ABTest({ + experimentId, + control, + variants, + fallback = null +}: ABTestProps) { + const { variant, isLoading } = useExperiment<{ variantId: string }>( + experimentId, + { variantId: 'control' } + ); + + if (isLoading) return fallback; + + if (variant.variantId === 'control') { + return <>{control}</>; + } + + return <>{variants[variant.variantId] || control}</>; +} + +// Usage Example +export function CTASection() { + return ( + <ABTest + experimentId="cta-button-color" + control={<CTAButton color="blue">Get Started</CTAButton>} + variants={{ + green: <CTAButton color="green">Get Started</CTAButton>, + orange: <CTAButton color="orange">Start Free Trial</CTAButton> + }} + /> + ); +} + +// Tracked CTA Button +function CTAButton({ + color, + children +}: { + color: string; + children: ReactNode; +}) { + const { trackConversion } = useExperiment('cta-button-color', {}); + + const handleClick = () => { + trackConversion('cta_clicked'); + // Navigate or perform action + }; + + return ( + <button + onClick={handleClick} + className={`btn bg-${color}-600 text-white`} + > + {children} + </button> + ); +} +``` + +--- + +## Statistical Analysis + +```typescript +// lib/ab-testing/statistics.ts + +interface ExperimentResults { + control: { + visitors: number; + conversions: number; + conversionRate: number; + }; + variant: { + visitors: number; + conversions: number; + conversionRate: number; + }; + lift: number; + significance: number; + isSignificant: boolean; + confidenceInterval: [number, number]; + requiredSampleSize: number; +} + +// Z-Score for confidence levels +const Z_SCORES: Record<number, number> = { + 0.90: 1.645, + 0.95: 1.96, + 0.99: 2.576 +}; + +export function calculateResults( + controlVisitors: number, + controlConversions: number, + variantVisitors: number, + variantConversions: number, + confidenceLevel: number = 0.95 +): ExperimentResults { + const controlRate = controlConversions / controlVisitors; + const variantRate = variantConversions / variantVisitors; + + // Lift + const lift = ((variantRate - controlRate) / controlRate) * 100; + + // Standard Error + const se = Math.sqrt( + (controlRate * (1 - controlRate)) / controlVisitors + + (variantRate * (1 - variantRate)) / variantVisitors + ); + + // Z-Score + const zScore = (variantRate - controlRate) / se; + + // P-Value (two-tailed) + const pValue = 2 * (1 - normalCDF(Math.abs(zScore))); + + // Significance + const significance = (1 - pValue) * 100; + const isSignificant = pValue < (1 - confidenceLevel); + + // Confidence Interval + const z = Z_SCORES[confidenceLevel] || 1.96; + const marginOfError = z * se; + const confidenceInterval: [number, number] = [ + (variantRate - controlRate - marginOfError) * 100, + (variantRate - controlRate + marginOfError) * 100 + ]; + + // Required Sample Size (for 80% power) + const requiredSampleSize = calculateSampleSize( + controlRate, + controlRate * 1.1, // 10% MDE + 0.05, + 0.8 + ); + + return { + control: { + visitors: controlVisitors, + conversions: controlConversions, + conversionRate: controlRate * 100 + }, + variant: { + visitors: variantVisitors, + conversions: variantConversions, + conversionRate: variantRate * 100 + }, + lift, + significance, + isSignificant, + confidenceInterval, + requiredSampleSize + }; +} + +// Normal CDF approximation +function normalCDF(x: number): number { + const a1 = 0.254829592; + const a2 = -0.284496736; + const a3 = 1.421413741; + const a4 = -1.453152027; + const a5 = 1.061405429; + const p = 0.3275911; + + const sign = x < 0 ? -1 : 1; + x = Math.abs(x) / Math.sqrt(2); + + const t = 1.0 / (1.0 + p * x); + const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x); + + return 0.5 * (1.0 + sign * y); +} + +// Sample Size Calculator +export function calculateSampleSize( + baselineRate: number, + expectedRate: number, + alpha: number = 0.05, + power: number = 0.8 +): number { + const zAlpha = Z_SCORES[1 - alpha] || 1.96; + const zBeta = 0.84; // 80% power + + const p1 = baselineRate; + const p2 = expectedRate; + const pBar = (p1 + p2) / 2; + + const n = Math.ceil( + 2 * Math.pow(zAlpha * Math.sqrt(2 * pBar * (1 - pBar)) + + zBeta * Math.sqrt(p1 * (1 - p1) + p2 * (1 - p2)), 2) / + Math.pow(p1 - p2, 2) + ); + + return n; +} +``` + +--- + +## Results Dashboard + +```tsx +// app/admin/experiments/[id]/page.tsx +'use client'; + +import { useState, useEffect } from 'react'; +import { calculateResults, ExperimentResults } from '@/lib/ab-testing/statistics'; + +interface ExperimentData { + experiment: Experiment; + results: { + control: { visitors: number; conversions: number }; + variants: Record<string, { visitors: number; conversions: number }>; + }; +} + +export default function ExperimentResultsPage({ + params +}: { + params: { id: string } +}) { + const [data, setData] = useState<ExperimentData | null>(null); + const [analysis, setAnalysis] = useState<ExperimentResults | null>(null); + + useEffect(() => { + fetch(`/api/experiments/${params.id}/results`) + .then(res => res.json()) + .then(setData); + }, [params.id]); + + useEffect(() => { + if (!data) return; + + const variant = Object.values(data.results.variants)[0]; + if (!variant) return; + + const results = calculateResults( + data.results.control.visitors, + data.results.control.conversions, + variant.visitors, + variant.conversions + ); + setAnalysis(results); + }, [data]); + + if (!data || !analysis) return <div>Loading...</div>; + + return ( + <div className="p-6 space-y-6"> + <h1 className="text-2xl font-bold">{data.experiment.name}</h1> + + {/* Results Cards */} + <div className="grid grid-cols-3 gap-4"> + <ResultCard + title="Control" + visitors={analysis.control.visitors} + conversions={analysis.control.conversions} + rate={analysis.control.conversionRate} + /> + <ResultCard + title="Variant" + visitors={analysis.variant.visitors} + conversions={analysis.variant.conversions} + rate={analysis.variant.conversionRate} + isWinner={analysis.isSignificant && analysis.lift > 0} + /> + <div className="bg-white p-4 rounded-lg shadow"> + <h3 className="text-sm text-gray-500">Lift</h3> + <p className={`text-3xl font-bold ${analysis.lift > 0 ? 'text-green-600' : 'text-red-600'}`}> + {analysis.lift > 0 ? '+' : ''}{analysis.lift.toFixed(2)}% + </p> + </div> + </div> + + {/* Statistical Significance */} + <div className="bg-white p-6 rounded-lg shadow"> + <h2 className="text-lg font-semibold mb-4">Statistical Analysis</h2> + + <div className="grid grid-cols-2 gap-4"> + <div> + <p className="text-sm text-gray-500">Confidence Level</p> + <p className="text-xl font-semibold"> + {analysis.significance.toFixed(1)}% + </p> + </div> + <div> + <p className="text-sm text-gray-500">Status</p> + <span className={`px-3 py-1 rounded-full text-sm ${ + analysis.isSignificant + ? 'bg-green-100 text-green-800' + : 'bg-yellow-100 text-yellow-800' + }`}> + {analysis.isSignificant ? 'Significant' : 'Not Significant'} + </span> + </div> + <div> + <p className="text-sm text-gray-500">Confidence Interval</p> + <p className="text-xl font-semibold"> + [{analysis.confidenceInterval[0].toFixed(2)}%, {analysis.confidenceInterval[1].toFixed(2)}%] + </p> + </div> + <div> + <p className="text-sm text-gray-500">Required Sample Size</p> + <p className="text-xl font-semibold"> + {analysis.requiredSampleSize.toLocaleString()} per variant + </p> + </div> + </div> + </div> + + {/* Recommendation */} + {analysis.isSignificant && ( + <div className={`p-4 rounded-lg ${ + analysis.lift > 0 ? 'bg-green-50 border border-green-200' : 'bg-red-50 border border-red-200' + }`}> + <h3 className="font-semibold">Recommendation</h3> + <p> + {analysis.lift > 0 + ? 'The variant significantly outperforms the control. Consider rolling out to 100% of users.' + : 'The control outperforms the variant. Consider keeping the original version.'} + </p> + </div> + )} + </div> + ); +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **Sample Size** | Min 1,000 visitors per variant | +| **Duration** | Run for at least 1-2 full weeks | +| **Significance** | Wait for 95% confidence | +| **One Change** | Test one variable at a time | +| **Primary Metric** | Define before starting | +| **Segmentation** | Analyze by device, location | + +--- + +## Fazit + +A/B Testing erfordert: + +1. **Deterministic Assignment**: Hash-basierte Zuweisung +2. **Statistical Rigor**: Signifikanz-Tests +3. **Proper Tracking**: Events und Conversions +4. **Patience**: Ausreichende Sample Size abwarten + +Datengetriebene Entscheidungen führen zu besseren Produkten. + +--- + +## Bildprompts + +1. "A/B testing dashboard showing variant comparison, charts and metrics" +2. "Statistical significance visualization, confidence intervals" +3. "Experiment funnel diagram, control vs variant flow" + +--- + +## Quellen + +- [Google Optimize Best Practices](https://support.google.com/optimize) +- [Evan Miller A/B Testing Calculator](https://www.evanmiller.org/ab-testing/) +- [VWO A/B Testing Guide](https://vwo.com/ab-testing/) +- [Statistical Methods in A/B Testing](https://www.optimizely.com/optimization-glossary/statistical-significance/) diff --git a/blog-posts/82-conversion-rate-optimization.md b/blog-posts/82-conversion-rate-optimization.md new file mode 100644 index 0000000..6e0b4a9 --- /dev/null +++ b/blog-posts/82-conversion-rate-optimization.md @@ -0,0 +1,690 @@ +# Conversion Rate Optimization (CRO) + +**Meta-Description:** Conversion Rate Optimization für SaaS. Landing Pages, User Funnels, Micro-Conversions und datengetriebene Optimierung. + +**Keywords:** CRO, Conversion Rate, Landing Page, User Funnel, Micro-Conversions, UX Optimization, Growth Hacking + +--- + +## Einführung + +**Conversion Rate Optimization (CRO)** maximiert den Wert bestehenden Traffics. Statt mehr Besucher zu kaufen, werden bestehende Nutzer besser konvertiert. Dieser Guide zeigt systematische Ansätze für **Landing Pages**, **Funnels** und **Micro-Conversions**. + +--- + +## CRO Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CONVERSION OPTIMIZATION FUNNEL │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ TOFU (Top of Funnel): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Awareness → Interest │ │ +│ │ ├── Landing Page Views │ │ +│ │ ├── Blog Post Reads │ │ +│ │ ├── Social Media Clicks │ │ +│ │ └── Target: Reduce Bounce Rate │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ ↓ │ +│ MOFU (Middle of Funnel): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Consideration → Intent │ │ +│ │ ├── Email Signups │ │ +│ │ ├── Free Trial Starts │ │ +│ │ ├── Demo Requests │ │ +│ │ └── Target: Increase Lead Quality │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ ↓ │ +│ BOFU (Bottom of Funnel): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Purchase → Retention │ │ +│ │ ├── Trial to Paid Conversion │ │ +│ │ ├── Checkout Completion │ │ +│ │ ├── Upsells / Cross-sells │ │ +│ │ └── Target: Maximize Revenue │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Key Metrics: │ +│ ├── Conversion Rate = Conversions / Visitors × 100 │ +│ ├── Micro-Conversion Rate (Email, Trial, etc.) │ +│ ├── Revenue per Visitor (RPV) │ +│ └── Customer Acquisition Cost (CAC) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Landing Page Optimization + +```tsx +// components/landing/OptimizedHero.tsx +'use client'; + +import { useState } from 'react'; +import { track } from '@/lib/analytics'; + +interface HeroProps { + headline: string; + subheadline: string; + ctaText: string; + ctaUrl: string; + trustBadges: string[]; + videoUrl?: string; +} + +export function OptimizedHero({ + headline, + subheadline, + ctaText, + ctaUrl, + trustBadges, + videoUrl +}: HeroProps) { + const [videoPlaying, setVideoPlaying] = useState(false); + + const handleCTAClick = () => { + track('hero_cta_clicked', { + headline, + ctaText + }); + }; + + const handleVideoPlay = () => { + setVideoPlaying(true); + track('hero_video_played'); + }; + + return ( + <section className="relative py-20 lg:py-32"> + <div className="container mx-auto px-4"> + <div className="grid lg:grid-cols-2 gap-12 items-center"> + {/* Copy Side */} + <div className="space-y-6"> + {/* Social Proof Mini */} + <div className="flex items-center gap-2 text-sm text-gray-600"> + <span className="flex -space-x-2"> + {[1, 2, 3, 4, 5].map(i => ( + <img + key={i} + src={`/avatars/${i}.jpg`} + alt="" + className="w-8 h-8 rounded-full border-2 border-white" + /> + ))} + </span> + <span>Join 10,000+ happy customers</span> + </div> + + {/* Headline - Max 6 words */} + <h1 className="text-4xl lg:text-6xl font-bold leading-tight"> + {headline} + </h1> + + {/* Subheadline - Benefits focused */} + <p className="text-xl text-gray-600 max-w-lg"> + {subheadline} + </p> + + {/* CTA Group */} + <div className="flex flex-col sm:flex-row gap-4"> + <a + href={ctaUrl} + onClick={handleCTAClick} + className="btn-primary text-lg px-8 py-4 text-center" + > + {ctaText} + </a> + <span className="text-sm text-gray-500 self-center"> + No credit card required + </span> + </div> + + {/* Trust Badges */} + <div className="flex flex-wrap gap-4 pt-4"> + {trustBadges.map((badge, i) => ( + <div + key={i} + className="flex items-center gap-2 text-sm text-gray-600" + > + <CheckIcon className="w-5 h-5 text-green-500" /> + {badge} + </div> + ))} + </div> + </div> + + {/* Visual Side */} + <div className="relative"> + {videoUrl ? ( + <VideoPlayer + url={videoUrl} + onPlay={handleVideoPlay} + playing={videoPlaying} + /> + ) : ( + <img + src="/hero-image.webp" + alt="Product screenshot" + className="rounded-lg shadow-2xl" + /> + )} + </div> + </div> + </div> + </section> + ); +} +``` + +```tsx +// components/landing/SocialProof.tsx +interface Testimonial { + quote: string; + author: string; + role: string; + company: string; + avatar: string; + logo: string; + metric?: string; +} + +export function SocialProofSection({ + testimonials +}: { + testimonials: Testimonial[] +}) { + return ( + <section className="py-20 bg-gray-50"> + <div className="container mx-auto px-4"> + {/* Logos Bar */} + <div className="text-center mb-12"> + <p className="text-sm text-gray-500 mb-6"> + Trusted by leading companies + </p> + <div className="flex flex-wrap justify-center items-center gap-8 opacity-60"> + {testimonials.map((t, i) => ( + <img + key={i} + src={t.logo} + alt={t.company} + className="h-8 grayscale hover:grayscale-0 transition" + /> + ))} + </div> + </div> + + {/* Testimonials Grid */} + <div className="grid md:grid-cols-3 gap-8"> + {testimonials.map((testimonial, i) => ( + <TestimonialCard key={i} {...testimonial} /> + ))} + </div> + + {/* Aggregate Stats */} + <div className="grid grid-cols-2 md:grid-cols-4 gap-8 mt-16 text-center"> + <Stat value="10,000+" label="Active Users" /> + <Stat value="99.9%" label="Uptime" /> + <Stat value="4.9/5" label="Customer Rating" /> + <Stat value="24/7" label="Support" /> + </div> + </div> + </section> + ); +} + +function TestimonialCard({ + quote, + author, + role, + company, + avatar, + metric +}: Testimonial) { + return ( + <div className="bg-white p-6 rounded-xl shadow-sm"> + {metric && ( + <div className="text-3xl font-bold text-blue-600 mb-4"> + {metric} + </div> + )} + <blockquote className="text-gray-700 mb-4"> + "{quote}" + </blockquote> + <div className="flex items-center gap-3"> + <img + src={avatar} + alt={author} + className="w-12 h-12 rounded-full" + /> + <div> + <div className="font-semibold">{author}</div> + <div className="text-sm text-gray-500">{role}, {company}</div> + </div> + </div> + </div> + ); +} +``` + +--- + +## Funnel Tracking + +```typescript +// lib/cro/funnel.ts +export interface FunnelStep { + id: string; + name: string; + pagePattern: string | RegExp; + requiredEvents?: string[]; +} + +export interface FunnelConfig { + id: string; + name: string; + steps: FunnelStep[]; +} + +export const signupFunnel: FunnelConfig = { + id: 'signup', + name: 'Signup Funnel', + steps: [ + { id: 'landing', name: 'Landing Page', pagePattern: '/' }, + { id: 'pricing', name: 'Pricing Page', pagePattern: '/pricing' }, + { id: 'signup-start', name: 'Signup Started', pagePattern: '/signup', requiredEvents: ['signup_form_viewed'] }, + { id: 'signup-complete', name: 'Signup Complete', pagePattern: '/welcome', requiredEvents: ['signup_completed'] }, + { id: 'onboarding', name: 'Onboarding', pagePattern: '/onboarding' }, + { id: 'activated', name: 'Activated', pagePattern: '/dashboard', requiredEvents: ['first_action_completed'] } + ] +}; + +// Funnel Analytics +export async function getFunnelAnalytics( + funnelId: string, + startDate: Date, + endDate: Date +): Promise<FunnelAnalytics> { + const funnel = funnels.get(funnelId); + if (!funnel) throw new Error(`Funnel ${funnelId} not found`); + + const stepData = await Promise.all( + funnel.steps.map(async (step, index) => { + const count = await getStepCount(step, startDate, endDate); + const previousCount = index > 0 + ? await getStepCount(funnel.steps[index - 1], startDate, endDate) + : count; + + return { + step: step.name, + count, + conversionRate: previousCount > 0 ? (count / previousCount) * 100 : 100, + dropoffRate: previousCount > 0 ? ((previousCount - count) / previousCount) * 100 : 0 + }; + }) + ); + + const overallConversion = stepData[0].count > 0 + ? (stepData[stepData.length - 1].count / stepData[0].count) * 100 + : 0; + + return { + funnel: funnel.name, + period: { start: startDate, end: endDate }, + steps: stepData, + overallConversion + }; +} +``` + +```tsx +// components/analytics/FunnelVisualization.tsx +'use client'; + +interface FunnelStep { + step: string; + count: number; + conversionRate: number; + dropoffRate: number; +} + +export function FunnelVisualization({ steps }: { steps: FunnelStep[] }) { + const maxCount = Math.max(...steps.map(s => s.count)); + + return ( + <div className="space-y-4"> + {steps.map((step, index) => { + const width = (step.count / maxCount) * 100; + const isLastStep = index === steps.length - 1; + + return ( + <div key={step.step} className="relative"> + {/* Step Bar */} + <div className="flex items-center gap-4"> + <div className="w-32 text-sm font-medium">{step.step}</div> + <div className="flex-1 h-12 bg-gray-100 rounded-lg overflow-hidden"> + <div + className="h-full bg-blue-500 flex items-center justify-end px-4 transition-all" + style={{ width: `${width}%` }} + > + <span className="text-white font-semibold"> + {step.count.toLocaleString()} + </span> + </div> + </div> + <div className="w-20 text-right"> + <span className={`text-sm font-medium ${ + step.conversionRate >= 50 ? 'text-green-600' : 'text-red-600' + }`}> + {step.conversionRate.toFixed(1)}% + </span> + </div> + </div> + + {/* Dropoff Indicator */} + {!isLastStep && step.dropoffRate > 0 && ( + <div className="ml-32 pl-4 py-2 text-sm text-red-500"> + ↳ {step.dropoffRate.toFixed(1)}% dropoff ({Math.round(step.count * step.dropoffRate / 100).toLocaleString()} users) + </div> + )} + </div> + ); + })} + </div> + ); +} +``` + +--- + +## Micro-Conversions + +```typescript +// lib/cro/micro-conversions.ts +export const microConversions = { + engagement: [ + { id: 'scroll_depth_50', name: 'Scrolled 50%', weight: 1 }, + { id: 'scroll_depth_90', name: 'Scrolled 90%', weight: 2 }, + { id: 'time_on_page_60s', name: '60s on page', weight: 2 }, + { id: 'video_played', name: 'Video Played', weight: 3 }, + { id: 'video_completed', name: 'Video Completed', weight: 5 } + ], + intent: [ + { id: 'pricing_viewed', name: 'Viewed Pricing', weight: 5 }, + { id: 'feature_comparison', name: 'Compared Features', weight: 4 }, + { id: 'faq_expanded', name: 'Expanded FAQ', weight: 2 }, + { id: 'live_chat_started', name: 'Started Live Chat', weight: 8 } + ], + lead: [ + { id: 'email_captured', name: 'Email Signup', weight: 10 }, + { id: 'demo_requested', name: 'Demo Request', weight: 15 }, + { id: 'trial_started', name: 'Trial Started', weight: 20 } + ] +}; + +// Calculate Lead Score +export function calculateLeadScore(events: string[]): number { + let score = 0; + + Object.values(microConversions).flat().forEach(conversion => { + if (events.includes(conversion.id)) { + score += conversion.weight; + } + }); + + return score; +} + +// hooks/useMicroConversions.ts +'use client'; + +import { useEffect, useCallback } from 'react'; +import { track } from '@/lib/analytics'; + +export function useMicroConversions() { + // Scroll Depth Tracking + useEffect(() => { + let maxScroll = 0; + const milestones = [25, 50, 75, 90]; + const triggered = new Set<number>(); + + const handleScroll = () => { + const scrollHeight = document.documentElement.scrollHeight - window.innerHeight; + const scrollPercent = Math.round((window.scrollY / scrollHeight) * 100); + + if (scrollPercent > maxScroll) { + maxScroll = scrollPercent; + + milestones.forEach(milestone => { + if (scrollPercent >= milestone && !triggered.has(milestone)) { + triggered.add(milestone); + track(`scroll_depth_${milestone}`); + } + }); + } + }; + + window.addEventListener('scroll', handleScroll, { passive: true }); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + // Time on Page Tracking + useEffect(() => { + const milestones = [30, 60, 120, 300]; + let elapsed = 0; + + const interval = setInterval(() => { + elapsed += 1; + + if (milestones.includes(elapsed)) { + track(`time_on_page_${elapsed}s`); + } + }, 1000); + + return () => clearInterval(interval); + }, []); + + // Track specific actions + const trackMicro = useCallback((eventId: string, metadata?: Record<string, unknown>) => { + track(eventId, metadata); + }, []); + + return { trackMicro }; +} +``` + +--- + +## Form Optimization + +```tsx +// components/forms/OptimizedSignupForm.tsx +'use client'; + +import { useState, useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { track } from '@/lib/analytics'; + +const signupSchema = z.object({ + email: z.string().email('Please enter a valid email'), + password: z.string().min(8, 'Password must be at least 8 characters'), + name: z.string().min(2, 'Name is required') +}); + +type SignupData = z.infer<typeof signupSchema>; + +export function OptimizedSignupForm() { + const [step, setStep] = useState(1); + const [startTime] = useState(Date.now()); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + watch + } = useForm<SignupData>({ + resolver: zodResolver(signupSchema), + mode: 'onBlur' + }); + + // Track form interactions + useEffect(() => { + track('signup_form_viewed'); + }, []); + + const email = watch('email'); + + useEffect(() => { + if (email && email.includes('@')) { + track('signup_email_entered'); + } + }, [email]); + + const onSubmit = async (data: SignupData) => { + const timeToComplete = Date.now() - startTime; + + track('signup_form_submitted', { + timeToComplete, + step + }); + + try { + await createAccount(data); + track('signup_completed', { timeToComplete }); + } catch (error) { + track('signup_error', { error: (error as Error).message }); + } + }; + + // Field focus tracking + const trackFieldFocus = (field: string) => { + track('form_field_focused', { field }); + }; + + return ( + <form onSubmit={handleSubmit(onSubmit)} className="space-y-6"> + {/* Progress Indicator */} + <div className="flex gap-2 mb-6"> + {[1, 2, 3].map(s => ( + <div + key={s} + className={`h-1 flex-1 rounded ${ + s <= step ? 'bg-blue-600' : 'bg-gray-200' + }`} + /> + ))} + </div> + + {/* Step 1: Email */} + {step >= 1 && ( + <div> + <label className="block text-sm font-medium mb-2"> + Work Email + </label> + <input + type="email" + {...register('email')} + onFocus={() => trackFieldFocus('email')} + className="w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500" + placeholder="you@company.com" + autoComplete="email" + /> + {errors.email && ( + <p className="text-red-500 text-sm mt-1">{errors.email.message}</p> + )} + </div> + )} + + {/* Step 2: Password */} + {step >= 2 && ( + <div> + <label className="block text-sm font-medium mb-2"> + Create Password + </label> + <input + type="password" + {...register('password')} + onFocus={() => trackFieldFocus('password')} + className="w-full px-4 py-3 border rounded-lg" + placeholder="8+ characters" + /> + {errors.password && ( + <p className="text-red-500 text-sm mt-1">{errors.password.message}</p> + )} + {/* Password Strength */} + <PasswordStrengthMeter password={watch('password') || ''} /> + </div> + )} + + {/* CTA Button */} + <button + type={step < 2 ? 'button' : 'submit'} + onClick={() => step < 2 && setStep(step + 1)} + disabled={isSubmitting} + className="w-full py-4 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 disabled:opacity-50" + > + {isSubmitting ? 'Creating account...' : step < 2 ? 'Continue' : 'Create Free Account'} + </button> + + {/* Trust Signals */} + <div className="flex items-center justify-center gap-4 text-sm text-gray-500"> + <span className="flex items-center gap-1"> + <LockIcon className="w-4 h-4" /> + Secure + </span> + <span>No credit card required</span> + <span>Cancel anytime</span> + </div> + </form> + ); +} +``` + +--- + +## CRO Checklist + +| Element | Best Practice | +|---------|--------------| +| **Headline** | Clear value proposition, max 6 words | +| **CTA** | Action-oriented, contrasting color | +| **Social Proof** | Numbers, logos, testimonials | +| **Trust Signals** | Security badges, guarantees | +| **Forms** | Progressive, minimal fields | +| **Mobile** | Touch-friendly, fast loading | +| **Urgency** | Scarcity, limited time (honest!) | +| **Exit Intent** | Offer value, not desperation | + +--- + +## Fazit + +Conversion Rate Optimization erfordert: + +1. **Datenanalyse**: Funnels und Micro-Conversions tracken +2. **User Research**: Verstehen warum Nutzer konvertieren +3. **Testing**: Systematisches A/B Testing +4. **Iteration**: Kontinuierliche Verbesserung + +Kleine Verbesserungen summieren sich zu großen Ergebnissen. + +--- + +## Bildprompts + +1. "Conversion funnel visualization, colorful stages with dropoff points" +2. "Landing page wireframe with CRO annotations, best practices highlighted" +3. "A/B test results dashboard, winner variant celebration" + +--- + +## Quellen + +- [CXL Conversion Optimization Guide](https://cxl.com/blog/conversion-optimization-guide/) +- [Unbounce Landing Page Best Practices](https://unbounce.com/landing-page-articles/) +- [Hotjar CRO Resources](https://www.hotjar.com/conversion-rate-optimization/) +- [Neil Patel CRO Guide](https://neilpatel.com/blog/conversion-rate-optimization/) diff --git a/blog-posts/83-saas-pricing-strategies.md b/blog-posts/83-saas-pricing-strategies.md new file mode 100644 index 0000000..72aeed2 --- /dev/null +++ b/blog-posts/83-saas-pricing-strategies.md @@ -0,0 +1,611 @@ +# SaaS Pricing Strategies + +**Meta-Description:** SaaS Pricing Modelle implementieren. Freemium, Tiered Pricing, Usage-Based und Value-Based Pricing mit Stripe Integration. + +**Keywords:** SaaS Pricing, Freemium, Tiered Pricing, Usage-Based, Value Pricing, Stripe, Subscription, Revenue Model + +--- + +## Einführung + +**Pricing** ist einer der wichtigsten Hebel für SaaS-Wachstum. Das richtige **Pricing-Modell** kann Revenue verdoppeln ohne zusätzliche Kunden. Dieser Guide zeigt verschiedene Strategien und deren technische Implementation. + +--- + +## Pricing Models Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SAAS PRICING MODELS │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 1. FLAT-RATE PRICING: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ $49/month - All features included │ │ +│ │ ✓ Simple to understand │ │ +│ │ ✗ Limited upsell potential │ │ +│ │ Example: Basecamp │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 2. TIERED PRICING: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Starter Pro Enterprise │ │ +│ │ $19/mo $49/mo $199/mo │ │ +│ │ 5 users 25 users Unlimited │ │ +│ │ ✓ Clear upgrade path │ │ +│ │ ✓ Captures different segments │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 3. USAGE-BASED (Pay-as-you-go): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ $0.01 per API call / $0.10 per GB │ │ +│ │ ✓ Low barrier to entry │ │ +│ │ ✓ Revenue scales with customer success │ │ +│ │ ✗ Unpredictable revenue │ │ +│ │ Example: AWS, Twilio │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 4. FREEMIUM: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Free tier + Premium tiers │ │ +│ │ ✓ Viral growth potential │ │ +│ │ ✓ Low friction acquisition │ │ +│ │ ✗ High support costs │ │ +│ │ Example: Slack, Dropbox │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 5. PER-SEAT PRICING: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ $10/user/month │ │ +│ │ ✓ Predictable, easy to understand │ │ +│ │ ✓ Grows with customer's team │ │ +│ │ Example: Notion, Linear │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Pricing Configuration + +```typescript +// lib/pricing/config.ts +export interface PricingTier { + id: string; + name: string; + description: string; + monthlyPrice: number; + yearlyPrice: number; + features: Feature[]; + limits: TierLimits; + highlighted?: boolean; + ctaText: string; + stripeMonthlyPriceId: string; + stripeYearlyPriceId: string; +} + +export interface Feature { + name: string; + included: boolean; + limit?: string; + tooltip?: string; +} + +export interface TierLimits { + users: number; + projects: number; + storage: number; // in GB + apiCalls: number; + customDomains: number; + support: 'community' | 'email' | 'priority' | 'dedicated'; +} + +export const pricingTiers: PricingTier[] = [ + { + id: 'free', + name: 'Free', + description: 'For individuals getting started', + monthlyPrice: 0, + yearlyPrice: 0, + features: [ + { name: 'Up to 3 projects', included: true }, + { name: 'Basic analytics', included: true }, + { name: 'Community support', included: true }, + { name: 'API access', included: false }, + { name: 'Custom domains', included: false }, + { name: 'Team collaboration', included: false } + ], + limits: { + users: 1, + projects: 3, + storage: 1, + apiCalls: 1000, + customDomains: 0, + support: 'community' + }, + ctaText: 'Get Started Free', + stripeMonthlyPriceId: '', + stripeYearlyPriceId: '' + }, + { + id: 'pro', + name: 'Pro', + description: 'For professionals and small teams', + monthlyPrice: 29, + yearlyPrice: 290, // 2 months free + features: [ + { name: 'Unlimited projects', included: true }, + { name: 'Advanced analytics', included: true }, + { name: 'Email support', included: true }, + { name: 'API access', included: true, limit: '10k calls/mo' }, + { name: 'Custom domains', included: true, limit: '3 domains' }, + { name: 'Team collaboration', included: true, limit: '5 members' } + ], + limits: { + users: 5, + projects: -1, // unlimited + storage: 50, + apiCalls: 10000, + customDomains: 3, + support: 'email' + }, + highlighted: true, + ctaText: 'Start Free Trial', + stripeMonthlyPriceId: 'price_pro_monthly', + stripeYearlyPriceId: 'price_pro_yearly' + }, + { + id: 'enterprise', + name: 'Enterprise', + description: 'For large teams with advanced needs', + monthlyPrice: 99, + yearlyPrice: 990, + features: [ + { name: 'Unlimited everything', included: true }, + { name: 'Custom analytics', included: true }, + { name: 'Dedicated support', included: true }, + { name: 'API access', included: true, limit: 'Unlimited' }, + { name: 'Custom domains', included: true, limit: 'Unlimited' }, + { name: 'Team collaboration', included: true, limit: 'Unlimited' }, + { name: 'SSO/SAML', included: true }, + { name: 'SLA guarantee', included: true } + ], + limits: { + users: -1, + projects: -1, + storage: -1, + apiCalls: -1, + customDomains: -1, + support: 'dedicated' + }, + ctaText: 'Contact Sales', + stripeMonthlyPriceId: 'price_enterprise_monthly', + stripeYearlyPriceId: 'price_enterprise_yearly' + } +]; + +// Check if feature is available +export function hasFeature( + tierId: string, + featureName: string +): boolean { + const tier = pricingTiers.find(t => t.id === tierId); + if (!tier) return false; + + const feature = tier.features.find(f => f.name === featureName); + return feature?.included ?? false; +} + +// Check limits +export function checkLimit( + tierId: string, + limitKey: keyof TierLimits, + currentUsage: number +): { allowed: boolean; limit: number; usage: number } { + const tier = pricingTiers.find(t => t.id === tierId); + if (!tier) return { allowed: false, limit: 0, usage: currentUsage }; + + const limit = tier.limits[limitKey]; + + return { + allowed: limit === -1 || currentUsage < limit, + limit, + usage: currentUsage + }; +} +``` + +--- + +## Pricing Component + +```tsx +// components/pricing/PricingTable.tsx +'use client'; + +import { useState } from 'react'; +import { pricingTiers, PricingTier } from '@/lib/pricing/config'; +import { CheckIcon, XIcon } from 'lucide-react'; +import { track } from '@/lib/analytics'; + +export function PricingTable() { + const [billingPeriod, setBillingPeriod] = useState<'monthly' | 'yearly'>('monthly'); + + const handleToggle = () => { + const newPeriod = billingPeriod === 'monthly' ? 'yearly' : 'monthly'; + setBillingPeriod(newPeriod); + track('pricing_period_changed', { period: newPeriod }); + }; + + return ( + <section className="py-20"> + <div className="container mx-auto px-4"> + {/* Header */} + <div className="text-center mb-12"> + <h2 className="text-4xl font-bold mb-4"> + Simple, transparent pricing + </h2> + <p className="text-xl text-gray-600"> + No hidden fees. Cancel anytime. + </p> + + {/* Billing Toggle */} + <div className="flex items-center justify-center gap-4 mt-8"> + <span className={billingPeriod === 'monthly' ? 'font-semibold' : 'text-gray-500'}> + Monthly + </span> + <button + onClick={handleToggle} + className="relative w-14 h-7 bg-gray-200 rounded-full transition" + aria-label="Toggle billing period" + > + <span + className={`absolute w-5 h-5 bg-blue-600 rounded-full top-1 transition-all ${ + billingPeriod === 'yearly' ? 'left-8' : 'left-1' + }`} + /> + </button> + <span className={billingPeriod === 'yearly' ? 'font-semibold' : 'text-gray-500'}> + Yearly + <span className="ml-2 text-green-600 text-sm">Save 17%</span> + </span> + </div> + </div> + + {/* Pricing Cards */} + <div className="grid md:grid-cols-3 gap-8 max-w-5xl mx-auto"> + {pricingTiers.map((tier) => ( + <PricingCard + key={tier.id} + tier={tier} + billingPeriod={billingPeriod} + /> + ))} + </div> + + {/* FAQ Link */} + <div className="text-center mt-12"> + <p className="text-gray-600"> + Have questions?{' '} + <a href="/faq" className="text-blue-600 hover:underline"> + Check our FAQ + </a>{' '} + or{' '} + <a href="/contact" className="text-blue-600 hover:underline"> + contact us + </a> + </p> + </div> + </div> + </section> + ); +} + +function PricingCard({ + tier, + billingPeriod +}: { + tier: PricingTier; + billingPeriod: 'monthly' | 'yearly'; +}) { + const price = billingPeriod === 'monthly' ? tier.monthlyPrice : tier.yearlyPrice; + const pricePerMonth = billingPeriod === 'yearly' ? Math.round(price / 12) : price; + + const handleCTAClick = () => { + track('pricing_cta_clicked', { + tier: tier.id, + billingPeriod, + price + }); + }; + + return ( + <div + className={`relative rounded-2xl p-8 ${ + tier.highlighted + ? 'bg-blue-600 text-white ring-4 ring-blue-600 ring-offset-2' + : 'bg-white border border-gray-200' + }`} + > + {tier.highlighted && ( + <span className="absolute -top-4 left-1/2 -translate-x-1/2 bg-yellow-400 text-yellow-900 px-4 py-1 rounded-full text-sm font-semibold"> + Most Popular + </span> + )} + + <h3 className="text-2xl font-bold">{tier.name}</h3> + <p className={`mt-2 ${tier.highlighted ? 'text-blue-100' : 'text-gray-600'}`}> + {tier.description} + </p> + + {/* Price */} + <div className="mt-6"> + <span className="text-5xl font-bold"> + ${pricePerMonth} + </span> + <span className={tier.highlighted ? 'text-blue-100' : 'text-gray-500'}> + /month + </span> + {billingPeriod === 'yearly' && price > 0 && ( + <p className={`text-sm mt-1 ${tier.highlighted ? 'text-blue-100' : 'text-gray-500'}`}> + Billed ${price} annually + </p> + )} + </div> + + {/* CTA */} + <button + onClick={handleCTAClick} + className={`w-full mt-8 py-3 rounded-lg font-semibold transition ${ + tier.highlighted + ? 'bg-white text-blue-600 hover:bg-blue-50' + : 'bg-blue-600 text-white hover:bg-blue-700' + }`} + > + {tier.ctaText} + </button> + + {/* Features */} + <ul className="mt-8 space-y-4"> + {tier.features.map((feature, index) => ( + <li key={index} className="flex items-start gap-3"> + {feature.included ? ( + <CheckIcon className={`w-5 h-5 flex-shrink-0 ${ + tier.highlighted ? 'text-blue-200' : 'text-green-500' + }`} /> + ) : ( + <XIcon className="w-5 h-5 flex-shrink-0 text-gray-400" /> + )} + <span className={!feature.included ? 'text-gray-400' : ''}> + {feature.name} + {feature.limit && ( + <span className={`text-sm ${tier.highlighted ? 'text-blue-200' : 'text-gray-500'}`}> + {' '}({feature.limit}) + </span> + )} + </span> + </li> + ))} + </ul> + </div> + ); +} +``` + +--- + +## Usage-Based Pricing + +```typescript +// lib/pricing/usage.ts +export interface UsageMetric { + id: string; + name: string; + unit: string; + pricePerUnit: number; + freeAllowance: number; + tiers?: UsageTier[]; +} + +export interface UsageTier { + upTo: number; + pricePerUnit: number; +} + +export const usageMetrics: UsageMetric[] = [ + { + id: 'api_calls', + name: 'API Calls', + unit: 'request', + pricePerUnit: 0.0001, // $0.0001 per call + freeAllowance: 10000, + tiers: [ + { upTo: 100000, pricePerUnit: 0.0001 }, + { upTo: 1000000, pricePerUnit: 0.00008 }, + { upTo: Infinity, pricePerUnit: 0.00005 } + ] + }, + { + id: 'storage', + name: 'Storage', + unit: 'GB', + pricePerUnit: 0.10, + freeAllowance: 5, + tiers: [ + { upTo: 100, pricePerUnit: 0.10 }, + { upTo: 1000, pricePerUnit: 0.08 }, + { upTo: Infinity, pricePerUnit: 0.05 } + ] + }, + { + id: 'bandwidth', + name: 'Bandwidth', + unit: 'GB', + pricePerUnit: 0.05, + freeAllowance: 10 + } +]; + +export function calculateUsageCost( + metricId: string, + usage: number +): number { + const metric = usageMetrics.find(m => m.id === metricId); + if (!metric) return 0; + + // Subtract free allowance + const billableUsage = Math.max(0, usage - metric.freeAllowance); + if (billableUsage === 0) return 0; + + // Calculate with tiers if available + if (metric.tiers) { + let remaining = billableUsage; + let cost = 0; + let previousLimit = 0; + + for (const tier of metric.tiers) { + const tierUsage = Math.min(remaining, tier.upTo - previousLimit); + cost += tierUsage * tier.pricePerUnit; + remaining -= tierUsage; + previousLimit = tier.upTo; + + if (remaining <= 0) break; + } + + return cost; + } + + return billableUsage * metric.pricePerUnit; +} + +export function calculateTotalUsageCost( + usage: Record<string, number> +): { breakdown: Record<string, number>; total: number } { + const breakdown: Record<string, number> = {}; + let total = 0; + + for (const [metricId, amount] of Object.entries(usage)) { + const cost = calculateUsageCost(metricId, amount); + breakdown[metricId] = cost; + total += cost; + } + + return { breakdown, total }; +} +``` + +--- + +## Stripe Integration + +```typescript +// lib/stripe/checkout.ts +import Stripe from 'stripe'; + +const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { + apiVersion: '2024-06-20' +}); + +export async function createCheckoutSession( + customerId: string, + priceId: string, + options?: { + trialDays?: number; + successUrl?: string; + cancelUrl?: string; + couponId?: string; + } +): Promise<string> { + const session = await stripe.checkout.sessions.create({ + customer: customerId, + mode: 'subscription', + payment_method_types: ['card'], + line_items: [ + { + price: priceId, + quantity: 1 + } + ], + subscription_data: { + trial_period_days: options?.trialDays || 14, + ...(options?.couponId && { + coupon: options.couponId + }) + }, + success_url: options?.successUrl || `${process.env.NEXT_PUBLIC_APP_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`, + cancel_url: options?.cancelUrl || `${process.env.NEXT_PUBLIC_APP_URL}/pricing`, + allow_promotion_codes: true + }); + + return session.url!; +} + +export async function createBillingPortalSession( + customerId: string +): Promise<string> { + const session = await stripe.billingPortal.sessions.create({ + customer: customerId, + return_url: `${process.env.NEXT_PUBLIC_APP_URL}/settings/billing` + }); + + return session.url; +} + +// Usage-based billing +export async function reportUsage( + subscriptionItemId: string, + quantity: number, + timestamp?: number +): Promise<void> { + await stripe.subscriptionItems.createUsageRecord(subscriptionItemId, { + quantity, + timestamp: timestamp || Math.floor(Date.now() / 1000), + action: 'increment' + }); +} +``` + +--- + +## Pricing Psychology + +| Tactic | Implementation | +|--------|---------------| +| **Anchoring** | Show highest tier first or middle highlighted | +| **Decoy Effect** | Add a tier that makes others look better | +| **Charm Pricing** | $29 instead of $30 | +| **Annual Discount** | 2 months free for yearly | +| **Social Proof** | "Most Popular" badge | +| **Loss Aversion** | "Limited time offer" | +| **Value Framing** | Compare to alternatives | + +--- + +## Fazit + +SaaS Pricing Strategien erfordern: + +1. **Know Your Customers**: Segmente verstehen +2. **Value-Based**: Preis an Kundennutzen koppeln +3. **Test & Iterate**: A/B Testing für Pricing +4. **Clear Communication**: Transparente Preisgestaltung + +Pricing ist nie "fertig" – kontinuierlich optimieren. + +--- + +## Bildprompts + +1. "SaaS pricing page design, three-tier comparison with highlighted option" +2. "Usage-based pricing calculator interface, slider and cost preview" +3. "Pricing psychology infographic, anchoring and decoy effects" + +--- + +## Quellen + +- [Stripe Pricing Guide](https://stripe.com/guides/atlas/saas-pricing) +- [ProfitWell Pricing Strategy](https://www.profitwell.com/recur/all/pricing-strategy) +- [OpenView Pricing](https://openviewpartners.com/blog/saas-pricing-models/) +- [Price Intelligently](https://www.priceintelligently.com/) diff --git a/blog-posts/84-subscription-billing-stripe.md b/blog-posts/84-subscription-billing-stripe.md new file mode 100644 index 0000000..d485005 --- /dev/null +++ b/blog-posts/84-subscription-billing-stripe.md @@ -0,0 +1,730 @@ +# Subscription Billing mit Stripe + +**Meta-Description:** Stripe Subscription Billing implementieren. Checkout, Webhooks, Customer Portal und Metered Billing für SaaS. + +**Keywords:** Stripe, Subscription, Billing, Webhooks, Customer Portal, Metered Billing, SaaS, Payment + +--- + +## Einführung + +**Stripe** ist der De-facto-Standard für SaaS Billing. Von einfachen Subscriptions bis komplexem **Metered Billing** – Stripe bietet alle Tools. Dieser Guide zeigt die komplette Implementation für Next.js Anwendungen. + +--- + +## Stripe Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ STRIPE BILLING ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ User Flow: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 1. User clicks "Subscribe" │ │ +│ │ ↓ │ │ +│ │ 2. Create Stripe Customer (if new) │ │ +│ │ ↓ │ │ +│ │ 3. Create Checkout Session │ │ +│ │ ↓ │ │ +│ │ 4. Redirect to Stripe Checkout │ │ +│ │ ↓ │ │ +│ │ 5. User completes payment │ │ +│ │ ↓ │ │ +│ │ 6. Stripe sends webhook │ │ +│ │ ↓ │ │ +│ │ 7. Update database, activate subscription │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Key Objects: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Customer: │ │ +│ │ ├── email, name, metadata │ │ +│ │ └── payment methods, invoices │ │ +│ │ │ │ +│ │ Product: │ │ +│ │ ├── name, description │ │ +│ │ └── has many Prices │ │ +│ │ │ │ +│ │ Price: │ │ +│ │ ├── amount, currency, interval │ │ +│ │ └── recurring or one-time │ │ +│ │ │ │ +│ │ Subscription: │ │ +│ │ ├── customer, items (prices) │ │ +│ │ ├── status: active, past_due, canceled │ │ +│ │ └── billing cycle, trial │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Webhooks to Handle: │ +│ ├── checkout.session.completed │ +│ ├── customer.subscription.created │ +│ ├── customer.subscription.updated │ +│ ├── customer.subscription.deleted │ +│ ├── invoice.paid │ +│ ├── invoice.payment_failed │ +│ └── customer.updated │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Stripe Setup + +```typescript +// lib/stripe/client.ts +import Stripe from 'stripe'; + +if (!process.env.STRIPE_SECRET_KEY) { + throw new Error('STRIPE_SECRET_KEY is required'); +} + +export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { + apiVersion: '2024-06-20', + typescript: true +}); + +// lib/stripe/config.ts +export const STRIPE_CONFIG = { + prices: { + pro_monthly: process.env.STRIPE_PRICE_PRO_MONTHLY!, + pro_yearly: process.env.STRIPE_PRICE_PRO_YEARLY!, + enterprise_monthly: process.env.STRIPE_PRICE_ENTERPRISE_MONTHLY!, + enterprise_yearly: process.env.STRIPE_PRICE_ENTERPRISE_YEARLY! + }, + products: { + pro: process.env.STRIPE_PRODUCT_PRO!, + enterprise: process.env.STRIPE_PRODUCT_ENTERPRISE! + }, + webhookSecret: process.env.STRIPE_WEBHOOK_SECRET! +}; + +// Map price IDs to plan names +export const PRICE_TO_PLAN: Record<string, string> = { + [STRIPE_CONFIG.prices.pro_monthly]: 'pro', + [STRIPE_CONFIG.prices.pro_yearly]: 'pro', + [STRIPE_CONFIG.prices.enterprise_monthly]: 'enterprise', + [STRIPE_CONFIG.prices.enterprise_yearly]: 'enterprise' +}; +``` + +--- + +## Customer Management + +```typescript +// lib/stripe/customers.ts +import { stripe } from './client'; +import { db } from '@/lib/db'; + +export async function getOrCreateStripeCustomer( + userId: string +): Promise<string> { + // Check if user already has a Stripe customer ID + const user = await db.user.findUnique({ + where: { id: userId }, + select: { stripeCustomerId: true, email: true, name: true } + }); + + if (!user) { + throw new Error('User not found'); + } + + if (user.stripeCustomerId) { + return user.stripeCustomerId; + } + + // Create new Stripe customer + const customer = await stripe.customers.create({ + email: user.email!, + name: user.name || undefined, + metadata: { + userId + } + }); + + // Save customer ID to database + await db.user.update({ + where: { id: userId }, + data: { stripeCustomerId: customer.id } + }); + + return customer.id; +} + +export async function getCustomerSubscription( + customerId: string +): Promise<Stripe.Subscription | null> { + const subscriptions = await stripe.subscriptions.list({ + customer: customerId, + status: 'all', + limit: 1, + expand: ['data.default_payment_method'] + }); + + return subscriptions.data[0] || null; +} + +export async function updateCustomerEmail( + customerId: string, + email: string +): Promise<void> { + await stripe.customers.update(customerId, { email }); +} +``` + +--- + +## Checkout Session + +```typescript +// lib/stripe/checkout.ts +import { stripe } from './client'; +import { STRIPE_CONFIG } from './config'; +import { getOrCreateStripeCustomer } from './customers'; + +interface CreateCheckoutOptions { + userId: string; + priceId: string; + quantity?: number; + trialDays?: number; + couponId?: string; + metadata?: Record<string, string>; +} + +export async function createCheckoutSession( + options: CreateCheckoutOptions +): Promise<string> { + const customerId = await getOrCreateStripeCustomer(options.userId); + + const sessionConfig: Stripe.Checkout.SessionCreateParams = { + customer: customerId, + mode: 'subscription', + payment_method_types: ['card'], + line_items: [ + { + price: options.priceId, + quantity: options.quantity || 1 + } + ], + subscription_data: { + trial_period_days: options.trialDays, + metadata: { + userId: options.userId, + ...options.metadata + } + }, + success_url: `${process.env.NEXT_PUBLIC_APP_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`, + cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`, + allow_promotion_codes: true, + billing_address_collection: 'auto', + customer_update: { + address: 'auto', + name: 'auto' + } + }; + + // Add coupon if provided + if (options.couponId) { + sessionConfig.discounts = [{ coupon: options.couponId }]; + } + + const session = await stripe.checkout.sessions.create(sessionConfig); + + return session.url!; +} + +// API Route +// app/api/checkout/route.ts +import { NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { createCheckoutSession } from '@/lib/stripe/checkout'; + +export async function POST(request: Request) { + try { + const session = await getServerSession(); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const { priceId, quantity } = await request.json(); + + const checkoutUrl = await createCheckoutSession({ + userId: session.user.id, + priceId, + quantity, + trialDays: 14 + }); + + return NextResponse.json({ url: checkoutUrl }); + } catch (error) { + console.error('Checkout error:', error); + return NextResponse.json( + { error: 'Failed to create checkout session' }, + { status: 500 } + ); + } +} +``` + +--- + +## Webhook Handler + +```typescript +// app/api/webhooks/stripe/route.ts +import { NextResponse } from 'next/server'; +import { headers } from 'next/headers'; +import { stripe } from '@/lib/stripe/client'; +import { STRIPE_CONFIG, PRICE_TO_PLAN } from '@/lib/stripe/config'; +import { db } from '@/lib/db'; +import Stripe from 'stripe'; + +export async function POST(request: Request) { + const body = await request.text(); + const headersList = await headers(); + const signature = headersList.get('stripe-signature')!; + + let event: Stripe.Event; + + try { + event = stripe.webhooks.constructEvent( + body, + signature, + STRIPE_CONFIG.webhookSecret + ); + } catch (error) { + console.error('Webhook signature verification failed:', error); + return NextResponse.json({ error: 'Invalid signature' }, { status: 400 }); + } + + try { + switch (event.type) { + case 'checkout.session.completed': + await handleCheckoutCompleted(event.data.object as Stripe.Checkout.Session); + break; + + case 'customer.subscription.created': + case 'customer.subscription.updated': + await handleSubscriptionChange(event.data.object as Stripe.Subscription); + break; + + case 'customer.subscription.deleted': + await handleSubscriptionDeleted(event.data.object as Stripe.Subscription); + break; + + case 'invoice.paid': + await handleInvoicePaid(event.data.object as Stripe.Invoice); + break; + + case 'invoice.payment_failed': + await handlePaymentFailed(event.data.object as Stripe.Invoice); + break; + + default: + console.log(`Unhandled event type: ${event.type}`); + } + + return NextResponse.json({ received: true }); + } catch (error) { + console.error('Webhook handler error:', error); + return NextResponse.json( + { error: 'Webhook handler failed' }, + { status: 500 } + ); + } +} + +async function handleCheckoutCompleted(session: Stripe.Checkout.Session) { + const customerId = session.customer as string; + const subscriptionId = session.subscription as string; + + // Get user by Stripe customer ID + const user = await db.user.findFirst({ + where: { stripeCustomerId: customerId } + }); + + if (!user) { + throw new Error(`User not found for customer: ${customerId}`); + } + + // Get subscription details + const subscription = await stripe.subscriptions.retrieve(subscriptionId); + const priceId = subscription.items.data[0].price.id; + const plan = PRICE_TO_PLAN[priceId] || 'free'; + + // Update user subscription + await db.user.update({ + where: { id: user.id }, + data: { + stripeSubscriptionId: subscriptionId, + plan, + subscriptionStatus: subscription.status, + currentPeriodEnd: new Date(subscription.current_period_end * 1000) + } + }); + + // Send welcome email + await sendWelcomeEmail(user.email!, plan); +} + +async function handleSubscriptionChange(subscription: Stripe.Subscription) { + const customerId = subscription.customer as string; + + const user = await db.user.findFirst({ + where: { stripeCustomerId: customerId } + }); + + if (!user) return; + + const priceId = subscription.items.data[0].price.id; + const plan = PRICE_TO_PLAN[priceId] || 'free'; + + await db.user.update({ + where: { id: user.id }, + data: { + plan, + subscriptionStatus: subscription.status, + currentPeriodEnd: new Date(subscription.current_period_end * 1000), + cancelAtPeriodEnd: subscription.cancel_at_period_end + } + }); +} + +async function handleSubscriptionDeleted(subscription: Stripe.Subscription) { + const customerId = subscription.customer as string; + + const user = await db.user.findFirst({ + where: { stripeCustomerId: customerId } + }); + + if (!user) return; + + await db.user.update({ + where: { id: user.id }, + data: { + plan: 'free', + subscriptionStatus: 'canceled', + stripeSubscriptionId: null + } + }); +} + +async function handleInvoicePaid(invoice: Stripe.Invoice) { + // Record payment in database + if (invoice.subscription) { + await db.payment.create({ + data: { + stripeInvoiceId: invoice.id, + stripeCustomerId: invoice.customer as string, + amount: invoice.amount_paid, + currency: invoice.currency, + status: 'paid', + paidAt: new Date() + } + }); + } +} + +async function handlePaymentFailed(invoice: Stripe.Invoice) { + const customerId = invoice.customer as string; + + const user = await db.user.findFirst({ + where: { stripeCustomerId: customerId } + }); + + if (!user) return; + + // Update subscription status + await db.user.update({ + where: { id: user.id }, + data: { subscriptionStatus: 'past_due' } + }); + + // Send payment failed email + await sendPaymentFailedEmail(user.email!); +} +``` + +--- + +## Customer Portal + +```typescript +// lib/stripe/portal.ts +import { stripe } from './client'; + +export async function createBillingPortalSession( + customerId: string, + returnUrl?: string +): Promise<string> { + const session = await stripe.billingPortal.sessions.create({ + customer: customerId, + return_url: returnUrl || `${process.env.NEXT_PUBLIC_APP_URL}/settings/billing` + }); + + return session.url; +} + +// API Route +// app/api/billing/portal/route.ts +import { NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { createBillingPortalSession } from '@/lib/stripe/portal'; +import { db } from '@/lib/db'; + +export async function POST(request: Request) { + try { + const session = await getServerSession(); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const user = await db.user.findUnique({ + where: { id: session.user.id }, + select: { stripeCustomerId: true } + }); + + if (!user?.stripeCustomerId) { + return NextResponse.json( + { error: 'No billing account found' }, + { status: 400 } + ); + } + + const portalUrl = await createBillingPortalSession(user.stripeCustomerId); + + return NextResponse.json({ url: portalUrl }); + } catch (error) { + console.error('Portal error:', error); + return NextResponse.json( + { error: 'Failed to create portal session' }, + { status: 500 } + ); + } +} +``` + +--- + +## Metered Billing + +```typescript +// lib/stripe/metered.ts +import { stripe } from './client'; +import { db } from '@/lib/db'; + +export async function reportUsage( + userId: string, + quantity: number, + idempotencyKey?: string +): Promise<void> { + const user = await db.user.findUnique({ + where: { id: userId }, + select: { stripeSubscriptionId: true } + }); + + if (!user?.stripeSubscriptionId) { + throw new Error('User has no active subscription'); + } + + // Get the subscription item for metered billing + const subscription = await stripe.subscriptions.retrieve( + user.stripeSubscriptionId, + { expand: ['items.data.price'] } + ); + + const meteredItem = subscription.items.data.find( + item => (item.price as Stripe.Price).recurring?.usage_type === 'metered' + ); + + if (!meteredItem) { + throw new Error('No metered billing item found'); + } + + // Report usage + await stripe.subscriptionItems.createUsageRecord( + meteredItem.id, + { + quantity, + timestamp: Math.floor(Date.now() / 1000), + action: 'increment' + }, + { + idempotencyKey + } + ); +} + +// Get usage for current period +export async function getCurrentUsage(userId: string): Promise<{ + used: number; + limit: number | null; +}> { + const user = await db.user.findUnique({ + where: { id: userId }, + select: { stripeSubscriptionId: true } + }); + + if (!user?.stripeSubscriptionId) { + return { used: 0, limit: null }; + } + + const subscription = await stripe.subscriptions.retrieve( + user.stripeSubscriptionId + ); + + const meteredItem = subscription.items.data.find( + item => item.price.recurring?.usage_type === 'metered' + ); + + if (!meteredItem) { + return { used: 0, limit: null }; + } + + // Get usage records + const usageRecords = await stripe.subscriptionItems.listUsageRecordSummaries( + meteredItem.id, + { limit: 1 } + ); + + const currentUsage = usageRecords.data[0]?.total_usage || 0; + + return { + used: currentUsage, + limit: null // or get from subscription metadata + }; +} +``` + +--- + +## Subscription Management UI + +```tsx +// components/billing/SubscriptionStatus.tsx +'use client'; + +import { useState } from 'react'; +import { useSubscription } from '@/hooks/useSubscription'; + +export function SubscriptionStatus() { + const { subscription, isLoading } = useSubscription(); + const [isPortalLoading, setIsPortalLoading] = useState(false); + + const openBillingPortal = async () => { + setIsPortalLoading(true); + try { + const res = await fetch('/api/billing/portal', { method: 'POST' }); + const { url } = await res.json(); + window.location.href = url; + } catch (error) { + console.error('Portal error:', error); + } + setIsPortalLoading(false); + }; + + if (isLoading) return <div>Loading...</div>; + + return ( + <div className="bg-white rounded-lg shadow p-6"> + <h2 className="text-lg font-semibold mb-4">Subscription</h2> + + <div className="space-y-4"> + <div className="flex justify-between"> + <span className="text-gray-600">Current Plan</span> + <span className="font-semibold capitalize"> + {subscription?.plan || 'Free'} + </span> + </div> + + <div className="flex justify-between"> + <span className="text-gray-600">Status</span> + <StatusBadge status={subscription?.status || 'inactive'} /> + </div> + + {subscription?.currentPeriodEnd && ( + <div className="flex justify-between"> + <span className="text-gray-600"> + {subscription.cancelAtPeriodEnd ? 'Cancels on' : 'Renews on'} + </span> + <span> + {new Date(subscription.currentPeriodEnd).toLocaleDateString()} + </span> + </div> + )} + + <hr /> + + <button + onClick={openBillingPortal} + disabled={isPortalLoading} + className="w-full py-2 bg-gray-100 hover:bg-gray-200 rounded-lg" + > + {isPortalLoading ? 'Loading...' : 'Manage Subscription'} + </button> + </div> + </div> + ); +} + +function StatusBadge({ status }: { status: string }) { + const colors: Record<string, string> = { + active: 'bg-green-100 text-green-800', + trialing: 'bg-blue-100 text-blue-800', + past_due: 'bg-yellow-100 text-yellow-800', + canceled: 'bg-red-100 text-red-800', + inactive: 'bg-gray-100 text-gray-800' + }; + + return ( + <span className={`px-2 py-1 rounded-full text-sm ${colors[status] || colors.inactive}`}> + {status.replace('_', ' ')} + </span> + ); +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **Webhooks** | Always verify signatures | +| **Idempotency** | Use idempotency keys | +| **Retries** | Handle webhook retries | +| **Testing** | Use Stripe CLI for local testing | +| **Errors** | Log all Stripe errors | +| **Security** | Never expose secret keys | + +--- + +## Fazit + +Stripe Subscription Billing erfordert: + +1. **Customer Management**: Stripe Customer ID syncen +2. **Webhooks**: Alle relevanten Events handeln +3. **Portal**: Kunden Self-Service ermöglichen +4. **Metered**: Usage-Based Billing für Flexibilität + +Robustes Billing ist Grundlage für SaaS-Erfolg. + +--- + +## Bildprompts + +1. "Stripe checkout page, credit card form with security badges" +2. "Subscription management dashboard, plan upgrade flow" +3. "Webhook event flow diagram, Stripe to application" + +--- + +## Quellen + +- [Stripe Billing Documentation](https://stripe.com/docs/billing) +- [Stripe Webhooks](https://stripe.com/docs/webhooks) +- [Stripe Customer Portal](https://stripe.com/docs/billing/subscriptions/customer-portal) +- [Stripe Testing](https://stripe.com/docs/testing) diff --git a/blog-posts/85-multi-tenancy-architecture.md b/blog-posts/85-multi-tenancy-architecture.md new file mode 100644 index 0000000..a1adf1d --- /dev/null +++ b/blog-posts/85-multi-tenancy-architecture.md @@ -0,0 +1,619 @@ +# Multi-Tenancy Architecture + +**Meta-Description:** Multi-Tenant SaaS Architektur. Database Isolation, Tenant Context, Row-Level Security und Scaling Strategien. + +**Keywords:** Multi-Tenancy, SaaS Architecture, Database Isolation, Row-Level Security, Tenant, B2B, Scaling + +--- + +## Einführung + +**Multi-Tenancy** ermöglicht es, eine Anwendung für viele Kunden (Tenants) zu betreiben. Von **Database per Tenant** bis **Row-Level Security** – die richtige Strategie hängt von Sicherheit, Kosten und Skalierung ab. + +--- + +## Multi-Tenancy Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ MULTI-TENANCY STRATEGIES │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 1. DATABASE PER TENANT: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ [App] → [Tenant A DB] [Tenant B DB] [Tenant C DB] │ │ +│ │ │ │ +│ │ ✓ Maximum isolation │ │ +│ │ ✓ Easy data migration │ │ +│ │ ✗ Higher infrastructure cost │ │ +│ │ ✗ Complex connection management │ │ +│ │ Use case: Enterprise, regulated industries │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 2. SCHEMA PER TENANT: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ [Database] │ │ +│ │ ├── schema_tenant_a │ │ +│ │ ├── schema_tenant_b │ │ +│ │ └── schema_tenant_c │ │ +│ │ │ │ +│ │ ✓ Good isolation │ │ +│ │ ✓ Easier backup per tenant │ │ +│ │ ✗ Schema migrations complexity │ │ +│ │ Use case: Mid-size SaaS │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 3. SHARED DATABASE (Row-Level): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ [Database] │ │ +│ │ └── [Tables with tenant_id column] │ │ +│ │ ├── users (tenant_id, ...) │ │ +│ │ ├── projects (tenant_id, ...) │ │ +│ │ └── documents (tenant_id, ...) │ │ +│ │ │ │ +│ │ ✓ Cost efficient │ │ +│ │ ✓ Simple deployment │ │ +│ │ ✗ Requires careful query design │ │ +│ │ Use case: SMB SaaS, high tenant count │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 4. HYBRID APPROACH: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ - Shared DB for small tenants │ │ +│ │ - Dedicated DB for enterprise tenants │ │ +│ │ ✓ Flexible pricing tiers │ │ +│ │ ✓ Meet enterprise requirements │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Tenant Context + +```typescript +// lib/tenant/context.ts +import { AsyncLocalStorage } from 'async_hooks'; + +export interface TenantContext { + tenantId: string; + tenantSlug: string; + plan: string; + features: string[]; + limits: Record<string, number>; +} + +const tenantStorage = new AsyncLocalStorage<TenantContext>(); + +export function getTenantContext(): TenantContext { + const context = tenantStorage.getStore(); + if (!context) { + throw new Error('Tenant context not initialized'); + } + return context; +} + +export function runWithTenant<T>( + tenant: TenantContext, + fn: () => T +): T { + return tenantStorage.run(tenant, fn); +} + +// Optional: Get tenant without throwing +export function getTenantContextSafe(): TenantContext | null { + return tenantStorage.getStore() || null; +} +``` + +```typescript +// middleware.ts - Tenant Resolution +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export async function middleware(request: NextRequest) { + const response = NextResponse.next(); + + // Strategy 1: Subdomain-based + const hostname = request.headers.get('host') || ''; + const subdomain = hostname.split('.')[0]; + + if (subdomain && subdomain !== 'www' && subdomain !== 'app') { + response.headers.set('x-tenant-slug', subdomain); + } + + // Strategy 2: Path-based (/org/tenant-slug/...) + const pathname = request.nextUrl.pathname; + const pathMatch = pathname.match(/^\/org\/([^\/]+)/); + + if (pathMatch) { + response.headers.set('x-tenant-slug', pathMatch[1]); + } + + // Strategy 3: Header-based (for API) + const headerTenant = request.headers.get('x-tenant-id'); + if (headerTenant) { + response.headers.set('x-tenant-id', headerTenant); + } + + return response; +} + +// lib/tenant/resolver.ts +import { db } from '@/lib/db'; +import { TenantContext } from './context'; + +export async function resolveTenant( + identifier: string, + type: 'slug' | 'id' = 'slug' +): Promise<TenantContext | null> { + const tenant = await db.tenant.findUnique({ + where: type === 'slug' ? { slug: identifier } : { id: identifier }, + include: { + subscription: true, + features: true + } + }); + + if (!tenant) return null; + + return { + tenantId: tenant.id, + tenantSlug: tenant.slug, + plan: tenant.subscription?.plan || 'free', + features: tenant.features.map(f => f.name), + limits: { + users: tenant.subscription?.userLimit || 5, + storage: tenant.subscription?.storageLimit || 1024, + projects: tenant.subscription?.projectLimit || 10 + } + }; +} +``` + +--- + +## Database with Row-Level Security + +```typescript +// prisma/schema.prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} + +model Tenant { + id String @id @default(cuid()) + slug String @unique + name String + createdAt DateTime @default(now()) + + // Relations + users User[] + projects Project[] + subscription Subscription? +} + +model User { + id String @id @default(cuid()) + email String + name String? + + // Tenant relation + tenantId String + tenant Tenant @relation(fields: [tenantId], references: [id]) + + @@unique([tenantId, email]) + @@index([tenantId]) +} + +model Project { + id String @id @default(cuid()) + name String + description String? + + // Tenant relation + tenantId String + tenant Tenant @relation(fields: [tenantId], references: [id]) + + // Other relations + documents Document[] + + @@index([tenantId]) +} + +model Document { + id String @id @default(cuid()) + title String + content String? + + // Both tenant and project for redundant filtering + tenantId String + projectId String + project Project @relation(fields: [projectId], references: [id]) + + @@index([tenantId]) + @@index([projectId]) +} +``` + +```typescript +// lib/db/tenant-client.ts +import { PrismaClient } from '@prisma/client'; +import { getTenantContext } from '@/lib/tenant/context'; + +// Extend Prisma with tenant filtering +function createTenantPrismaClient() { + const prisma = new PrismaClient(); + + return prisma.$extends({ + query: { + $allModels: { + async findMany({ model, operation, args, query }) { + const context = getTenantContext(); + + // Models that need tenant filtering + const tenantModels = ['User', 'Project', 'Document', 'Team']; + + if (tenantModels.includes(model)) { + args.where = { + ...args.where, + tenantId: context.tenantId + }; + } + + return query(args); + }, + + async findFirst({ model, operation, args, query }) { + const context = getTenantContext(); + const tenantModels = ['User', 'Project', 'Document', 'Team']; + + if (tenantModels.includes(model)) { + args.where = { + ...args.where, + tenantId: context.tenantId + }; + } + + return query(args); + }, + + async create({ model, operation, args, query }) { + const context = getTenantContext(); + const tenantModels = ['User', 'Project', 'Document', 'Team']; + + if (tenantModels.includes(model)) { + args.data = { + ...args.data, + tenantId: context.tenantId + }; + } + + return query(args); + }, + + async update({ model, operation, args, query }) { + const context = getTenantContext(); + const tenantModels = ['User', 'Project', 'Document', 'Team']; + + if (tenantModels.includes(model)) { + args.where = { + ...args.where, + tenantId: context.tenantId + }; + } + + return query(args); + }, + + async delete({ model, operation, args, query }) { + const context = getTenantContext(); + const tenantModels = ['User', 'Project', 'Document', 'Team']; + + if (tenantModels.includes(model)) { + args.where = { + ...args.where, + tenantId: context.tenantId + }; + } + + return query(args); + } + } + } + }); +} + +export const tenantDb = createTenantPrismaClient(); +``` + +--- + +## PostgreSQL Row-Level Security + +```sql +-- Enable RLS on tables +ALTER TABLE users ENABLE ROW LEVEL SECURITY; +ALTER TABLE projects ENABLE ROW LEVEL SECURITY; +ALTER TABLE documents ENABLE ROW LEVEL SECURITY; + +-- Create policies +CREATE POLICY tenant_isolation_users ON users + USING (tenant_id = current_setting('app.current_tenant_id')::uuid); + +CREATE POLICY tenant_isolation_projects ON projects + USING (tenant_id = current_setting('app.current_tenant_id')::uuid); + +CREATE POLICY tenant_isolation_documents ON documents + USING (tenant_id = current_setting('app.current_tenant_id')::uuid); + +-- Function to set tenant context +CREATE OR REPLACE FUNCTION set_tenant_context(tenant_id uuid) +RETURNS void AS $$ +BEGIN + PERFORM set_config('app.current_tenant_id', tenant_id::text, false); +END; +$$ LANGUAGE plpgsql; +``` + +```typescript +// lib/db/rls-client.ts +import { Pool } from 'pg'; +import { getTenantContext } from '@/lib/tenant/context'; + +const pool = new Pool({ + connectionString: process.env.DATABASE_URL +}); + +export async function query<T>( + sql: string, + params?: unknown[] +): Promise<T[]> { + const client = await pool.connect(); + const context = getTenantContext(); + + try { + // Set tenant context for RLS + await client.query( + "SELECT set_config('app.current_tenant_id', $1, true)", + [context.tenantId] + ); + + const result = await client.query(sql, params); + return result.rows as T[]; + } finally { + client.release(); + } +} + +// Usage +const projects = await query<Project>( + 'SELECT * FROM projects WHERE status = $1', + ['active'] +); +// RLS automatically filters by tenant! +``` + +--- + +## API Routes with Tenant + +```typescript +// app/api/projects/route.ts +import { NextResponse } from 'next/server'; +import { headers } from 'next/headers'; +import { resolveTenant, runWithTenant } from '@/lib/tenant'; +import { tenantDb } from '@/lib/db/tenant-client'; + +export async function GET(request: Request) { + const headersList = await headers(); + const tenantSlug = headersList.get('x-tenant-slug'); + + if (!tenantSlug) { + return NextResponse.json({ error: 'Tenant not found' }, { status: 400 }); + } + + const tenant = await resolveTenant(tenantSlug); + if (!tenant) { + return NextResponse.json({ error: 'Invalid tenant' }, { status: 404 }); + } + + // Run with tenant context + const projects = await runWithTenant(tenant, async () => { + return tenantDb.project.findMany({ + orderBy: { createdAt: 'desc' } + }); + }); + + return NextResponse.json(projects); +} + +export async function POST(request: Request) { + const headersList = await headers(); + const tenantSlug = headersList.get('x-tenant-slug'); + + const tenant = await resolveTenant(tenantSlug!); + if (!tenant) { + return NextResponse.json({ error: 'Invalid tenant' }, { status: 404 }); + } + + const body = await request.json(); + + // Check limits + const projectCount = await runWithTenant(tenant, async () => { + return tenantDb.project.count(); + }); + + if (tenant.limits.projects !== -1 && projectCount >= tenant.limits.projects) { + return NextResponse.json( + { error: 'Project limit reached. Please upgrade your plan.' }, + { status: 403 } + ); + } + + const project = await runWithTenant(tenant, async () => { + return tenantDb.project.create({ + data: { + name: body.name, + description: body.description + } + }); + }); + + return NextResponse.json(project, { status: 201 }); +} +``` + +--- + +## Tenant Provisioning + +```typescript +// lib/tenant/provisioning.ts +import { db } from '@/lib/db'; +import { stripe } from '@/lib/stripe'; + +interface CreateTenantInput { + name: string; + slug: string; + ownerEmail: string; + ownerName: string; + plan?: string; +} + +export async function provisionTenant(input: CreateTenantInput) { + // Validate slug + const existingTenant = await db.tenant.findUnique({ + where: { slug: input.slug } + }); + + if (existingTenant) { + throw new Error('Tenant slug already exists'); + } + + // Create Stripe customer + const stripeCustomer = await stripe.customers.create({ + email: input.ownerEmail, + name: input.name, + metadata: { + tenantSlug: input.slug + } + }); + + // Create tenant with owner in transaction + const tenant = await db.$transaction(async (tx) => { + // Create tenant + const newTenant = await tx.tenant.create({ + data: { + name: input.name, + slug: input.slug, + stripeCustomerId: stripeCustomer.id, + settings: { + create: { + theme: 'light', + timezone: 'Europe/Berlin' + } + } + } + }); + + // Create owner user + await tx.user.create({ + data: { + email: input.ownerEmail, + name: input.ownerName, + tenantId: newTenant.id, + role: 'owner' + } + }); + + // Create default resources + await tx.project.create({ + data: { + name: 'My First Project', + tenantId: newTenant.id + } + }); + + return newTenant; + }); + + // Send welcome email + await sendWelcomeEmail(input.ownerEmail, tenant.slug); + + return tenant; +} + +// Tenant deletion (with data cleanup) +export async function deprovisionTenant(tenantId: string) { + const tenant = await db.tenant.findUnique({ + where: { id: tenantId }, + include: { subscription: true } + }); + + if (!tenant) { + throw new Error('Tenant not found'); + } + + // Cancel Stripe subscription + if (tenant.subscription?.stripeSubscriptionId) { + await stripe.subscriptions.cancel(tenant.subscription.stripeSubscriptionId); + } + + // Delete all tenant data in transaction + await db.$transaction([ + db.document.deleteMany({ where: { tenantId } }), + db.project.deleteMany({ where: { tenantId } }), + db.user.deleteMany({ where: { tenantId } }), + db.subscription.deleteMany({ where: { tenantId } }), + db.tenantSettings.deleteMany({ where: { tenantId } }), + db.tenant.delete({ where: { id: tenantId } }) + ]); + + // Archive data (optional) + await archiveTenantData(tenantId); +} +``` + +--- + +## Strategy Comparison + +| Strategy | Isolation | Cost | Complexity | Best For | +|----------|-----------|------|------------|----------| +| **DB per Tenant** | Highest | High | High | Enterprise | +| **Schema per Tenant** | High | Medium | Medium | Mid-market | +| **Shared + RLS** | Medium | Low | Medium | SMB/Startup | +| **Hybrid** | Flexible | Variable | High | Mixed segments | + +--- + +## Fazit + +Multi-Tenancy Architektur erfordert: + +1. **Isolation Strategy**: Passend zum Use Case wählen +2. **Tenant Context**: Durchgängig in der Anwendung +3. **Security**: RLS oder Application-Level Filtering +4. **Provisioning**: Automatisierte Tenant-Erstellung + +Die richtige Strategie hängt von Kunden und Skalierung ab. + +--- + +## Bildprompts + +1. "Multi-tenant architecture diagram, database isolation strategies" +2. "Tenant provisioning flow, signup to activation" +3. "Row-level security visualization, data filtering per tenant" + +--- + +## Quellen + +- [Microsoft Multi-Tenancy Patterns](https://docs.microsoft.com/en-us/azure/architecture/guide/multitenant/overview) +- [PostgreSQL RLS Documentation](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) +- [Prisma Multi-Tenancy](https://www.prisma.io/docs/guides/other/multi-tenancy) +- [AWS Multi-Tenant SaaS](https://aws.amazon.com/blogs/apn/building-a-multi-tenant-saas-solution-using-aws-serverless-services/) diff --git a/blog-posts/86-feature-flags-progressive-rollout.md b/blog-posts/86-feature-flags-progressive-rollout.md new file mode 100644 index 0000000..2b25a5a --- /dev/null +++ b/blog-posts/86-feature-flags-progressive-rollout.md @@ -0,0 +1,752 @@ +# Feature Flags & Progressive Rollout + +**Meta-Description:** Feature Flags implementieren mit Next.js. Progressive Rollouts, Canary Releases und A/B Testing mit Feature Management. + +**Keywords:** Feature Flags, Feature Toggle, Progressive Rollout, Canary Release, LaunchDarkly, Unleash, Feature Management + +--- + +## Einführung + +**Feature Flags** ermöglichen kontinuierliche Deployment ohne Risiko. Features können für bestimmte Nutzer aktiviert, schrittweise ausgerollt oder bei Problemen sofort deaktiviert werden. Dieser Guide zeigt Implementation und Best Practices. + +--- + +## Feature Flags Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ FEATURE FLAGS ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Flag Types: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 1. Release Flags: │ │ +│ │ - Hide unfinished features │ │ +│ │ - Temporary, remove after launch │ │ +│ │ │ │ +│ │ 2. Experiment Flags: │ │ +│ │ - A/B testing │ │ +│ │ - Feature comparison │ │ +│ │ │ │ +│ │ 3. Ops Flags: │ │ +│ │ - Kill switches │ │ +│ │ - Performance toggles │ │ +│ │ │ │ +│ │ 4. Permission Flags: │ │ +│ │ - Plan-based features │ │ +│ │ - User-specific access │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Rollout Strategies: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Percentage Rollout: │ │ +│ │ ├── 5% → 25% → 50% → 100% │ │ +│ │ └── Gradual exposure to reduce risk │ │ +│ │ │ │ +│ │ User Targeting: │ │ +│ │ ├── Specific user IDs │ │ +│ │ ├── User attributes (plan, role, country) │ │ +│ │ └── Cohort-based │ │ +│ │ │ │ +│ │ Environment-based: │ │ +│ │ ├── Development: all flags on │ │ +│ │ ├── Staging: beta flags on │ │ +│ │ └── Production: controlled rollout │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Decision Flow: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Request → Get User Context → Evaluate Rules │ │ +│ │ → Check Percentage → Return Flag Value │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Custom Feature Flag System + +```typescript +// lib/features/types.ts +export interface FeatureFlag { + key: string; + name: string; + description: string; + type: 'boolean' | 'string' | 'number' | 'json'; + defaultValue: unknown; + enabled: boolean; + rules: TargetingRule[]; + rolloutPercentage: number; + createdAt: Date; + updatedAt: Date; +} + +export interface TargetingRule { + id: string; + attribute: string; + operator: 'equals' | 'contains' | 'greaterThan' | 'lessThan' | 'in' | 'notIn'; + value: unknown; + enabled: boolean; +} + +export interface EvaluationContext { + userId?: string; + email?: string; + plan?: string; + role?: string; + country?: string; + tenantId?: string; + attributes?: Record<string, unknown>; +} + +export interface FlagEvaluation { + value: unknown; + reason: 'default' | 'rule' | 'percentage' | 'override' | 'disabled'; + ruleId?: string; +} +``` + +```typescript +// lib/features/evaluator.ts +import { createHash } from 'crypto'; +import { FeatureFlag, EvaluationContext, FlagEvaluation, TargetingRule } from './types'; + +export class FlagEvaluator { + evaluate( + flag: FeatureFlag, + context: EvaluationContext + ): FlagEvaluation { + // Check if flag is globally disabled + if (!flag.enabled) { + return { + value: flag.defaultValue, + reason: 'disabled' + }; + } + + // Check targeting rules + for (const rule of flag.rules) { + if (!rule.enabled) continue; + + if (this.matchesRule(rule, context)) { + return { + value: true, + reason: 'rule', + ruleId: rule.id + }; + } + } + + // Check percentage rollout + if (flag.rolloutPercentage > 0 && flag.rolloutPercentage < 100) { + const inRollout = this.isInRolloutPercentage( + flag.key, + context.userId || '', + flag.rolloutPercentage + ); + + if (inRollout) { + return { + value: true, + reason: 'percentage' + }; + } + } + + // 100% rollout + if (flag.rolloutPercentage === 100) { + return { + value: true, + reason: 'percentage' + }; + } + + // Default value + return { + value: flag.defaultValue, + reason: 'default' + }; + } + + private matchesRule(rule: TargetingRule, context: EvaluationContext): boolean { + const contextValue = this.getContextValue(rule.attribute, context); + + switch (rule.operator) { + case 'equals': + return contextValue === rule.value; + + case 'contains': + return String(contextValue).includes(String(rule.value)); + + case 'greaterThan': + return Number(contextValue) > Number(rule.value); + + case 'lessThan': + return Number(contextValue) < Number(rule.value); + + case 'in': + return Array.isArray(rule.value) && rule.value.includes(contextValue); + + case 'notIn': + return Array.isArray(rule.value) && !rule.value.includes(contextValue); + + default: + return false; + } + } + + private getContextValue(attribute: string, context: EvaluationContext): unknown { + if (attribute in context) { + return context[attribute as keyof EvaluationContext]; + } + return context.attributes?.[attribute]; + } + + private isInRolloutPercentage( + flagKey: string, + userId: string, + percentage: number + ): boolean { + // Deterministic hash for consistent assignment + const hash = createHash('md5') + .update(`${flagKey}:${userId}`) + .digest('hex'); + + const hashValue = parseInt(hash.substring(0, 8), 16) % 100; + return hashValue < percentage; + } +} +``` + +```typescript +// lib/features/client.ts +import { FeatureFlag, EvaluationContext, FlagEvaluation } from './types'; +import { FlagEvaluator } from './evaluator'; + +class FeatureFlagClient { + private flags: Map<string, FeatureFlag> = new Map(); + private evaluator = new FlagEvaluator(); + private context: EvaluationContext = {}; + private overrides: Map<string, unknown> = new Map(); + + async initialize(): Promise<void> { + await this.fetchFlags(); + + // Poll for updates + setInterval(() => this.fetchFlags(), 60000); + } + + private async fetchFlags(): Promise<void> { + try { + const response = await fetch('/api/features'); + const flags: FeatureFlag[] = await response.json(); + + this.flags.clear(); + flags.forEach(flag => this.flags.set(flag.key, flag)); + } catch (error) { + console.error('Failed to fetch feature flags:', error); + } + } + + setContext(context: EvaluationContext): void { + this.context = context; + } + + // Override for testing/debugging + setOverride(key: string, value: unknown): void { + this.overrides.set(key, value); + } + + clearOverrides(): void { + this.overrides.clear(); + } + + isEnabled(key: string, defaultValue: boolean = false): boolean { + // Check overrides first + if (this.overrides.has(key)) { + return Boolean(this.overrides.get(key)); + } + + const flag = this.flags.get(key); + if (!flag) { + return defaultValue; + } + + const evaluation = this.evaluator.evaluate(flag, this.context); + return Boolean(evaluation.value); + } + + getValue<T>(key: string, defaultValue: T): T { + if (this.overrides.has(key)) { + return this.overrides.get(key) as T; + } + + const flag = this.flags.get(key); + if (!flag) { + return defaultValue; + } + + const evaluation = this.evaluator.evaluate(flag, this.context); + return (evaluation.value as T) ?? defaultValue; + } + + getEvaluation(key: string): FlagEvaluation | null { + const flag = this.flags.get(key); + if (!flag) return null; + + return this.evaluator.evaluate(flag, this.context); + } + + getAllFlags(): Record<string, unknown> { + const result: Record<string, unknown> = {}; + + this.flags.forEach((flag, key) => { + const evaluation = this.evaluator.evaluate(flag, this.context); + result[key] = evaluation.value; + }); + + return result; + } +} + +// Singleton +export const featureFlags = new FeatureFlagClient(); +``` + +--- + +## React Integration + +```tsx +// contexts/FeatureFlagContext.tsx +'use client'; + +import { createContext, useContext, useEffect, useState, ReactNode } from 'react'; +import { featureFlags } from '@/lib/features/client'; +import { EvaluationContext } from '@/lib/features/types'; + +interface FeatureFlagContextValue { + isEnabled: (key: string, defaultValue?: boolean) => boolean; + getValue: <T>(key: string, defaultValue: T) => T; + isReady: boolean; +} + +const FeatureFlagContext = createContext<FeatureFlagContextValue | null>(null); + +export function FeatureFlagProvider({ + children, + context +}: { + children: ReactNode; + context?: EvaluationContext; +}) { + const [isReady, setIsReady] = useState(false); + + useEffect(() => { + if (context) { + featureFlags.setContext(context); + } + + featureFlags.initialize().then(() => setIsReady(true)); + }, [context]); + + const value: FeatureFlagContextValue = { + isEnabled: (key, defaultValue = false) => + featureFlags.isEnabled(key, defaultValue), + getValue: (key, defaultValue) => + featureFlags.getValue(key, defaultValue), + isReady + }; + + return ( + <FeatureFlagContext.Provider value={value}> + {children} + </FeatureFlagContext.Provider> + ); +} + +export function useFeatureFlags() { + const context = useContext(FeatureFlagContext); + if (!context) { + throw new Error('useFeatureFlags must be used within FeatureFlagProvider'); + } + return context; +} + +// hooks/useFeature.ts +export function useFeature(key: string, defaultValue: boolean = false): boolean { + const { isEnabled, isReady } = useFeatureFlags(); + + if (!isReady) return defaultValue; + return isEnabled(key, defaultValue); +} +``` + +```tsx +// components/Feature.tsx +'use client'; + +import { ReactNode } from 'react'; +import { useFeature } from '@/hooks/useFeature'; + +interface FeatureProps { + flag: string; + children: ReactNode; + fallback?: ReactNode; +} + +export function Feature({ flag, children, fallback = null }: FeatureProps) { + const isEnabled = useFeature(flag); + + if (!isEnabled) { + return <>{fallback}</>; + } + + return <>{children}</>; +} + +// Usage +export function Dashboard() { + return ( + <div> + <h1>Dashboard</h1> + + <Feature flag="new-analytics-dashboard"> + <NewAnalyticsDashboard /> + </Feature> + + <Feature + flag="ai-insights" + fallback={<p>AI Insights coming soon!</p>} + > + <AIInsightsPanel /> + </Feature> + </div> + ); +} +``` + +--- + +## Server-Side Evaluation + +```typescript +// lib/features/server.ts +import { cookies, headers } from 'next/headers'; +import { db } from '@/lib/db'; +import { FlagEvaluator } from './evaluator'; +import { FeatureFlag, EvaluationContext } from './types'; + +const evaluator = new FlagEvaluator(); + +// Cache flags in memory +let flagsCache: Map<string, FeatureFlag> = new Map(); +let lastFetch = 0; +const CACHE_TTL = 60000; // 1 minute + +async function getFlags(): Promise<Map<string, FeatureFlag>> { + if (Date.now() - lastFetch < CACHE_TTL && flagsCache.size > 0) { + return flagsCache; + } + + const flags = await db.featureFlag.findMany({ + include: { rules: true } + }); + + flagsCache = new Map(flags.map(f => [f.key, f])); + lastFetch = Date.now(); + + return flagsCache; +} + +export async function isFeatureEnabled( + key: string, + context?: EvaluationContext +): Promise<boolean> { + const flags = await getFlags(); + const flag = flags.get(key); + + if (!flag) return false; + + // Build context from request if not provided + const evalContext = context || await buildContextFromRequest(); + + const evaluation = evaluator.evaluate(flag, evalContext); + return Boolean(evaluation.value); +} + +async function buildContextFromRequest(): Promise<EvaluationContext> { + const headersList = await headers(); + const cookieStore = await cookies(); + + return { + userId: cookieStore.get('userId')?.value, + tenantId: headersList.get('x-tenant-id') || undefined, + country: headersList.get('cf-ipcountry') || undefined + }; +} + +// Usage in Server Components +export default async function SettingsPage() { + const showBetaFeatures = await isFeatureEnabled('beta-features'); + + return ( + <div> + <h1>Settings</h1> + {showBetaFeatures && <BetaSettings />} + </div> + ); +} +``` + +--- + +## Progressive Rollout + +```typescript +// lib/features/rollout.ts +import { db } from '@/lib/db'; + +interface RolloutPlan { + stages: RolloutStage[]; + currentStage: number; + startedAt: Date; +} + +interface RolloutStage { + percentage: number; + duration: number; // hours + metrics: { + errorRateThreshold: number; + latencyThreshold: number; + }; +} + +export async function progressRollout(flagKey: string): Promise<void> { + const flag = await db.featureFlag.findUnique({ + where: { key: flagKey }, + include: { rolloutPlan: true } + }); + + if (!flag?.rolloutPlan) { + throw new Error('No rollout plan found'); + } + + const plan = flag.rolloutPlan as RolloutPlan; + const currentStage = plan.stages[plan.currentStage]; + const nextStage = plan.stages[plan.currentStage + 1]; + + if (!nextStage) { + console.log(`Rollout complete for ${flagKey}`); + return; + } + + // Check metrics before progressing + const metricsOk = await checkRolloutMetrics(flagKey, currentStage.metrics); + + if (!metricsOk) { + // Automatic rollback + await rollbackFeature(flagKey); + throw new Error('Metrics threshold exceeded, rolling back'); + } + + // Progress to next stage + await db.featureFlag.update({ + where: { key: flagKey }, + data: { + rolloutPercentage: nextStage.percentage, + rolloutPlan: { + ...plan, + currentStage: plan.currentStage + 1 + } + } + }); + + console.log(`Rolled out ${flagKey} to ${nextStage.percentage}%`); +} + +async function checkRolloutMetrics( + flagKey: string, + thresholds: { errorRateThreshold: number; latencyThreshold: number } +): Promise<boolean> { + // Query your metrics system + const metrics = await getFeatureMetrics(flagKey); + + return ( + metrics.errorRate < thresholds.errorRateThreshold && + metrics.p95Latency < thresholds.latencyThreshold + ); +} + +export async function rollbackFeature(flagKey: string): Promise<void> { + await db.featureFlag.update({ + where: { key: flagKey }, + data: { + rolloutPercentage: 0, + enabled: false + } + }); + + // Alert team + await sendSlackAlert(`Feature ${flagKey} automatically rolled back due to metrics`); +} + +// Scheduled job for automatic progression +export async function processRollouts(): Promise<void> { + const activeRollouts = await db.featureFlag.findMany({ + where: { + rolloutPercentage: { gt: 0, lt: 100 }, + enabled: true + } + }); + + for (const flag of activeRollouts) { + try { + await progressRollout(flag.key); + } catch (error) { + console.error(`Rollout failed for ${flag.key}:`, error); + } + } +} +``` + +--- + +## Admin Dashboard + +```tsx +// app/admin/features/page.tsx +'use client'; + +import { useState, useEffect } from 'react'; +import { FeatureFlag } from '@/lib/features/types'; + +export default function FeatureFlagsAdmin() { + const [flags, setFlags] = useState<FeatureFlag[]>([]); + const [selectedFlag, setSelectedFlag] = useState<FeatureFlag | null>(null); + + useEffect(() => { + fetch('/api/admin/features') + .then(res => res.json()) + .then(setFlags); + }, []); + + const updateFlag = async (key: string, updates: Partial<FeatureFlag>) => { + await fetch(`/api/admin/features/${key}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(updates) + }); + + setFlags(flags.map(f => + f.key === key ? { ...f, ...updates } : f + )); + }; + + return ( + <div className="p-6"> + <h1 className="text-2xl font-bold mb-6">Feature Flags</h1> + + <div className="bg-white rounded-lg shadow"> + <table className="w-full"> + <thead className="bg-gray-50"> + <tr> + <th className="px-4 py-3 text-left">Flag</th> + <th className="px-4 py-3 text-left">Status</th> + <th className="px-4 py-3 text-left">Rollout</th> + <th className="px-4 py-3 text-left">Actions</th> + </tr> + </thead> + <tbody> + {flags.map(flag => ( + <tr key={flag.key} className="border-t"> + <td className="px-4 py-3"> + <div> + <div className="font-medium">{flag.name}</div> + <div className="text-sm text-gray-500">{flag.key}</div> + </div> + </td> + <td className="px-4 py-3"> + <ToggleSwitch + enabled={flag.enabled} + onChange={(enabled) => updateFlag(flag.key, { enabled })} + /> + </td> + <td className="px-4 py-3"> + <RolloutSlider + value={flag.rolloutPercentage} + onChange={(percentage) => + updateFlag(flag.key, { rolloutPercentage: percentage }) + } + /> + </td> + <td className="px-4 py-3"> + <button + onClick={() => setSelectedFlag(flag)} + className="text-blue-600 hover:underline" + > + Edit Rules + </button> + </td> + </tr> + ))} + </tbody> + </table> + </div> + + {selectedFlag && ( + <FlagRulesModal + flag={selectedFlag} + onClose={() => setSelectedFlag(null)} + onSave={(rules) => updateFlag(selectedFlag.key, { rules })} + /> + )} + </div> + ); +} +``` + +--- + +## Best Practices + +| Practice | Description | +|----------|-------------| +| **Naming** | Use clear, descriptive flag names | +| **Lifecycle** | Remove flags after full rollout | +| **Testing** | Test both flag states | +| **Documentation** | Document flag purpose and owner | +| **Monitoring** | Track flag evaluations | +| **Defaults** | Safe defaults (usually off) | + +--- + +## Fazit + +Feature Flags ermöglichen: + +1. **Safe Deployments**: Features schrittweise ausrollen +2. **Quick Rollbacks**: Probleme sofort beheben +3. **Experimentation**: A/B Tests und Experimente +4. **Customization**: Feature-based Pricing + +Feature Flags sind essentiell für moderne Software-Entwicklung. + +--- + +## Bildprompts + +1. "Feature flag dashboard, toggles and rollout percentages" +2. "Progressive rollout diagram, stages from 1% to 100%" +3. "Feature targeting rules interface, user segmentation" + +--- + +## Quellen + +- [Martin Fowler Feature Toggles](https://martinfowler.com/articles/feature-toggles.html) +- [LaunchDarkly Documentation](https://docs.launchdarkly.com/) +- [Unleash Feature Management](https://www.getunleash.io/) +- [Feature Flags Best Practices](https://featureflags.io/) diff --git a/blog-posts/87-user-onboarding-flows.md b/blog-posts/87-user-onboarding-flows.md new file mode 100644 index 0000000..d063333 --- /dev/null +++ b/blog-posts/87-user-onboarding-flows.md @@ -0,0 +1,788 @@ +# User Onboarding Flows + +**Meta-Description:** User Onboarding für SaaS optimieren. Progressive Onboarding, Checklists, Product Tours und Activation Metrics. + +**Keywords:** User Onboarding, Activation, Product Tour, Checklist, Time-to-Value, Retention, SaaS Onboarding + +--- + +## Einführung + +**User Onboarding** entscheidet über Trial-to-Paid Conversion und Retention. Der erste Eindruck zählt – **40-60% der Free Trial User** kehren nach dem ersten Tag nie zurück. Dieser Guide zeigt effektive Onboarding-Strategien. + +--- + +## Onboarding Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ USER ONBOARDING JOURNEY │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Onboarding Phases: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 1. SIGNUP FLOW: │ │ +│ │ ├── Minimal friction │ │ +│ │ ├── Social login options │ │ +│ │ └── Welcome message │ │ +│ │ ↓ │ │ +│ │ 2. INITIAL SETUP: │ │ +│ │ ├── Profile completion │ │ +│ │ ├── Preferences selection │ │ +│ │ └── Team invitation (if B2B) │ │ +│ │ ↓ │ │ +│ │ 3. FIRST VALUE: │ │ +│ │ ├── Guided first action │ │ +│ │ ├── Quick win celebration │ │ +│ │ └── "Aha moment" trigger │ │ +│ │ ↓ │ │ +│ │ 4. ACTIVATION: │ │ +│ │ ├── Core features used │ │ +│ │ ├── Habit formation │ │ +│ │ └── Ready for conversion │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Key Metrics: │ +│ ├── Time to First Value (TTFV) │ +│ ├── Activation Rate │ +│ ├── Onboarding Completion Rate │ +│ ├── Day 1/7/30 Retention │ +│ └── Trial-to-Paid Conversion │ +│ │ +│ Activation Events (Example): │ +│ ├── Created first project │ +│ ├── Invited team member │ +│ ├── Connected integration │ +│ └── Completed first workflow │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Onboarding Checklist + +```typescript +// lib/onboarding/types.ts +export interface OnboardingStep { + id: string; + title: string; + description: string; + action: { + type: 'link' | 'modal' | 'trigger'; + target: string; + }; + completionEvent: string; + points?: number; + required?: boolean; + order: number; +} + +export interface UserOnboarding { + userId: string; + completedSteps: string[]; + currentStep: string | null; + startedAt: Date; + completedAt: Date | null; + skippedAt: Date | null; +} + +// lib/onboarding/config.ts +export const onboardingSteps: OnboardingStep[] = [ + { + id: 'complete-profile', + title: 'Complete your profile', + description: 'Add your name and profile picture', + action: { type: 'link', target: '/settings/profile' }, + completionEvent: 'profile_completed', + points: 10, + required: true, + order: 1 + }, + { + id: 'create-project', + title: 'Create your first project', + description: 'Set up a project to organize your work', + action: { type: 'modal', target: 'create-project-modal' }, + completionEvent: 'project_created', + points: 20, + required: true, + order: 2 + }, + { + id: 'invite-team', + title: 'Invite your team', + description: 'Collaborate with your colleagues', + action: { type: 'link', target: '/settings/team' }, + completionEvent: 'team_member_invited', + points: 15, + required: false, + order: 3 + }, + { + id: 'connect-integration', + title: 'Connect an integration', + description: 'Sync with your favorite tools', + action: { type: 'link', target: '/settings/integrations' }, + completionEvent: 'integration_connected', + points: 20, + required: false, + order: 4 + }, + { + id: 'complete-workflow', + title: 'Complete your first workflow', + description: 'Experience the full power of our platform', + action: { type: 'trigger', target: 'start-workflow-tour' }, + completionEvent: 'workflow_completed', + points: 35, + required: true, + order: 5 + } +]; + +export const ACTIVATION_THRESHOLD = 3; // Steps to be "activated" +``` + +```typescript +// lib/onboarding/service.ts +import { db } from '@/lib/db'; +import { onboardingSteps, ACTIVATION_THRESHOLD } from './config'; +import { track } from '@/lib/analytics'; + +export async function getOnboardingProgress(userId: string) { + const onboarding = await db.userOnboarding.findUnique({ + where: { userId } + }); + + if (!onboarding) { + // Initialize onboarding + return db.userOnboarding.create({ + data: { + userId, + completedSteps: [], + currentStep: onboardingSteps[0].id, + startedAt: new Date() + } + }); + } + + return onboarding; +} + +export async function completeOnboardingStep( + userId: string, + stepId: string +): Promise<{ completed: boolean; isActivated: boolean; nextStep: string | null }> { + const onboarding = await getOnboardingProgress(userId); + + if (onboarding.completedSteps.includes(stepId)) { + return { + completed: false, + isActivated: isUserActivated(onboarding.completedSteps), + nextStep: onboarding.currentStep + }; + } + + const step = onboardingSteps.find(s => s.id === stepId); + if (!step) { + throw new Error(`Unknown step: ${stepId}`); + } + + const completedSteps = [...onboarding.completedSteps, stepId]; + const nextStep = getNextStep(completedSteps); + const isCompleted = nextStep === null; + + await db.userOnboarding.update({ + where: { userId }, + data: { + completedSteps, + currentStep: nextStep, + ...(isCompleted && { completedAt: new Date() }) + } + }); + + // Track analytics + track('onboarding_step_completed', { + userId, + stepId, + stepTitle: step.title, + points: step.points, + totalCompleted: completedSteps.length, + isActivated: isUserActivated(completedSteps) + }); + + // Award points (gamification) + if (step.points) { + await awardPoints(userId, step.points, `Completed: ${step.title}`); + } + + // Check activation + const activated = isUserActivated(completedSteps); + if (activated && !isUserActivated(onboarding.completedSteps)) { + await handleUserActivation(userId); + } + + return { + completed: true, + isActivated: activated, + nextStep + }; +} + +function getNextStep(completedSteps: string[]): string | null { + const remaining = onboardingSteps + .filter(s => !completedSteps.includes(s.id)) + .sort((a, b) => a.order - b.order); + + return remaining[0]?.id || null; +} + +function isUserActivated(completedSteps: string[]): boolean { + const requiredSteps = onboardingSteps.filter(s => s.required); + const completedRequired = requiredSteps.filter(s => + completedSteps.includes(s.id) + ); + + return completedRequired.length >= ACTIVATION_THRESHOLD; +} + +async function handleUserActivation(userId: string) { + // Update user status + await db.user.update({ + where: { id: userId }, + data: { isActivated: true, activatedAt: new Date() } + }); + + // Track activation + track('user_activated', { userId }); + + // Send activation email + await sendActivationEmail(userId); + + // Notify sales (for high-value leads) + await notifySalesIfQualified(userId); +} +``` + +--- + +## Onboarding UI Components + +```tsx +// components/onboarding/OnboardingChecklist.tsx +'use client'; + +import { useState, useEffect } from 'react'; +import { CheckCircle, Circle, ChevronRight, X } from 'lucide-react'; +import { onboardingSteps, OnboardingStep } from '@/lib/onboarding/config'; + +interface OnboardingProgress { + completedSteps: string[]; + currentStep: string | null; +} + +export function OnboardingChecklist() { + const [progress, setProgress] = useState<OnboardingProgress | null>(null); + const [isExpanded, setIsExpanded] = useState(true); + const [isDismissed, setIsDismissed] = useState(false); + + useEffect(() => { + fetch('/api/onboarding/progress') + .then(res => res.json()) + .then(setProgress); + }, []); + + if (!progress || isDismissed) return null; + + const completedCount = progress.completedSteps.length; + const totalCount = onboardingSteps.length; + const progressPercent = (completedCount / totalCount) * 100; + + // Hide if completed + if (completedCount === totalCount) return null; + + return ( + <div className="fixed bottom-4 right-4 w-80 bg-white rounded-lg shadow-lg border"> + {/* Header */} + <div + className="flex items-center justify-between p-4 cursor-pointer" + onClick={() => setIsExpanded(!isExpanded)} + > + <div className="flex items-center gap-3"> + <div className="relative w-10 h-10"> + <svg className="w-10 h-10 -rotate-90"> + <circle + cx="20" cy="20" r="16" + fill="none" + stroke="#e5e7eb" + strokeWidth="4" + /> + <circle + cx="20" cy="20" r="16" + fill="none" + stroke="#3b82f6" + strokeWidth="4" + strokeDasharray={`${progressPercent} 100`} + strokeLinecap="round" + /> + </svg> + <span className="absolute inset-0 flex items-center justify-center text-sm font-semibold"> + {completedCount}/{totalCount} + </span> + </div> + <div> + <h3 className="font-semibold">Getting Started</h3> + <p className="text-sm text-gray-500"> + {completedCount === 0 ? "Let's set you up!" : `${totalCount - completedCount} steps left`} + </p> + </div> + </div> + <button + onClick={(e) => { e.stopPropagation(); setIsDismissed(true); }} + className="text-gray-400 hover:text-gray-600" + > + <X className="w-5 h-5" /> + </button> + </div> + + {/* Steps */} + {isExpanded && ( + <div className="border-t max-h-80 overflow-y-auto"> + {onboardingSteps.map((step) => ( + <OnboardingStepItem + key={step.id} + step={step} + isCompleted={progress.completedSteps.includes(step.id)} + isCurrent={progress.currentStep === step.id} + /> + ))} + </div> + )} + </div> + ); +} + +function OnboardingStepItem({ + step, + isCompleted, + isCurrent +}: { + step: OnboardingStep; + isCompleted: boolean; + isCurrent: boolean; +}) { + const handleClick = () => { + if (isCompleted) return; + + if (step.action.type === 'link') { + window.location.href = step.action.target; + } else if (step.action.type === 'modal') { + // Dispatch event to open modal + window.dispatchEvent(new CustomEvent('open-modal', { + detail: { id: step.action.target } + })); + } else if (step.action.type === 'trigger') { + window.dispatchEvent(new CustomEvent(step.action.target)); + } + }; + + return ( + <button + onClick={handleClick} + disabled={isCompleted} + className={`w-full flex items-center gap-3 p-4 text-left hover:bg-gray-50 transition ${ + isCompleted ? 'opacity-60' : '' + } ${isCurrent ? 'bg-blue-50' : ''}`} + > + {isCompleted ? ( + <CheckCircle className="w-5 h-5 text-green-500 flex-shrink-0" /> + ) : ( + <Circle className={`w-5 h-5 flex-shrink-0 ${isCurrent ? 'text-blue-500' : 'text-gray-300'}`} /> + )} + <div className="flex-1 min-w-0"> + <p className={`font-medium ${isCompleted ? 'line-through' : ''}`}> + {step.title} + </p> + <p className="text-sm text-gray-500 truncate"> + {step.description} + </p> + </div> + {!isCompleted && ( + <ChevronRight className="w-5 h-5 text-gray-400 flex-shrink-0" /> + )} + </button> + ); +} +``` + +--- + +## Product Tour + +```tsx +// components/onboarding/ProductTour.tsx +'use client'; + +import { useState, useEffect, useCallback } from 'react'; +import { createPortal } from 'react-dom'; + +interface TourStep { + target: string; // CSS selector + title: string; + content: string; + placement: 'top' | 'bottom' | 'left' | 'right'; + action?: { + label: string; + onClick: () => void; + }; +} + +interface ProductTourProps { + tourId: string; + steps: TourStep[]; + onComplete: () => void; + onSkip?: () => void; +} + +export function ProductTour({ + tourId, + steps, + onComplete, + onSkip +}: ProductTourProps) { + const [currentStep, setCurrentStep] = useState(0); + const [targetRect, setTargetRect] = useState<DOMRect | null>(null); + + const step = steps[currentStep]; + + // Find and highlight target element + useEffect(() => { + const target = document.querySelector(step.target); + if (target) { + setTargetRect(target.getBoundingClientRect()); + + // Scroll into view + target.scrollIntoView({ behavior: 'smooth', block: 'center' }); + + // Add highlight class + target.classList.add('tour-highlight'); + + return () => { + target.classList.remove('tour-highlight'); + }; + } + }, [step.target]); + + const handleNext = useCallback(() => { + if (currentStep < steps.length - 1) { + setCurrentStep(currentStep + 1); + } else { + onComplete(); + } + }, [currentStep, steps.length, onComplete]); + + const handlePrev = () => { + if (currentStep > 0) { + setCurrentStep(currentStep - 1); + } + }; + + const handleSkip = () => { + onSkip?.(); + }; + + if (!targetRect) return null; + + // Calculate tooltip position + const tooltipStyle = calculateTooltipPosition(targetRect, step.placement); + + return createPortal( + <> + {/* Overlay with spotlight */} + <div className="fixed inset-0 z-50"> + <svg className="w-full h-full"> + <defs> + <mask id="spotlight-mask"> + <rect width="100%" height="100%" fill="white" /> + <rect + x={targetRect.x - 8} + y={targetRect.y - 8} + width={targetRect.width + 16} + height={targetRect.height + 16} + rx="8" + fill="black" + /> + </mask> + </defs> + <rect + width="100%" + height="100%" + fill="rgba(0,0,0,0.5)" + mask="url(#spotlight-mask)" + /> + </svg> + </div> + + {/* Tooltip */} + <div + className="fixed z-50 w-80 bg-white rounded-lg shadow-xl p-4" + style={tooltipStyle} + > + {/* Progress */} + <div className="flex gap-1 mb-3"> + {steps.map((_, i) => ( + <div + key={i} + className={`h-1 flex-1 rounded ${ + i <= currentStep ? 'bg-blue-500' : 'bg-gray-200' + }`} + /> + ))} + </div> + + <h3 className="font-semibold text-lg mb-2">{step.title}</h3> + <p className="text-gray-600 mb-4">{step.content}</p> + + {/* Actions */} + <div className="flex items-center justify-between"> + <button + onClick={handleSkip} + className="text-sm text-gray-500 hover:text-gray-700" + > + Skip tour + </button> + <div className="flex gap-2"> + {currentStep > 0 && ( + <button + onClick={handlePrev} + className="px-4 py-2 text-gray-600 hover:bg-gray-100 rounded" + > + Back + </button> + )} + <button + onClick={handleNext} + className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700" + > + {currentStep === steps.length - 1 ? 'Finish' : 'Next'} + </button> + </div> + </div> + </div> + </>, + document.body + ); +} + +function calculateTooltipPosition( + targetRect: DOMRect, + placement: string +): React.CSSProperties { + const offset = 16; + + switch (placement) { + case 'bottom': + return { + top: targetRect.bottom + offset, + left: targetRect.left + targetRect.width / 2, + transform: 'translateX(-50%)' + }; + case 'top': + return { + bottom: window.innerHeight - targetRect.top + offset, + left: targetRect.left + targetRect.width / 2, + transform: 'translateX(-50%)' + }; + case 'right': + return { + top: targetRect.top + targetRect.height / 2, + left: targetRect.right + offset, + transform: 'translateY(-50%)' + }; + case 'left': + return { + top: targetRect.top + targetRect.height / 2, + right: window.innerWidth - targetRect.left + offset, + transform: 'translateY(-50%)' + }; + default: + return {}; + } +} + +// CSS for highlighting +/* +.tour-highlight { + position: relative; + z-index: 51; + box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.5); + border-radius: 8px; +} +*/ +``` + +--- + +## Activation Tracking + +```typescript +// lib/onboarding/activation.ts +import { db } from '@/lib/db'; +import { track } from '@/lib/analytics'; + +interface ActivationEvent { + userId: string; + event: string; + metadata?: Record<string, unknown>; +} + +// Define activation criteria +const ACTIVATION_EVENTS = [ + { event: 'project_created', weight: 2 }, + { event: 'team_member_invited', weight: 1 }, + { event: 'integration_connected', weight: 2 }, + { event: 'workflow_completed', weight: 3 }, + { event: 'dashboard_customized', weight: 1 } +]; + +const ACTIVATION_THRESHOLD = 5; // Total weight needed + +export async function trackActivationEvent( + event: ActivationEvent +): Promise<{ isActivated: boolean; progress: number }> { + const { userId, event: eventName, metadata } = event; + + // Record the event + await db.userEvent.create({ + data: { + userId, + eventName, + metadata: metadata || {}, + occurredAt: new Date() + } + }); + + // Calculate activation progress + const userEvents = await db.userEvent.findMany({ + where: { userId }, + select: { eventName: true } + }); + + const uniqueEvents = new Set(userEvents.map(e => e.eventName)); + + let totalWeight = 0; + ACTIVATION_EVENTS.forEach(ae => { + if (uniqueEvents.has(ae.event)) { + totalWeight += ae.weight; + } + }); + + const progress = Math.min(100, (totalWeight / ACTIVATION_THRESHOLD) * 100); + const isActivated = totalWeight >= ACTIVATION_THRESHOLD; + + // Check if user just became activated + const user = await db.user.findUnique({ + where: { id: userId }, + select: { isActivated: true } + }); + + if (isActivated && !user?.isActivated) { + await activateUser(userId); + } + + return { isActivated, progress }; +} + +async function activateUser(userId: string) { + const activationTime = await calculateTimeToActivation(userId); + + await db.user.update({ + where: { id: userId }, + data: { + isActivated: true, + activatedAt: new Date() + } + }); + + track('user_activated', { + userId, + timeToActivation: activationTime + }); + + // Trigger post-activation flow + await triggerPostActivation(userId); +} + +async function calculateTimeToActivation(userId: string): Promise<number> { + const user = await db.user.findUnique({ + where: { id: userId }, + select: { createdAt: true } + }); + + if (!user) return 0; + + return Date.now() - user.createdAt.getTime(); +} + +async function triggerPostActivation(userId: string) { + // Send congratulations email + await sendEmail(userId, 'activation-congrats'); + + // Schedule upgrade prompt + await scheduleEmail(userId, 'upgrade-prompt', { delay: '3d' }); + + // Notify sales for qualified leads + const user = await db.user.findUnique({ + where: { id: userId }, + include: { tenant: { include: { subscription: true } } } + }); + + if (user?.tenant && isQualifiedLead(user.tenant)) { + await notifySales(userId, 'activated_qualified_lead'); + } +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **Time to Value** | < 5 minutes to first success | +| **Steps** | Max 5-7 onboarding steps | +| **Progress** | Always show completion % | +| **Personalization** | Tailor based on use case | +| **Help** | Offer chat/support at each step | +| **Celebration** | Acknowledge completions | + +--- + +## Fazit + +Effektives User Onboarding: + +1. **Progressive Disclosure**: Nicht alles auf einmal zeigen +2. **Quick Wins**: Erste Erfolge ermöglichen +3. **Tracking**: Activation Events messen +4. **Iteration**: Kontinuierlich verbessern + +Gutes Onboarding ist der Schlüssel zu Retention. + +--- + +## Bildprompts + +1. "Onboarding checklist UI, progress bar and completed steps" +2. "Product tour spotlight on feature, tooltip explanation" +3. "User activation funnel, stages from signup to activated" + +--- + +## Quellen + +- [Appcues Onboarding Guide](https://www.appcues.com/blog/user-onboarding) +- [UserPilot Onboarding](https://userpilot.com/blog/user-onboarding/) +- [Product-Led Growth Onboarding](https://www.productled.org/foundations/user-onboarding) +- [Intercom Onboarding](https://www.intercom.com/blog/user-onboarding-best-practices/) diff --git a/blog-posts/88-saas-analytics-metrics.md b/blog-posts/88-saas-analytics-metrics.md new file mode 100644 index 0000000..a7d05c8 --- /dev/null +++ b/blog-posts/88-saas-analytics-metrics.md @@ -0,0 +1,661 @@ +# SaaS Analytics & Metrics + +**Meta-Description:** SaaS Metriken verstehen und tracken. MRR, Churn, LTV, CAC und Product Analytics für datengetriebenes Wachstum. + +**Keywords:** SaaS Metrics, MRR, ARR, Churn Rate, LTV, CAC, Product Analytics, Cohort Analysis, Retention + +--- + +## Einführung + +**SaaS Metriken** sind das Cockpit für Wachstum. Von **MRR** bis **Churn Rate** – die richtigen KPIs messen und verstehen ist entscheidend für fundierte Entscheidungen. Dieser Guide zeigt die wichtigsten Metriken und deren Implementation. + +--- + +## SaaS Metrics Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SAAS METRICS FRAMEWORK │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Revenue Metrics: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ MRR (Monthly Recurring Revenue): │ │ +│ │ ├── New MRR (New customers) │ │ +│ │ ├── Expansion MRR (Upgrades, add-ons) │ │ +│ │ ├── Contraction MRR (Downgrades) │ │ +│ │ └── Churned MRR (Cancellations) │ │ +│ │ │ │ +│ │ Net MRR = New + Expansion - Contraction - Churn │ │ +│ │ │ │ +│ │ ARR = MRR × 12 │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Growth Metrics: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ CAC (Customer Acquisition Cost): │ │ +│ │ = (Sales + Marketing Costs) / New Customers │ │ +│ │ │ │ +│ │ LTV (Lifetime Value): │ │ +│ │ = ARPU / Churn Rate │ │ +│ │ │ │ +│ │ LTV:CAC Ratio: │ │ +│ │ ├── < 1:1 = Losing money │ │ +│ │ ├── 3:1 = Healthy │ │ +│ │ └── > 5:1 = Under-investing in growth │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Retention Metrics: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Churn Rate: │ │ +│ │ = Customers Lost / Total Customers × 100 │ │ +│ │ │ │ +│ │ Net Revenue Retention (NRR): │ │ +│ │ = (MRR + Expansion - Contraction - Churn) / MRR │ │ +│ │ ├── < 100% = Net negative (shrinking) │ │ +│ │ ├── 100% = Flat │ │ +│ │ └── > 100% = Net positive (growing) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Product Metrics: │ +│ ├── DAU/MAU (Daily/Monthly Active Users) │ +│ ├── Feature Adoption Rate │ +│ ├── Time to Value │ +│ └── NPS (Net Promoter Score) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Metrics Implementation + +```typescript +// lib/analytics/metrics.ts +import { db } from '@/lib/db'; +import { startOfMonth, endOfMonth, subMonths, differenceInDays } from 'date-fns'; + +interface MRRBreakdown { + newMRR: number; + expansionMRR: number; + contractionMRR: number; + churnedMRR: number; + netMRR: number; + totalMRR: number; +} + +export async function calculateMRR(date: Date = new Date()): Promise<MRRBreakdown> { + const monthStart = startOfMonth(date); + const monthEnd = endOfMonth(date); + const prevMonthStart = startOfMonth(subMonths(date, 1)); + const prevMonthEnd = endOfMonth(subMonths(date, 1)); + + // Get active subscriptions at end of month + const activeSubscriptions = await db.subscription.findMany({ + where: { + status: 'active', + startDate: { lte: monthEnd } + }, + include: { plan: true } + }); + + const totalMRR = activeSubscriptions.reduce( + (sum, sub) => sum + sub.plan.monthlyPrice, + 0 + ); + + // New subscriptions this month + const newSubscriptions = await db.subscription.findMany({ + where: { + startDate: { gte: monthStart, lte: monthEnd }, + previousSubscriptionId: null + }, + include: { plan: true } + }); + + const newMRR = newSubscriptions.reduce( + (sum, sub) => sum + sub.plan.monthlyPrice, + 0 + ); + + // Upgrades this month + const upgrades = await db.subscriptionChange.findMany({ + where: { + type: 'upgrade', + changedAt: { gte: monthStart, lte: monthEnd } + } + }); + + const expansionMRR = upgrades.reduce( + (sum, change) => sum + change.mrrDelta, + 0 + ); + + // Downgrades this month + const downgrades = await db.subscriptionChange.findMany({ + where: { + type: 'downgrade', + changedAt: { gte: monthStart, lte: monthEnd } + } + }); + + const contractionMRR = Math.abs(downgrades.reduce( + (sum, change) => sum + change.mrrDelta, + 0 + )); + + // Churned this month + const churned = await db.subscription.findMany({ + where: { + status: 'canceled', + canceledAt: { gte: monthStart, lte: monthEnd } + }, + include: { plan: true } + }); + + const churnedMRR = churned.reduce( + (sum, sub) => sum + sub.plan.monthlyPrice, + 0 + ); + + const netMRR = newMRR + expansionMRR - contractionMRR - churnedMRR; + + return { + newMRR, + expansionMRR, + contractionMRR, + churnedMRR, + netMRR, + totalMRR + }; +} + +export async function calculateChurnRate(date: Date = new Date()): Promise<{ + customerChurn: number; + revenueChurn: number; + netRevenueRetention: number; +}> { + const monthStart = startOfMonth(date); + const prevMonthStart = startOfMonth(subMonths(date, 1)); + const prevMonthEnd = endOfMonth(subMonths(date, 1)); + + // Customers at start of month + const customersAtStart = await db.subscription.count({ + where: { + status: 'active', + startDate: { lt: monthStart } + } + }); + + // Customers churned this month + const customersChurned = await db.subscription.count({ + where: { + status: 'canceled', + canceledAt: { gte: monthStart } + } + }); + + const customerChurn = customersAtStart > 0 + ? (customersChurned / customersAtStart) * 100 + : 0; + + // MRR calculations for revenue churn + const mrrAtStart = await db.subscription.aggregate({ + where: { + status: 'active', + startDate: { lt: monthStart } + }, + _sum: { monthlyAmount: true } + }); + + const startMRR = mrrAtStart._sum.monthlyAmount || 0; + + const { churnedMRR, expansionMRR, contractionMRR } = await calculateMRR(date); + + const revenueChurn = startMRR > 0 + ? ((churnedMRR + contractionMRR) / startMRR) * 100 + : 0; + + const netRevenueRetention = startMRR > 0 + ? ((startMRR + expansionMRR - contractionMRR - churnedMRR) / startMRR) * 100 + : 100; + + return { + customerChurn, + revenueChurn, + netRevenueRetention + }; +} + +export async function calculateLTV(): Promise<{ + averageLTV: number; + ltv_by_plan: Record<string, number>; +}> { + // Average customer lifespan + const canceledSubscriptions = await db.subscription.findMany({ + where: { status: 'canceled' }, + select: { + startDate: true, + canceledAt: true, + monthlyAmount: true, + plan: { select: { name: true } } + } + }); + + if (canceledSubscriptions.length === 0) { + return { averageLTV: 0, ltv_by_plan: {} }; + } + + // Calculate average lifespan and ARPU + let totalLifespanMonths = 0; + let totalRevenue = 0; + const planData: Record<string, { revenue: number; months: number; count: number }> = {}; + + canceledSubscriptions.forEach(sub => { + const months = differenceInDays(sub.canceledAt!, sub.startDate) / 30; + const revenue = months * sub.monthlyAmount; + + totalLifespanMonths += months; + totalRevenue += revenue; + + const planName = sub.plan.name; + if (!planData[planName]) { + planData[planName] = { revenue: 0, months: 0, count: 0 }; + } + planData[planName].revenue += revenue; + planData[planName].months += months; + planData[planName].count += 1; + }); + + const averageLTV = totalRevenue / canceledSubscriptions.length; + + const ltv_by_plan: Record<string, number> = {}; + Object.entries(planData).forEach(([plan, data]) => { + ltv_by_plan[plan] = data.revenue / data.count; + }); + + return { averageLTV, ltv_by_plan }; +} + +export async function calculateCAC( + startDate: Date, + endDate: Date +): Promise<{ + totalCAC: number; + cacByChannel: Record<string, number>; +}> { + // Get marketing and sales costs + const costs = await db.marketingCost.aggregate({ + where: { + date: { gte: startDate, lte: endDate } + }, + _sum: { amount: true } + }); + + // Get new customers in period + const newCustomers = await db.subscription.count({ + where: { + startDate: { gte: startDate, lte: endDate }, + previousSubscriptionId: null + } + }); + + const totalCosts = costs._sum.amount || 0; + const totalCAC = newCustomers > 0 ? totalCosts / newCustomers : 0; + + // CAC by channel + const costsByChannel = await db.marketingCost.groupBy({ + by: ['channel'], + where: { date: { gte: startDate, lte: endDate } }, + _sum: { amount: true } + }); + + const customersByChannel = await db.subscription.groupBy({ + by: ['acquisitionChannel'], + where: { + startDate: { gte: startDate, lte: endDate }, + previousSubscriptionId: null + }, + _count: true + }); + + const cacByChannel: Record<string, number> = {}; + costsByChannel.forEach(cost => { + const customers = customersByChannel.find( + c => c.acquisitionChannel === cost.channel + )?._count || 0; + cacByChannel[cost.channel] = customers > 0 + ? (cost._sum.amount || 0) / customers + : 0; + }); + + return { totalCAC, cacByChannel }; +} +``` + +--- + +## Cohort Analysis + +```typescript +// lib/analytics/cohorts.ts +import { db } from '@/lib/db'; +import { startOfMonth, format, differenceInMonths } from 'date-fns'; + +interface CohortData { + cohort: string; // "2024-01" + size: number; + retention: number[]; // Retention % for each month + revenue: number[]; // Revenue for each month +} + +export async function generateRetentionCohorts( + months: number = 12 +): Promise<CohortData[]> { + const cohorts: CohortData[] = []; + const today = new Date(); + + for (let i = months - 1; i >= 0; i--) { + const cohortDate = startOfMonth(subMonths(today, i)); + const cohortKey = format(cohortDate, 'yyyy-MM'); + + // Get users who signed up in this cohort + const cohortUsers = await db.user.findMany({ + where: { + createdAt: { + gte: cohortDate, + lt: startOfMonth(subMonths(today, i - 1)) + } + }, + select: { id: true } + }); + + const cohortSize = cohortUsers.length; + if (cohortSize === 0) continue; + + const userIds = cohortUsers.map(u => u.id); + const retention: number[] = []; + const revenue: number[] = []; + + // Calculate retention for each subsequent month + const monthsSinceCohor = differenceInMonths(today, cohortDate); + + for (let m = 0; m <= monthsSinceCohor; m++) { + const checkMonth = startOfMonth(subMonths(today, monthsSinceCohor - m)); + + // Count active users in this month + const activeUsers = await db.userActivity.groupBy({ + by: ['userId'], + where: { + userId: { in: userIds }, + activityDate: { + gte: checkMonth, + lt: startOfMonth(subMonths(today, monthsSinceCohor - m - 1)) + } + } + }); + + const retentionRate = (activeUsers.length / cohortSize) * 100; + retention.push(Math.round(retentionRate * 10) / 10); + + // Calculate revenue from this cohort in this month + const monthRevenue = await db.payment.aggregate({ + where: { + userId: { in: userIds }, + paidAt: { + gte: checkMonth, + lt: startOfMonth(subMonths(today, monthsSinceCohor - m - 1)) + } + }, + _sum: { amount: true } + }); + + revenue.push(monthRevenue._sum.amount || 0); + } + + cohorts.push({ + cohort: cohortKey, + size: cohortSize, + retention, + revenue + }); + } + + return cohorts; +} +``` + +--- + +## Analytics Dashboard + +```tsx +// app/admin/analytics/page.tsx +'use client'; + +import { useState, useEffect } from 'react'; +import { Line, Bar } from 'react-chartjs-2'; + +interface DashboardMetrics { + mrr: { + total: number; + growth: number; + breakdown: { + new: number; + expansion: number; + contraction: number; + churn: number; + }; + }; + customers: { + total: number; + new: number; + churned: number; + churnRate: number; + }; + revenue: { + ltv: number; + cac: number; + ltvCacRatio: number; + nrr: number; + }; + mrrHistory: Array<{ month: string; mrr: number }>; +} + +export default function AnalyticsDashboard() { + const [metrics, setMetrics] = useState<DashboardMetrics | null>(null); + const [dateRange, setDateRange] = useState('12m'); + + useEffect(() => { + fetch(`/api/analytics/dashboard?range=${dateRange}`) + .then(res => res.json()) + .then(setMetrics); + }, [dateRange]); + + if (!metrics) return <div>Loading...</div>; + + return ( + <div className="p-6 space-y-6"> + <div className="flex justify-between items-center"> + <h1 className="text-2xl font-bold">Analytics Dashboard</h1> + <select + value={dateRange} + onChange={(e) => setDateRange(e.target.value)} + className="border rounded px-3 py-2" + > + <option value="3m">Last 3 months</option> + <option value="6m">Last 6 months</option> + <option value="12m">Last 12 months</option> + </select> + </div> + + {/* Key Metrics */} + <div className="grid grid-cols-4 gap-4"> + <MetricCard + title="MRR" + value={`$${metrics.mrr.total.toLocaleString()}`} + change={metrics.mrr.growth} + changeLabel="vs last month" + /> + <MetricCard + title="Customers" + value={metrics.customers.total.toLocaleString()} + subtitle={`+${metrics.customers.new} new this month`} + /> + <MetricCard + title="Churn Rate" + value={`${metrics.customers.churnRate.toFixed(1)}%`} + status={metrics.customers.churnRate < 5 ? 'good' : 'warning'} + /> + <MetricCard + title="LTV:CAC" + value={`${metrics.revenue.ltvCacRatio.toFixed(1)}:1`} + status={metrics.revenue.ltvCacRatio >= 3 ? 'good' : 'warning'} + /> + </div> + + {/* MRR Chart */} + <div className="bg-white p-6 rounded-lg shadow"> + <h2 className="text-lg font-semibold mb-4">MRR Growth</h2> + <Line + data={{ + labels: metrics.mrrHistory.map(h => h.month), + datasets: [{ + label: 'MRR', + data: metrics.mrrHistory.map(h => h.mrr), + borderColor: '#3b82f6', + tension: 0.3 + }] + }} + options={{ + scales: { + y: { + beginAtZero: true, + ticks: { + callback: (value) => `$${value.toLocaleString()}` + } + } + } + }} + /> + </div> + + {/* MRR Breakdown */} + <div className="grid grid-cols-2 gap-6"> + <div className="bg-white p-6 rounded-lg shadow"> + <h2 className="text-lg font-semibold mb-4">MRR Breakdown</h2> + <MRRWaterfall data={metrics.mrr.breakdown} /> + </div> + + <div className="bg-white p-6 rounded-lg shadow"> + <h2 className="text-lg font-semibold mb-4">Unit Economics</h2> + <div className="space-y-4"> + <div className="flex justify-between"> + <span>Customer Lifetime Value (LTV)</span> + <span className="font-semibold"> + ${metrics.revenue.ltv.toLocaleString()} + </span> + </div> + <div className="flex justify-between"> + <span>Customer Acquisition Cost (CAC)</span> + <span className="font-semibold"> + ${metrics.revenue.cac.toLocaleString()} + </span> + </div> + <div className="flex justify-between"> + <span>Net Revenue Retention (NRR)</span> + <span className={`font-semibold ${ + metrics.revenue.nrr >= 100 ? 'text-green-600' : 'text-red-600' + }`}> + {metrics.revenue.nrr.toFixed(0)}% + </span> + </div> + </div> + </div> + </div> + </div> + ); +} + +function MetricCard({ + title, + value, + change, + changeLabel, + subtitle, + status +}: { + title: string; + value: string; + change?: number; + changeLabel?: string; + subtitle?: string; + status?: 'good' | 'warning' | 'bad'; +}) { + return ( + <div className="bg-white p-6 rounded-lg shadow"> + <h3 className="text-sm text-gray-500 mb-1">{title}</h3> + <div className="flex items-baseline gap-2"> + <span className="text-3xl font-bold">{value}</span> + {status && ( + <span className={`w-3 h-3 rounded-full ${ + status === 'good' ? 'bg-green-500' : + status === 'warning' ? 'bg-yellow-500' : 'bg-red-500' + }`} /> + )} + </div> + {change !== undefined && ( + <p className={`text-sm mt-1 ${change >= 0 ? 'text-green-600' : 'text-red-600'}`}> + {change >= 0 ? '+' : ''}{change.toFixed(1)}% {changeLabel} + </p> + )} + {subtitle && ( + <p className="text-sm text-gray-500 mt-1">{subtitle}</p> + )} + </div> + ); +} +``` + +--- + +## Metrics Benchmarks + +| Metric | Seed/Early | Series A | Scale | +|--------|------------|----------|-------| +| **MRR Growth** | 15-20%/mo | 10-15%/mo | 5-10%/mo | +| **Churn Rate** | < 8% | < 5% | < 3% | +| **NRR** | > 90% | > 100% | > 120% | +| **LTV:CAC** | > 1:1 | > 3:1 | > 5:1 | +| **CAC Payback** | < 18mo | < 12mo | < 8mo | + +--- + +## Fazit + +SaaS Analytics erfordert: + +1. **Revenue Metrics**: MRR, ARR, NRR verstehen +2. **Cohort Analysis**: Retention über Zeit tracken +3. **Unit Economics**: LTV:CAC optimieren +4. **Automation**: Dashboards automatisch aktualisieren + +Datengetriebene Entscheidungen führen zu nachhaltigem Wachstum. + +--- + +## Bildprompts + +1. "SaaS metrics dashboard, MRR chart and key performance indicators" +2. "Cohort retention heatmap, color-coded percentages" +3. "LTV:CAC ratio visualization, balance scale metaphor" + +--- + +## Quellen + +- [SaaS Metrics 2.0](https://www.forentrepreneurs.com/saas-metrics-2/) +- [ChartMogul SaaS Metrics](https://chartmogul.com/blog/saas-metrics/) +- [Stripe Atlas Guides](https://stripe.com/atlas/guides) +- [OpenView SaaS Benchmarks](https://openviewpartners.com/saas-benchmarks/) diff --git a/blog-posts/89-customer-success-automation.md b/blog-posts/89-customer-success-automation.md new file mode 100644 index 0000000..5461e1a --- /dev/null +++ b/blog-posts/89-customer-success-automation.md @@ -0,0 +1,784 @@ +# Customer Success Automation + +**Meta-Description:** Customer Success automatisieren. Health Scores, Lifecycle Emails, Churn Prediction und Proactive Outreach für SaaS. + +**Keywords:** Customer Success, Health Score, Churn Prediction, Lifecycle Marketing, Customer Retention, Automation, NPS + +--- + +## Einführung + +**Customer Success** ist der Schlüssel zu Retention und Expansion. Mit **Automation** können Health Scores berechnet, Risiko-Kunden identifiziert und proaktive Maßnahmen ergriffen werden – skalierbar für tausende Kunden. + +--- + +## Customer Success Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CUSTOMER SUCCESS AUTOMATION │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Customer Health Score: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Score Components (0-100): │ │ +│ │ ├── Product Usage (40%) │ │ +│ │ │ ├── Login frequency │ │ +│ │ │ ├── Feature adoption │ │ +│ │ │ └── Active users/seats │ │ +│ │ ├── Engagement (30%) │ │ +│ │ │ ├── Support tickets │ │ +│ │ │ ├── NPS score │ │ +│ │ │ └── Training completion │ │ +│ │ └── Business Value (30%) │ │ +│ │ ├── ROI achieved │ │ +│ │ ├── Expansion potential │ │ +│ │ └── Contract value │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Lifecycle Stages: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Onboarding → Adoption → Growth → Renewal │ │ +│ │ │ │ │ │ │ │ +│ │ ↓ ↓ ↓ ↓ │ │ +│ │ Welcome Training Upsell Review │ │ +│ │ Emails Content Offers Campaign │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Risk Triggers: │ +│ ├── Login decrease > 50% │ +│ ├── Support tickets spike │ +│ ├── Key user departure │ +│ ├── Contract renewal in 60 days │ +│ └── Low NPS score (< 7) │ +│ │ +│ Automated Actions: │ +│ ├── Email sequences │ +│ ├── In-app messages │ +│ ├── CSM alerts │ +│ ├── Scheduled calls │ +│ └── Personalized offers │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Health Score Calculation + +```typescript +// lib/customer-success/health-score.ts +import { db } from '@/lib/db'; +import { subDays, differenceInDays } from 'date-fns'; + +interface HealthScoreWeights { + usage: number; + engagement: number; + business: number; +} + +interface HealthScoreComponents { + usage: { + loginScore: number; + featureAdoptionScore: number; + activeUsersScore: number; + }; + engagement: { + supportScore: number; + npsScore: number; + trainingScore: number; + }; + business: { + contractValueScore: number; + expansionScore: number; + tenureScore: number; + }; +} + +interface HealthScore { + score: number; + trend: 'up' | 'down' | 'stable'; + riskLevel: 'healthy' | 'attention' | 'at-risk' | 'critical'; + components: HealthScoreComponents; + lastUpdated: Date; +} + +const DEFAULT_WEIGHTS: HealthScoreWeights = { + usage: 0.4, + engagement: 0.3, + business: 0.3 +}; + +export async function calculateHealthScore( + customerId: string, + weights: HealthScoreWeights = DEFAULT_WEIGHTS +): Promise<HealthScore> { + const now = new Date(); + const thirtyDaysAgo = subDays(now, 30); + + // Get customer data + const customer = await db.customer.findUnique({ + where: { id: customerId }, + include: { + subscription: true, + users: true, + supportTickets: { + where: { createdAt: { gte: thirtyDaysAgo } } + }, + npsResponses: { + orderBy: { createdAt: 'desc' }, + take: 1 + } + } + }); + + if (!customer) { + throw new Error('Customer not found'); + } + + // Calculate usage scores + const loginScore = await calculateLoginScore(customerId, thirtyDaysAgo); + const featureAdoptionScore = await calculateFeatureAdoptionScore(customerId); + const activeUsersScore = calculateActiveUsersScore(customer); + + const usageScore = (loginScore + featureAdoptionScore + activeUsersScore) / 3; + + // Calculate engagement scores + const supportScore = calculateSupportScore(customer.supportTickets); + const npsScore = calculateNPSHealthScore(customer.npsResponses[0]?.score); + const trainingScore = await calculateTrainingScore(customerId); + + const engagementScore = (supportScore + npsScore + trainingScore) / 3; + + // Calculate business scores + const contractValueScore = calculateContractValueScore(customer.subscription); + const expansionScore = await calculateExpansionScore(customerId); + const tenureScore = calculateTenureScore(customer.createdAt); + + const businessScore = (contractValueScore + expansionScore + tenureScore) / 3; + + // Weighted total + const totalScore = Math.round( + usageScore * weights.usage + + engagementScore * weights.engagement + + businessScore * weights.business + ); + + // Calculate trend + const previousScore = await getPreviousHealthScore(customerId); + const trend = getTrend(totalScore, previousScore); + + // Determine risk level + const riskLevel = getRiskLevel(totalScore); + + const healthScore: HealthScore = { + score: totalScore, + trend, + riskLevel, + components: { + usage: { loginScore, featureAdoptionScore, activeUsersScore }, + engagement: { supportScore, npsScore, trainingScore }, + business: { contractValueScore, expansionScore, tenureScore } + }, + lastUpdated: now + }; + + // Store for history + await db.healthScoreHistory.create({ + data: { + customerId, + score: totalScore, + components: healthScore.components, + calculatedAt: now + } + }); + + return healthScore; +} + +async function calculateLoginScore( + customerId: string, + since: Date +): Promise<number> { + const logins = await db.userActivity.count({ + where: { + user: { customerId }, + activityType: 'login', + createdAt: { gte: since } + } + }); + + // Expected: at least 20 logins per month for healthy usage + return Math.min(100, (logins / 20) * 100); +} + +async function calculateFeatureAdoptionScore( + customerId: string +): Promise<number> { + const totalFeatures = await db.feature.count({ + where: { isCore: true } + }); + + const usedFeatures = await db.featureUsage.groupBy({ + by: ['featureId'], + where: { + user: { customerId } + } + }); + + return Math.round((usedFeatures.length / totalFeatures) * 100); +} + +function calculateActiveUsersScore(customer: any): number { + const totalSeats = customer.subscription?.seats || 1; + const activeUsers = customer.users.filter((u: any) => + u.lastActiveAt && differenceInDays(new Date(), u.lastActiveAt) < 7 + ).length; + + return Math.round((activeUsers / totalSeats) * 100); +} + +function calculateSupportScore(tickets: any[]): number { + if (tickets.length === 0) return 100; + + // Fewer tickets = higher score, but some engagement is good + const ticketCount = tickets.length; + if (ticketCount <= 2) return 100; + if (ticketCount <= 5) return 80; + if (ticketCount <= 10) return 60; + return 40; +} + +function calculateNPSHealthScore(npsScore?: number): number { + if (npsScore === undefined) return 50; // Neutral if no data + if (npsScore >= 9) return 100; + if (npsScore >= 7) return 75; + if (npsScore >= 5) return 50; + return 25; +} + +function getRiskLevel(score: number): HealthScore['riskLevel'] { + if (score >= 80) return 'healthy'; + if (score >= 60) return 'attention'; + if (score >= 40) return 'at-risk'; + return 'critical'; +} + +function getTrend(current: number, previous?: number): HealthScore['trend'] { + if (!previous) return 'stable'; + const diff = current - previous; + if (diff > 5) return 'up'; + if (diff < -5) return 'down'; + return 'stable'; +} +``` + +--- + +## Lifecycle Email Automation + +```typescript +// lib/customer-success/lifecycle.ts +import { db } from '@/lib/db'; +import { addDays, differenceInDays } from 'date-fns'; + +interface LifecycleStage { + id: string; + name: string; + triggers: TriggerCondition[]; + actions: Action[]; +} + +interface TriggerCondition { + type: 'days_since_signup' | 'event' | 'health_score' | 'subscription'; + value: any; +} + +interface Action { + type: 'email' | 'in_app' | 'slack_notify' | 'task'; + template: string; + delay?: number; +} + +const lifecycleStages: LifecycleStage[] = [ + { + id: 'onboarding', + name: 'Onboarding', + triggers: [{ type: 'days_since_signup', value: { min: 0, max: 14 } }], + actions: [ + { type: 'email', template: 'welcome', delay: 0 }, + { type: 'email', template: 'getting-started', delay: 1 }, + { type: 'email', template: 'first-project', delay: 3 }, + { type: 'email', template: 'invite-team', delay: 7 }, + { type: 'email', template: 'onboarding-check-in', delay: 14 } + ] + }, + { + id: 'adoption', + name: 'Adoption', + triggers: [ + { type: 'days_since_signup', value: { min: 14, max: 60 } }, + { type: 'event', value: 'onboarding_completed' } + ], + actions: [ + { type: 'email', template: 'feature-spotlight-1', delay: 0 }, + { type: 'email', template: 'success-story', delay: 7 }, + { type: 'email', template: 'feature-spotlight-2', delay: 14 }, + { type: 'email', template: 'tips-tricks', delay: 21 } + ] + }, + { + id: 'growth', + name: 'Growth', + triggers: [ + { type: 'days_since_signup', value: { min: 60 } }, + { type: 'health_score', value: { min: 70 } } + ], + actions: [ + { type: 'email', template: 'upgrade-benefits', delay: 0 }, + { type: 'email', template: 'case-study', delay: 14 }, + { type: 'in_app', template: 'upgrade-prompt', delay: 7 } + ] + }, + { + id: 'renewal', + name: 'Renewal', + triggers: [ + { type: 'subscription', value: 'renewal_in_60_days' } + ], + actions: [ + { type: 'email', template: 'renewal-reminder-60', delay: 0 }, + { type: 'task', template: 'csm-outreach', delay: 0 }, + { type: 'email', template: 'renewal-reminder-30', delay: 30 }, + { type: 'email', template: 'renewal-reminder-7', delay: 53 } + ] + }, + { + id: 'at-risk', + name: 'At Risk', + triggers: [ + { type: 'health_score', value: { max: 40 } } + ], + actions: [ + { type: 'slack_notify', template: 'at-risk-alert', delay: 0 }, + { type: 'email', template: 'we-miss-you', delay: 1 }, + { type: 'task', template: 'csm-intervention', delay: 0 }, + { type: 'email', template: 'how-can-we-help', delay: 3 } + ] + } +]; + +// Process lifecycle for all customers +export async function processLifecycleAutomation(): Promise<void> { + const customers = await db.customer.findMany({ + include: { + subscription: true, + healthScore: true, + lifecycleState: true + } + }); + + for (const customer of customers) { + await processCustomerLifecycle(customer); + } +} + +async function processCustomerLifecycle(customer: any): Promise<void> { + // Determine current stage + const currentStage = determineLifecycleStage(customer); + + if (!currentStage) return; + + // Check if stage changed + const previousStage = customer.lifecycleState?.stageId; + + if (currentStage.id !== previousStage) { + // Update stage + await db.customerLifecycleState.upsert({ + where: { customerId: customer.id }, + create: { + customerId: customer.id, + stageId: currentStage.id, + enteredAt: new Date() + }, + update: { + stageId: currentStage.id, + enteredAt: new Date() + } + }); + + // Execute stage entry actions + for (const action of currentStage.actions) { + await scheduleAction(customer.id, action); + } + } +} + +function determineLifecycleStage(customer: any): LifecycleStage | null { + const daysSinceSignup = differenceInDays(new Date(), customer.createdAt); + const healthScore = customer.healthScore?.score || 50; + const renewalDays = customer.subscription?.currentPeriodEnd + ? differenceInDays(customer.subscription.currentPeriodEnd, new Date()) + : null; + + // Check stages in priority order + if (healthScore < 40) { + return lifecycleStages.find(s => s.id === 'at-risk')!; + } + + if (renewalDays !== null && renewalDays <= 60) { + return lifecycleStages.find(s => s.id === 'renewal')!; + } + + if (daysSinceSignup < 14) { + return lifecycleStages.find(s => s.id === 'onboarding')!; + } + + if (daysSinceSignup < 60) { + return lifecycleStages.find(s => s.id === 'adoption')!; + } + + return lifecycleStages.find(s => s.id === 'growth')!; +} + +async function scheduleAction( + customerId: string, + action: Action +): Promise<void> { + const scheduledFor = action.delay + ? addDays(new Date(), action.delay) + : new Date(); + + await db.scheduledAction.create({ + data: { + customerId, + actionType: action.type, + template: action.template, + scheduledFor, + status: 'pending' + } + }); +} +``` + +--- + +## Churn Prediction + +```typescript +// lib/customer-success/churn-prediction.ts +import { db } from '@/lib/db'; + +interface ChurnPrediction { + customerId: string; + churnProbability: number; + riskFactors: RiskFactor[]; + recommendedActions: string[]; +} + +interface RiskFactor { + factor: string; + impact: 'high' | 'medium' | 'low'; + currentValue: number; + threshold: number; +} + +export async function predictChurn(customerId: string): Promise<ChurnPrediction> { + const riskFactors: RiskFactor[] = []; + let churnScore = 0; + + // Factor 1: Login frequency decline + const loginDecline = await calculateLoginDecline(customerId); + if (loginDecline > 50) { + riskFactors.push({ + factor: 'Login frequency decreased', + impact: 'high', + currentValue: loginDecline, + threshold: 50 + }); + churnScore += 25; + } + + // Factor 2: Feature usage decline + const featureDecline = await calculateFeatureUsageDecline(customerId); + if (featureDecline > 30) { + riskFactors.push({ + factor: 'Feature usage decreased', + impact: 'medium', + currentValue: featureDecline, + threshold: 30 + }); + churnScore += 15; + } + + // Factor 3: Support sentiment + const supportSentiment = await analyzeSupportSentiment(customerId); + if (supportSentiment < 0.3) { + riskFactors.push({ + factor: 'Negative support interactions', + impact: 'high', + currentValue: supportSentiment, + threshold: 0.3 + }); + churnScore += 20; + } + + // Factor 4: NPS score + const npsScore = await getLatestNPS(customerId); + if (npsScore !== null && npsScore < 7) { + riskFactors.push({ + factor: 'Low NPS score', + impact: npsScore < 5 ? 'high' : 'medium', + currentValue: npsScore, + threshold: 7 + }); + churnScore += npsScore < 5 ? 25 : 15; + } + + // Factor 5: Key user departure + const keyUserLeft = await checkKeyUserDeparture(customerId); + if (keyUserLeft) { + riskFactors.push({ + factor: 'Key user left organization', + impact: 'high', + currentValue: 1, + threshold: 0 + }); + churnScore += 20; + } + + // Factor 6: Contract value change + const contractDecreased = await checkContractDecrease(customerId); + if (contractDecreased) { + riskFactors.push({ + factor: 'Contract value decreased', + impact: 'medium', + currentValue: 1, + threshold: 0 + }); + churnScore += 15; + } + + const churnProbability = Math.min(100, churnScore); + + // Generate recommended actions + const recommendedActions = generateRecommendations(riskFactors); + + return { + customerId, + churnProbability, + riskFactors, + recommendedActions + }; +} + +function generateRecommendations(riskFactors: RiskFactor[]): string[] { + const recommendations: string[] = []; + + const factorTypes = riskFactors.map(f => f.factor); + + if (factorTypes.includes('Login frequency decreased')) { + recommendations.push('Schedule re-engagement call'); + recommendations.push('Send personalized win-back email'); + } + + if (factorTypes.includes('Feature usage decreased')) { + recommendations.push('Offer feature training session'); + recommendations.push('Share relevant use case examples'); + } + + if (factorTypes.includes('Negative support interactions')) { + recommendations.push('Escalate to senior support'); + recommendations.push('CSM personal outreach'); + } + + if (factorTypes.includes('Low NPS score')) { + recommendations.push('Schedule NPS follow-up call'); + recommendations.push('Address specific feedback'); + } + + if (factorTypes.includes('Key user left organization')) { + recommendations.push('Identify and onboard new champion'); + recommendations.push('Executive sponsor outreach'); + } + + return recommendations; +} + +// Batch process all customers +export async function runChurnPredictions(): Promise<void> { + const customers = await db.customer.findMany({ + where: { subscription: { status: 'active' } } + }); + + for (const customer of customers) { + const prediction = await predictChurn(customer.id); + + // Store prediction + await db.churnPrediction.upsert({ + where: { customerId: customer.id }, + create: { + customerId: customer.id, + probability: prediction.churnProbability, + riskFactors: prediction.riskFactors, + predictedAt: new Date() + }, + update: { + probability: prediction.churnProbability, + riskFactors: prediction.riskFactors, + predictedAt: new Date() + } + }); + + // Alert if high risk + if (prediction.churnProbability > 70) { + await createChurnAlert(customer.id, prediction); + } + } +} +``` + +--- + +## NPS Survey Automation + +```tsx +// components/NPS/NPSSurvey.tsx +'use client'; + +import { useState } from 'react'; + +interface NPSSurveyProps { + onSubmit: (score: number, feedback?: string) => void; + onDismiss: () => void; +} + +export function NPSSurvey({ onSubmit, onDismiss }: NPSSurveyProps) { + const [score, setScore] = useState<number | null>(null); + const [feedback, setFeedback] = useState(''); + const [step, setStep] = useState<'score' | 'feedback'>('score'); + + const handleScoreSelect = (value: number) => { + setScore(value); + setStep('feedback'); + }; + + const handleSubmit = () => { + if (score !== null) { + onSubmit(score, feedback || undefined); + } + }; + + return ( + <div className="fixed bottom-4 right-4 w-96 bg-white rounded-lg shadow-xl p-6"> + <button + onClick={onDismiss} + className="absolute top-2 right-2 text-gray-400" + > + × + </button> + + {step === 'score' ? ( + <> + <h3 className="text-lg font-semibold mb-4"> + How likely are you to recommend us? + </h3> + + <div className="flex gap-1 mb-4"> + {Array.from({ length: 11 }, (_, i) => ( + <button + key={i} + onClick={() => handleScoreSelect(i)} + className={`w-8 h-8 rounded text-sm font-medium transition ${ + i <= 6 ? 'hover:bg-red-100' : + i <= 8 ? 'hover:bg-yellow-100' : 'hover:bg-green-100' + } ${score === i ? 'ring-2 ring-blue-500' : 'bg-gray-100'}`} + > + {i} + </button> + ))} + </div> + + <div className="flex justify-between text-xs text-gray-500"> + <span>Not at all likely</span> + <span>Extremely likely</span> + </div> + </> + ) : ( + <> + <h3 className="text-lg font-semibold mb-2"> + {score! >= 9 ? "That's great! What do you love most?" : + score! >= 7 ? "Thanks! How can we improve?" : + "We're sorry to hear that. What went wrong?"} + </h3> + + <textarea + value={feedback} + onChange={(e) => setFeedback(e.target.value)} + placeholder="Your feedback helps us improve..." + className="w-full p-3 border rounded-lg mb-4" + rows={3} + /> + + <div className="flex gap-2"> + <button + onClick={() => setStep('score')} + className="px-4 py-2 text-gray-600" + > + Back + </button> + <button + onClick={handleSubmit} + className="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg" + > + Submit + </button> + </div> + </> + )} + </div> + ); +} +``` + +--- + +## Best Practices + +| Area | Recommendation | +|------|----------------| +| **Health Score** | Update daily, weight factors appropriately | +| **Lifecycle** | Personalize based on segment and behavior | +| **Churn** | Act on predictions within 48 hours | +| **NPS** | Survey at key moments, not randomly | +| **Automation** | Always allow human override | + +--- + +## Fazit + +Customer Success Automation ermöglicht: + +1. **Health Scores**: Frühzeitige Risiko-Erkennung +2. **Lifecycle Emails**: Skalierbare Kommunikation +3. **Churn Prediction**: Proaktive Intervention +4. **NPS**: Kontinuierliches Feedback + +Automatisierung skaliert Customer Success ohne Qualitätsverlust. + +--- + +## Bildprompts + +1. "Customer health score dashboard, gauge visualization with risk levels" +2. "Lifecycle email automation flow, triggered sequences" +3. "Churn prediction model, risk factors highlighted" + +--- + +## Quellen + +- [Gainsight Customer Success](https://www.gainsight.com/customer-success/) +- [ChurnZero Best Practices](https://churnzero.com/resources/) +- [Customer Success Metrics](https://www.custify.com/blog/customer-success-metrics/) +- [NPS Best Practices](https://www.netpromoter.com/know/) diff --git a/blog-posts/90-saas-security-compliance.md b/blog-posts/90-saas-security-compliance.md new file mode 100644 index 0000000..f62a47d --- /dev/null +++ b/blog-posts/90-saas-security-compliance.md @@ -0,0 +1,734 @@ +# SaaS Security & Compliance + +**Meta-Description:** SaaS Security Best Practices. SOC 2, GDPR Compliance, Data Encryption und Security Architecture für B2B SaaS. + +**Keywords:** SaaS Security, SOC 2, GDPR, Data Encryption, Authentication, Compliance, Security Architecture + +--- + +## Einführung + +**Security** ist für B2B SaaS nicht optional – es ist Verkaufsargument. Von **SOC 2** bis **GDPR** erwarten Enterprise-Kunden nachweisbare Sicherheit. Dieser Guide zeigt Security Best Practices und Compliance-Anforderungen. + +--- + +## Security Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SAAS SECURITY ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Defense in Depth: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Layer 1: Network Security │ │ +│ │ ├── WAF (Web Application Firewall) │ │ +│ │ ├── DDoS Protection │ │ +│ │ ├── Rate Limiting │ │ +│ │ └── IP Whitelisting (optional) │ │ +│ ├─────────────────────────────────────────────────────│ │ +│ │ Layer 2: Application Security │ │ +│ │ ├── Authentication (MFA, SSO) │ │ +│ │ ├── Authorization (RBAC, ABAC) │ │ +│ │ ├── Input Validation │ │ +│ │ └── CSRF/XSS Protection │ │ +│ ├─────────────────────────────────────────────────────│ │ +│ │ Layer 3: Data Security │ │ +│ │ ├── Encryption at Rest (AES-256) │ │ +│ │ ├── Encryption in Transit (TLS 1.3) │ │ +│ │ ├── Key Management (KMS) │ │ +│ │ └── Data Masking/Tokenization │ │ +│ ├─────────────────────────────────────────────────────│ │ +│ │ Layer 4: Infrastructure Security │ │ +│ │ ├── Private Networks (VPC) │ │ +│ │ ├── Security Groups │ │ +│ │ ├── Secrets Management │ │ +│ │ └── Container Security │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Compliance Frameworks: │ +│ ├── SOC 2 Type II │ +│ ├── GDPR │ +│ ├── HIPAA (Healthcare) │ +│ ├── PCI DSS (Payments) │ +│ └── ISO 27001 │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Authentication & Authorization + +```typescript +// lib/auth/config.ts +import { NextAuthConfig } from 'next-auth'; +import { PrismaAdapter } from '@auth/prisma-adapter'; +import { db } from '@/lib/db'; +import Google from 'next-auth/providers/google'; +import Credentials from 'next-auth/providers/credentials'; +import { compare } from 'bcryptjs'; + +export const authConfig: NextAuthConfig = { + adapter: PrismaAdapter(db), + providers: [ + Google({ + clientId: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_CLIENT_SECRET! + }), + Credentials({ + credentials: { + email: { label: 'Email', type: 'email' }, + password: { label: 'Password', type: 'password' } + }, + async authorize(credentials) { + if (!credentials?.email || !credentials?.password) { + return null; + } + + const user = await db.user.findUnique({ + where: { email: credentials.email as string }, + include: { tenant: true } + }); + + if (!user || !user.passwordHash) { + return null; + } + + const isValid = await compare( + credentials.password as string, + user.passwordHash + ); + + if (!isValid) { + // Log failed attempt + await logSecurityEvent('failed_login', { + email: credentials.email, + ip: 'unknown' // Get from request + }); + return null; + } + + // Check if MFA is required + if (user.mfaEnabled) { + return { ...user, requiresMfa: true }; + } + + return user; + } + }) + ], + callbacks: { + async jwt({ token, user }) { + if (user) { + token.tenantId = user.tenantId; + token.role = user.role; + token.permissions = await getUserPermissions(user.id); + } + return token; + }, + async session({ session, token }) { + session.user.tenantId = token.tenantId; + session.user.role = token.role; + session.user.permissions = token.permissions; + return session; + } + }, + pages: { + signIn: '/auth/login', + error: '/auth/error' + }, + session: { + strategy: 'jwt', + maxAge: 24 * 60 * 60 // 24 hours + }, + events: { + async signIn({ user, isNewUser }) { + await logSecurityEvent('login', { + userId: user.id, + isNewUser + }); + }, + async signOut({ token }) { + await logSecurityEvent('logout', { + userId: token.sub + }); + } + } +}; +``` + +```typescript +// lib/auth/mfa.ts +import { authenticator } from 'otplib'; +import { db } from '@/lib/db'; +import { encrypt, decrypt } from '@/lib/crypto'; + +export async function setupMFA(userId: string): Promise<{ + secret: string; + qrCodeUrl: string; +}> { + const secret = authenticator.generateSecret(); + + // Encrypt secret before storing + const encryptedSecret = await encrypt(secret); + + await db.user.update({ + where: { id: userId }, + data: { + mfaSecret: encryptedSecret, + mfaEnabled: false // Enable after verification + } + }); + + const user = await db.user.findUnique({ + where: { id: userId }, + select: { email: true } + }); + + const otpAuthUrl = authenticator.keyuri( + user!.email, + 'YourApp', + secret + ); + + return { + secret, + qrCodeUrl: otpAuthUrl + }; +} + +export async function verifyMFAToken( + userId: string, + token: string +): Promise<boolean> { + const user = await db.user.findUnique({ + where: { id: userId }, + select: { mfaSecret: true } + }); + + if (!user?.mfaSecret) { + return false; + } + + const secret = await decrypt(user.mfaSecret); + const isValid = authenticator.verify({ token, secret }); + + if (isValid) { + // Enable MFA if this is setup verification + await db.user.update({ + where: { id: userId }, + data: { mfaEnabled: true } + }); + } + + return isValid; +} + +// Generate backup codes +export async function generateBackupCodes(userId: string): Promise<string[]> { + const codes: string[] = []; + + for (let i = 0; i < 10; i++) { + codes.push(generateSecureCode(8)); + } + + // Hash and store codes + const hashedCodes = await Promise.all( + codes.map(code => hashCode(code)) + ); + + await db.mfaBackupCode.createMany({ + data: hashedCodes.map(hash => ({ + userId, + codeHash: hash, + used: false + })) + }); + + return codes; // Return plain codes to show user once +} +``` + +--- + +## Role-Based Access Control + +```typescript +// lib/auth/rbac.ts +export const permissions = { + // Project permissions + 'project:read': 'View projects', + 'project:create': 'Create projects', + 'project:update': 'Update projects', + 'project:delete': 'Delete projects', + + // User management + 'user:read': 'View users', + 'user:invite': 'Invite users', + 'user:update': 'Update users', + 'user:delete': 'Remove users', + + // Billing + 'billing:read': 'View billing', + 'billing:manage': 'Manage billing', + + // Admin + 'admin:settings': 'Manage settings', + 'admin:audit': 'View audit logs' +} as const; + +export type Permission = keyof typeof permissions; + +export const roles = { + owner: { + name: 'Owner', + permissions: Object.keys(permissions) as Permission[] + }, + admin: { + name: 'Admin', + permissions: [ + 'project:read', 'project:create', 'project:update', 'project:delete', + 'user:read', 'user:invite', 'user:update', + 'billing:read', 'admin:settings' + ] as Permission[] + }, + member: { + name: 'Member', + permissions: [ + 'project:read', 'project:create', 'project:update', + 'user:read' + ] as Permission[] + }, + viewer: { + name: 'Viewer', + permissions: ['project:read', 'user:read'] as Permission[] + } +} as const; + +export type Role = keyof typeof roles; + +// Permission check middleware +export function requirePermission(permission: Permission) { + return async (req: Request) => { + const session = await getServerSession(); + + if (!session?.user) { + throw new Error('Unauthorized'); + } + + const userPermissions = session.user.permissions || []; + + if (!userPermissions.includes(permission)) { + await logSecurityEvent('permission_denied', { + userId: session.user.id, + permission, + resource: req.url + }); + throw new Error('Forbidden'); + } + }; +} + +// React hook for permission checks +export function usePermission(permission: Permission): boolean { + const { data: session } = useSession(); + return session?.user?.permissions?.includes(permission) ?? false; +} +``` + +--- + +## Data Encryption + +```typescript +// lib/crypto/encryption.ts +import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto'; +import { promisify } from 'util'; + +const scryptAsync = promisify(scrypt); + +const ALGORITHM = 'aes-256-gcm'; +const KEY_LENGTH = 32; +const IV_LENGTH = 16; +const AUTH_TAG_LENGTH = 16; + +// Derive key from master key +async function deriveKey(masterKey: string, salt: Buffer): Promise<Buffer> { + return scryptAsync(masterKey, salt, KEY_LENGTH) as Promise<Buffer>; +} + +export async function encrypt(plaintext: string): Promise<string> { + const masterKey = process.env.ENCRYPTION_KEY!; + const salt = randomBytes(16); + const key = await deriveKey(masterKey, salt); + const iv = randomBytes(IV_LENGTH); + + const cipher = createCipheriv(ALGORITHM, key, iv); + const encrypted = Buffer.concat([ + cipher.update(plaintext, 'utf8'), + cipher.final() + ]); + const authTag = cipher.getAuthTag(); + + // Combine: salt + iv + authTag + encrypted + const combined = Buffer.concat([salt, iv, authTag, encrypted]); + return combined.toString('base64'); +} + +export async function decrypt(ciphertext: string): Promise<string> { + const masterKey = process.env.ENCRYPTION_KEY!; + const combined = Buffer.from(ciphertext, 'base64'); + + // Extract components + const salt = combined.subarray(0, 16); + const iv = combined.subarray(16, 32); + const authTag = combined.subarray(32, 48); + const encrypted = combined.subarray(48); + + const key = await deriveKey(masterKey, salt); + + const decipher = createDecipheriv(ALGORITHM, key, iv); + decipher.setAuthTag(authTag); + + const decrypted = Buffer.concat([ + decipher.update(encrypted), + decipher.final() + ]); + + return decrypted.toString('utf8'); +} + +// Field-level encryption for sensitive data +export class EncryptedField { + static async encrypt(value: string): Promise<string> { + return encrypt(value); + } + + static async decrypt(value: string): Promise<string> { + return decrypt(value); + } +} + +// Example usage in Prisma +// middleware for automatic encryption +export function encryptionMiddleware() { + return async (params: any, next: any) => { + // Define fields that need encryption + const encryptedFields: Record<string, string[]> = { + User: ['ssn', 'taxId'], + PaymentMethod: ['cardNumber', 'cvv'], + Integration: ['accessToken', 'refreshToken'] + }; + + const fields = encryptedFields[params.model]; + if (!fields) return next(params); + + // Encrypt on create/update + if (['create', 'update', 'upsert'].includes(params.action)) { + for (const field of fields) { + if (params.args.data?.[field]) { + params.args.data[field] = await encrypt(params.args.data[field]); + } + } + } + + const result = await next(params); + + // Decrypt on read + if (['findUnique', 'findFirst', 'findMany'].includes(params.action) && result) { + const items = Array.isArray(result) ? result : [result]; + for (const item of items) { + for (const field of fields) { + if (item[field]) { + item[field] = await decrypt(item[field]); + } + } + } + } + + return result; + }; +} +``` + +--- + +## Audit Logging + +```typescript +// lib/security/audit.ts +import { db } from '@/lib/db'; +import { headers } from 'next/headers'; + +interface AuditEvent { + action: string; + userId?: string; + tenantId?: string; + resourceType?: string; + resourceId?: string; + details?: Record<string, unknown>; + ip?: string; + userAgent?: string; +} + +export async function logSecurityEvent( + action: string, + details: Record<string, unknown> = {} +): Promise<void> { + const headersList = await headers(); + + const event: AuditEvent = { + action, + details, + ip: headersList.get('x-forwarded-for') || headersList.get('x-real-ip') || 'unknown', + userAgent: headersList.get('user-agent') || 'unknown' + }; + + await db.auditLog.create({ + data: { + ...event, + details: event.details, + createdAt: new Date() + } + }); + + // For critical events, also alert + if (isCriticalEvent(action)) { + await sendSecurityAlert(event); + } +} + +function isCriticalEvent(action: string): boolean { + const criticalActions = [ + 'failed_login_attempt', + 'permission_denied', + 'data_export', + 'user_deleted', + 'api_key_created', + 'mfa_disabled', + 'suspicious_activity' + ]; + + return criticalActions.includes(action); +} + +// Audit log query for compliance +export async function getAuditLogs( + tenantId: string, + options: { + startDate?: Date; + endDate?: Date; + action?: string; + userId?: string; + limit?: number; + offset?: number; + } +) { + return db.auditLog.findMany({ + where: { + tenantId, + ...(options.action && { action: options.action }), + ...(options.userId && { userId: options.userId }), + createdAt: { + ...(options.startDate && { gte: options.startDate }), + ...(options.endDate && { lte: options.endDate }) + } + }, + orderBy: { createdAt: 'desc' }, + take: options.limit || 100, + skip: options.offset || 0 + }); +} + +// Data retention (GDPR compliance) +export async function purgeOldAuditLogs(retentionDays: number = 365): Promise<number> { + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - retentionDays); + + const result = await db.auditLog.deleteMany({ + where: { + createdAt: { lt: cutoffDate } + } + }); + + return result.count; +} +``` + +--- + +## GDPR Compliance + +```typescript +// lib/gdpr/data-subject.ts +import { db } from '@/lib/db'; +import { encrypt } from '@/lib/crypto'; + +// Data export (Right to Portability) +export async function exportUserData(userId: string): Promise<object> { + const user = await db.user.findUnique({ + where: { id: userId }, + include: { + profile: true, + projects: true, + activities: true, + preferences: true + } + }); + + if (!user) { + throw new Error('User not found'); + } + + // Remove internal fields + const exportData = { + personalInfo: { + email: user.email, + name: user.name, + createdAt: user.createdAt + }, + profile: user.profile, + projects: user.projects.map(p => ({ + name: p.name, + createdAt: p.createdAt + })), + preferences: user.preferences, + activityLog: user.activities.slice(0, 1000) // Limit + }; + + // Log the export + await logSecurityEvent('data_export', { + userId, + dataTypes: Object.keys(exportData) + }); + + return exportData; +} + +// Data deletion (Right to Erasure) +export async function deleteUserData( + userId: string, + options: { hardDelete?: boolean } = {} +): Promise<void> { + const user = await db.user.findUnique({ + where: { id: userId } + }); + + if (!user) { + throw new Error('User not found'); + } + + if (options.hardDelete) { + // Permanent deletion + await db.$transaction([ + db.activity.deleteMany({ where: { userId } }), + db.project.deleteMany({ where: { ownerId: userId } }), + db.profile.delete({ where: { userId } }), + db.user.delete({ where: { id: userId } }) + ]); + } else { + // Soft delete with anonymization + await db.$transaction([ + db.user.update({ + where: { id: userId }, + data: { + email: `deleted_${userId}@anonymized.local`, + name: 'Deleted User', + deletedAt: new Date(), + passwordHash: null, + mfaSecret: null + } + }), + db.profile.update({ + where: { userId }, + data: { + bio: null, + avatar: null, + phone: null + } + }) + ]); + } + + await logSecurityEvent('user_deleted', { + userId, + hardDelete: options.hardDelete + }); +} + +// Consent management +export async function updateConsent( + userId: string, + consents: { + marketing?: boolean; + analytics?: boolean; + thirdParty?: boolean; + } +): Promise<void> { + await db.userConsent.upsert({ + where: { userId }, + create: { + userId, + ...consents, + updatedAt: new Date() + }, + update: { + ...consents, + updatedAt: new Date() + } + }); + + await logSecurityEvent('consent_updated', { + userId, + consents + }); +} +``` + +--- + +## Security Checklist + +| Category | Requirement | Priority | +|----------|-------------|----------| +| **Auth** | MFA for admins | Critical | +| **Auth** | SSO/SAML support | Important | +| **Data** | Encryption at rest | Critical | +| **Data** | Encryption in transit | Critical | +| **Access** | RBAC implementation | Critical | +| **Audit** | Comprehensive logging | Critical | +| **GDPR** | Data export capability | Required | +| **GDPR** | Data deletion process | Required | +| **Network** | WAF configured | Important | +| **Secrets** | Secure key management | Critical | + +--- + +## Fazit + +SaaS Security erfordert: + +1. **Defense in Depth**: Mehrere Sicherheitsebenen +2. **Compliance**: SOC 2, GDPR von Anfang an +3. **Encryption**: Daten at rest und in transit +4. **Audit Trails**: Vollständige Nachverfolgbarkeit + +Security ist kein Feature, sondern Grundvoraussetzung. + +--- + +## Bildprompts + +1. "Security architecture layers diagram, defense in depth visualization" +2. "SOC 2 compliance dashboard, audit controls checklist" +3. "Encryption key management flow, secure vault concept" + +--- + +## Quellen + +- [SOC 2 Compliance Guide](https://www.aicpa.org/resources/soc-2) +- [GDPR Official Text](https://gdpr.eu/) +- [OWASP Security Guidelines](https://owasp.org/www-project-web-security-testing-guide/) +- [AWS Security Best Practices](https://aws.amazon.com/security/security-resources/) diff --git a/blog-posts/91-api-design-documentation.md b/blog-posts/91-api-design-documentation.md new file mode 100644 index 0000000..213e38b --- /dev/null +++ b/blog-posts/91-api-design-documentation.md @@ -0,0 +1,616 @@ +# API Design & Documentation + +**Meta-Description:** REST API Design Best Practices. OpenAPI Specification, Dokumentation mit Swagger und Developer Experience optimieren. + +**Keywords:** API Design, REST API, OpenAPI, Swagger, API Documentation, Developer Experience, API Best Practices + +--- + +## Einführung + +**Gutes API Design** ist entscheidend für Developer Experience. Mit **OpenAPI/Swagger** können APIs dokumentiert, getestet und generiert werden. Dieser Guide zeigt Best Practices für konsistente, intuitive APIs. + +--- + +## API Design Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ REST API DESIGN PRINCIPLES │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ URL Structure: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Resources (Nouns, plural): │ │ +│ │ ├── GET /users List users │ │ +│ │ ├── POST /users Create user │ │ +│ │ ├── GET /users/{id} Get user │ │ +│ │ ├── PUT /users/{id} Update user │ │ +│ │ ├── PATCH /users/{id} Partial update │ │ +│ │ └── DELETE /users/{id} Delete user │ │ +│ │ │ │ +│ │ Nested Resources: │ │ +│ │ ├── GET /users/{id}/projects │ │ +│ │ └── POST /users/{id}/projects │ │ +│ │ │ │ +│ │ Query Parameters: │ │ +│ │ ├── ?page=1&limit=20 Pagination │ │ +│ │ ├── ?sort=createdAt:desc Sorting │ │ +│ │ ├── ?filter[status]=active Filtering │ │ +│ │ └── ?fields=id,name,email Field selection │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ HTTP Status Codes: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 2xx Success: │ │ +│ │ ├── 200 OK Successful request │ │ +│ │ ├── 201 Created Resource created │ │ +│ │ └── 204 No Content Successful, no body │ │ +│ │ │ │ +│ │ 4xx Client Errors: │ │ +│ │ ├── 400 Bad Request Invalid input │ │ +│ │ ├── 401 Unauthorized Not authenticated │ │ +│ │ ├── 403 Forbidden Not authorized │ │ +│ │ ├── 404 Not Found Resource not found │ │ +│ │ └── 422 Unprocessable Validation failed │ │ +│ │ │ │ +│ │ 5xx Server Errors: │ │ +│ │ ├── 500 Internal Error Server error │ │ +│ │ └── 503 Service Unavailable Temporarily down │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## OpenAPI Specification + +```yaml +# openapi/api.yaml +openapi: 3.1.0 +info: + title: My SaaS API + description: API for managing projects and tasks + version: 1.0.0 + contact: + name: API Support + email: api@example.com + url: https://example.com/support + license: + name: MIT + url: https://opensource.org/licenses/MIT + +servers: + - url: https://api.example.com/v1 + description: Production + - url: https://api.staging.example.com/v1 + description: Staging + - url: http://localhost:3000/api/v1 + description: Local development + +tags: + - name: Projects + description: Project management endpoints + - name: Tasks + description: Task management endpoints + - name: Users + description: User management endpoints + +paths: + /projects: + get: + tags: [Projects] + summary: List all projects + description: Returns a paginated list of projects for the authenticated user + operationId: listProjects + security: + - bearerAuth: [] + parameters: + - $ref: '#/components/parameters/PageParam' + - $ref: '#/components/parameters/LimitParam' + - name: status + in: query + description: Filter by status + schema: + type: string + enum: [active, archived, draft] + - name: sort + in: query + description: Sort field and direction + schema: + type: string + example: createdAt:desc + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/ProjectListResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalError' + + post: + tags: [Projects] + summary: Create a project + description: Creates a new project + operationId: createProject + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateProjectInput' + responses: + '201': + description: Project created + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + '400': + $ref: '#/components/responses/BadRequest' + '422': + $ref: '#/components/responses/ValidationError' + + /projects/{projectId}: + get: + tags: [Projects] + summary: Get a project + operationId: getProject + security: + - bearerAuth: [] + parameters: + - $ref: '#/components/parameters/ProjectIdParam' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + '404': + $ref: '#/components/responses/NotFound' + + patch: + tags: [Projects] + summary: Update a project + operationId: updateProject + security: + - bearerAuth: [] + parameters: + - $ref: '#/components/parameters/ProjectIdParam' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateProjectInput' + responses: + '200': + description: Project updated + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + + delete: + tags: [Projects] + summary: Delete a project + operationId: deleteProject + security: + - bearerAuth: [] + parameters: + - $ref: '#/components/parameters/ProjectIdParam' + responses: + '204': + description: Project deleted + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + parameters: + ProjectIdParam: + name: projectId + in: path + required: true + description: Project ID + schema: + type: string + format: uuid + + PageParam: + name: page + in: query + description: Page number + schema: + type: integer + minimum: 1 + default: 1 + + LimitParam: + name: limit + in: query + description: Items per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + + schemas: + Project: + type: object + required: [id, name, status, createdAt] + properties: + id: + type: string + format: uuid + example: '123e4567-e89b-12d3-a456-426614174000' + name: + type: string + minLength: 1 + maxLength: 100 + example: 'My Project' + description: + type: string + maxLength: 1000 + status: + type: string + enum: [active, archived, draft] + default: active + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + + CreateProjectInput: + type: object + required: [name] + properties: + name: + type: string + minLength: 1 + maxLength: 100 + description: + type: string + maxLength: 1000 + + UpdateProjectInput: + type: object + properties: + name: + type: string + minLength: 1 + maxLength: 100 + description: + type: string + maxLength: 1000 + status: + type: string + enum: [active, archived, draft] + + ProjectListResponse: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Project' + pagination: + $ref: '#/components/schemas/Pagination' + + Pagination: + type: object + properties: + page: + type: integer + limit: + type: integer + total: + type: integer + totalPages: + type: integer + + Error: + type: object + properties: + code: + type: string + message: + type: string + details: + type: object + + responses: + BadRequest: + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Unauthorized: + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + NotFound: + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + ValidationError: + description: Validation error + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - type: object + properties: + errors: + type: array + items: + type: object + properties: + field: + type: string + message: + type: string + + InternalError: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +``` + +--- + +## Type Generation from OpenAPI + +```typescript +// scripts/generate-types.ts +import { generateApi } from 'swagger-typescript-api'; +import path from 'path'; + +generateApi({ + name: 'api-client.ts', + output: path.resolve(__dirname, '../lib/api'), + input: path.resolve(__dirname, '../openapi/api.yaml'), + httpClientType: 'fetch', + generateClient: true, + generateRouteTypes: true, + generateResponses: true, + extractRequestParams: true, + extractRequestBody: true, + unwrapResponseData: true, + prettier: { + printWidth: 100, + singleQuote: true + } +}); +``` + +```typescript +// lib/api/client.ts - Generated API Client Usage +import { Api } from './api-client'; + +const api = new Api({ + baseUrl: process.env.NEXT_PUBLIC_API_URL, + securityWorker: async () => { + const token = await getAccessToken(); + return token ? { headers: { Authorization: `Bearer ${token}` } } : {}; + } +}); + +// Type-safe API calls +export async function getProjects(params?: { + page?: number; + limit?: number; + status?: 'active' | 'archived' | 'draft'; +}) { + const response = await api.projects.listProjects(params); + return response.data; +} + +export async function createProject(input: { + name: string; + description?: string; +}) { + const response = await api.projects.createProject(input); + return response.data; +} +``` + +--- + +## API Response Format + +```typescript +// lib/api/response.ts +import { NextResponse } from 'next/server'; + +interface ApiResponse<T> { + data?: T; + error?: { + code: string; + message: string; + details?: unknown; + }; + pagination?: { + page: number; + limit: number; + total: number; + totalPages: number; + }; +} + +export function success<T>( + data: T, + status: number = 200 +): NextResponse<ApiResponse<T>> { + return NextResponse.json({ data }, { status }); +} + +export function paginated<T>( + data: T[], + pagination: { page: number; limit: number; total: number } +): NextResponse<ApiResponse<T[]>> { + return NextResponse.json({ + data, + pagination: { + ...pagination, + totalPages: Math.ceil(pagination.total / pagination.limit) + } + }); +} + +export function created<T>(data: T): NextResponse<ApiResponse<T>> { + return success(data, 201); +} + +export function noContent(): NextResponse { + return new NextResponse(null, { status: 204 }); +} + +export function error( + code: string, + message: string, + status: number = 400, + details?: unknown +): NextResponse<ApiResponse<never>> { + return NextResponse.json( + { error: { code, message, details } }, + { status } + ); +} + +export function badRequest(message: string, details?: unknown) { + return error('BAD_REQUEST', message, 400, details); +} + +export function unauthorized(message: string = 'Unauthorized') { + return error('UNAUTHORIZED', message, 401); +} + +export function forbidden(message: string = 'Forbidden') { + return error('FORBIDDEN', message, 403); +} + +export function notFound(resource: string = 'Resource') { + return error('NOT_FOUND', `${resource} not found`, 404); +} + +export function validationError(errors: Array<{ field: string; message: string }>) { + return error('VALIDATION_ERROR', 'Validation failed', 422, { errors }); +} + +export function internalError(message: string = 'Internal server error') { + return error('INTERNAL_ERROR', message, 500); +} +``` + +--- + +## Interactive Documentation + +```tsx +// app/docs/api/page.tsx +import SwaggerUI from 'swagger-ui-react'; +import 'swagger-ui-react/swagger-ui.css'; + +export default function ApiDocsPage() { + return ( + <div className="min-h-screen"> + <SwaggerUI + url="/openapi/api.yaml" + docExpansion="list" + defaultModelsExpandDepth={-1} + displayRequestDuration={true} + filter={true} + showExtensions={true} + tryItOutEnabled={true} + /> + </div> + ); +} + +// Alternative: Custom docs with syntax highlighting +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; + +function CodeExample({ code, language }: { code: string; language: string }) { + return ( + <SyntaxHighlighter + language={language} + style={vscDarkPlus} + customStyle={{ borderRadius: '8px' }} + > + {code} + </SyntaxHighlighter> + ); +} +``` + +--- + +## API Design Checklist + +| Aspect | Best Practice | +|--------|--------------| +| **URLs** | Use nouns, plural, lowercase | +| **HTTP Methods** | Match CRUD operations | +| **Status Codes** | Use appropriate codes | +| **Errors** | Consistent format with codes | +| **Pagination** | Always paginate lists | +| **Versioning** | Use URL versioning (/v1/) | +| **Documentation** | OpenAPI specification | +| **Authentication** | Bearer tokens, API keys | + +--- + +## Fazit + +Gutes API Design erfordert: + +1. **Konsistenz**: Einheitliche Struktur und Namenskonventionen +2. **OpenAPI**: Maschinenlesbare Spezifikation +3. **Dokumentation**: Interaktiv und aktuell +4. **Type Safety**: Generierte Typen aus Spec + +Developer Experience beginnt mit gutem API Design. + +--- + +## Bildprompts + +1. "API documentation page with Swagger UI, interactive endpoints" +2. "REST API resource diagram, CRUD operations visualization" +3. "OpenAPI specification editor, YAML code with validation" + +--- + +## Quellen + +- [OpenAPI Specification](https://spec.openapis.org/oas/latest.html) +- [REST API Design Guide](https://restfulapi.net/) +- [Google API Design Guide](https://cloud.google.com/apis/design) +- [Microsoft REST API Guidelines](https://github.com/microsoft/api-guidelines) diff --git a/blog-posts/92-webhook-implementation.md b/blog-posts/92-webhook-implementation.md new file mode 100644 index 0000000..c83f9e5 --- /dev/null +++ b/blog-posts/92-webhook-implementation.md @@ -0,0 +1,655 @@ +# Webhook Implementation + +**Meta-Description:** Webhooks implementieren für Event-Driven Architecture. Signierung, Retry-Logic und Webhook Management für SaaS. + +**Keywords:** Webhooks, Event-Driven, Signature Verification, Retry Logic, Webhook Delivery, Integration, API Events + +--- + +## Einführung + +**Webhooks** ermöglichen Echtzeit-Benachrichtigungen bei Events. Statt Polling erhalten Clients Push-Notifications. Dieser Guide zeigt sichere, zuverlässige Webhook-Implementation mit Retry-Logic und Signaturverifikation. + +--- + +## Webhook Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ WEBHOOK DELIVERY SYSTEM │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Event Flow: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 1. Event occurs in application │ │ +│ │ ↓ │ │ +│ │ 2. Event published to queue │ │ +│ │ ↓ │ │ +│ │ 3. Webhook worker picks up event │ │ +│ │ ↓ │ │ +│ │ 4. Look up subscribed endpoints │ │ +│ │ ↓ │ │ +│ │ 5. Sign and send payload │ │ +│ │ ↓ │ │ +│ │ 6. Handle response / retry on failure │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Webhook Payload: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ { │ │ +│ │ "id": "evt_123", │ │ +│ │ "type": "project.created", │ │ +│ │ "timestamp": "2024-01-15T10:30:00Z", │ │ +│ │ "data": { │ │ +│ │ "id": "proj_456", │ │ +│ │ "name": "New Project" │ │ +│ │ } │ │ +│ │ } │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Security: │ +│ ├── HMAC-SHA256 signature in header │ +│ ├── Timestamp to prevent replay attacks │ +│ ├── HTTPS only │ +│ └── IP allowlisting (optional) │ +│ │ +│ Retry Strategy: │ +│ ├── Attempt 1: Immediate │ +│ ├── Attempt 2: After 1 minute │ +│ ├── Attempt 3: After 5 minutes │ +│ ├── Attempt 4: After 30 minutes │ +│ └── Attempt 5: After 2 hours │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Webhook Configuration + +```typescript +// lib/webhooks/types.ts +export interface WebhookEndpoint { + id: string; + tenantId: string; + url: string; + secret: string; + events: string[]; + active: boolean; + createdAt: Date; + metadata?: Record<string, string>; +} + +export interface WebhookEvent { + id: string; + type: string; + timestamp: Date; + data: unknown; + tenantId: string; +} + +export interface WebhookDelivery { + id: string; + endpointId: string; + eventId: string; + status: 'pending' | 'success' | 'failed'; + attempts: number; + lastAttemptAt?: Date; + nextAttemptAt?: Date; + response?: { + statusCode: number; + body?: string; + duration: number; + }; +} + +// Event types +export const WEBHOOK_EVENTS = { + // Project events + 'project.created': 'Project was created', + 'project.updated': 'Project was updated', + 'project.deleted': 'Project was deleted', + + // User events + 'user.created': 'User was created', + 'user.updated': 'User was updated', + 'user.deleted': 'User was deleted', + + // Subscription events + 'subscription.created': 'Subscription was created', + 'subscription.updated': 'Subscription was updated', + 'subscription.canceled': 'Subscription was canceled', + + // Invoice events + 'invoice.created': 'Invoice was created', + 'invoice.paid': 'Invoice was paid', + 'invoice.failed': 'Invoice payment failed' +} as const; + +export type WebhookEventType = keyof typeof WEBHOOK_EVENTS; +``` + +--- + +## Signature & Verification + +```typescript +// lib/webhooks/signature.ts +import { createHmac, timingSafeEqual } from 'crypto'; + +const SIGNATURE_HEADER = 'X-Webhook-Signature'; +const TIMESTAMP_HEADER = 'X-Webhook-Timestamp'; +const TOLERANCE_SECONDS = 300; // 5 minutes + +export function signWebhookPayload( + payload: string, + secret: string, + timestamp: number +): string { + const signedPayload = `${timestamp}.${payload}`; + const signature = createHmac('sha256', secret) + .update(signedPayload) + .digest('hex'); + + return `v1=${signature}`; +} + +export function verifyWebhookSignature( + payload: string, + signature: string, + secret: string, + timestamp: number +): boolean { + // Check timestamp tolerance + const now = Math.floor(Date.now() / 1000); + if (Math.abs(now - timestamp) > TOLERANCE_SECONDS) { + throw new Error('Webhook timestamp outside tolerance window'); + } + + // Extract signature version and value + const [version, receivedSig] = signature.split('='); + if (version !== 'v1') { + throw new Error('Unsupported signature version'); + } + + // Calculate expected signature + const signedPayload = `${timestamp}.${payload}`; + const expectedSig = createHmac('sha256', secret) + .update(signedPayload) + .digest('hex'); + + // Constant-time comparison + const receivedBuffer = Buffer.from(receivedSig, 'hex'); + const expectedBuffer = Buffer.from(expectedSig, 'hex'); + + if (receivedBuffer.length !== expectedBuffer.length) { + return false; + } + + return timingSafeEqual(receivedBuffer, expectedBuffer); +} + +// Generate webhook secret +export function generateWebhookSecret(): string { + return `whsec_${randomBytes(32).toString('hex')}`; +} +``` + +--- + +## Event Publishing + +```typescript +// lib/webhooks/publisher.ts +import { db } from '@/lib/db'; +import { redis } from '@/lib/redis'; +import { WebhookEvent, WebhookEventType } from './types'; +import { nanoid } from 'nanoid'; + +export async function publishWebhookEvent( + type: WebhookEventType, + tenantId: string, + data: unknown +): Promise<string> { + const event: WebhookEvent = { + id: `evt_${nanoid()}`, + type, + timestamp: new Date(), + data, + tenantId + }; + + // Store event + await db.webhookEvent.create({ + data: { + id: event.id, + type: event.type, + tenantId: event.tenantId, + data: event.data as any, + createdAt: event.timestamp + } + }); + + // Get subscribed endpoints + const endpoints = await db.webhookEndpoint.findMany({ + where: { + tenantId, + active: true, + events: { has: type } + } + }); + + // Create delivery records and queue + for (const endpoint of endpoints) { + const deliveryId = `del_${nanoid()}`; + + await db.webhookDelivery.create({ + data: { + id: deliveryId, + endpointId: endpoint.id, + eventId: event.id, + status: 'pending', + attempts: 0, + createdAt: new Date() + } + }); + + // Add to queue + await redis.lpush('webhook:deliveries', JSON.stringify({ + deliveryId, + endpointId: endpoint.id, + eventId: event.id + })); + } + + return event.id; +} + +// Helper to publish from anywhere in the app +export function webhookEvents(tenantId: string) { + return { + projectCreated: (project: any) => + publishWebhookEvent('project.created', tenantId, project), + projectUpdated: (project: any) => + publishWebhookEvent('project.updated', tenantId, project), + projectDeleted: (projectId: string) => + publishWebhookEvent('project.deleted', tenantId, { id: projectId }), + userCreated: (user: any) => + publishWebhookEvent('user.created', tenantId, user), + subscriptionCreated: (subscription: any) => + publishWebhookEvent('subscription.created', tenantId, subscription) + }; +} +``` + +--- + +## Delivery Worker + +```typescript +// lib/webhooks/worker.ts +import { db } from '@/lib/db'; +import { redis } from '@/lib/redis'; +import { signWebhookPayload } from './signature'; + +const RETRY_DELAYS = [0, 60, 300, 1800, 7200]; // seconds + +interface DeliveryJob { + deliveryId: string; + endpointId: string; + eventId: string; +} + +export async function processWebhookDeliveries(): Promise<void> { + while (true) { + // Get job from queue + const jobData = await redis.brpop('webhook:deliveries', 0); + if (!jobData) continue; + + const job: DeliveryJob = JSON.parse(jobData[1]); + await deliverWebhook(job); + } +} + +async function deliverWebhook(job: DeliveryJob): Promise<void> { + const [delivery, endpoint, event] = await Promise.all([ + db.webhookDelivery.findUnique({ where: { id: job.deliveryId } }), + db.webhookEndpoint.findUnique({ where: { id: job.endpointId } }), + db.webhookEvent.findUnique({ where: { id: job.eventId } }) + ]); + + if (!delivery || !endpoint || !event) { + console.error('Missing data for webhook delivery', job); + return; + } + + // Prepare payload + const payload = JSON.stringify({ + id: event.id, + type: event.type, + timestamp: event.createdAt.toISOString(), + data: event.data + }); + + const timestamp = Math.floor(Date.now() / 1000); + const signature = signWebhookPayload(payload, endpoint.secret, timestamp); + + // Attempt delivery + const startTime = Date.now(); + let response: Response; + let error: Error | null = null; + + try { + response = await fetch(endpoint.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Webhook-Signature': signature, + 'X-Webhook-Timestamp': timestamp.toString(), + 'X-Webhook-Event-Type': event.type, + 'X-Webhook-Event-ID': event.id, + 'User-Agent': 'MyApp-Webhooks/1.0' + }, + body: payload, + signal: AbortSignal.timeout(30000) // 30 second timeout + }); + } catch (e) { + error = e as Error; + } + + const duration = Date.now() - startTime; + const attempts = delivery.attempts + 1; + const success = response?.ok ?? false; + + // Update delivery record + await db.webhookDelivery.update({ + where: { id: delivery.id }, + data: { + status: success ? 'success' : (attempts >= 5 ? 'failed' : 'pending'), + attempts, + lastAttemptAt: new Date(), + nextAttemptAt: success ? null : getNextAttemptTime(attempts), + response: { + statusCode: response?.status ?? 0, + body: error?.message ?? (await response?.text().catch(() => '')), + duration + } + } + }); + + // Schedule retry if needed + if (!success && attempts < 5) { + const delay = RETRY_DELAYS[attempts] * 1000; + setTimeout(() => { + redis.lpush('webhook:deliveries', JSON.stringify(job)); + }, delay); + } + + // Log for monitoring + console.log('Webhook delivery', { + deliveryId: delivery.id, + success, + attempts, + duration, + statusCode: response?.status + }); +} + +function getNextAttemptTime(attempts: number): Date { + const delay = RETRY_DELAYS[Math.min(attempts, RETRY_DELAYS.length - 1)]; + return new Date(Date.now() + delay * 1000); +} +``` + +--- + +## Webhook Management API + +```typescript +// app/api/webhooks/route.ts +import { NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { db } from '@/lib/db'; +import { generateWebhookSecret } from '@/lib/webhooks/signature'; +import { WEBHOOK_EVENTS } from '@/lib/webhooks/types'; +import { z } from 'zod'; + +const createWebhookSchema = z.object({ + url: z.string().url(), + events: z.array(z.enum(Object.keys(WEBHOOK_EVENTS) as [string, ...string[]])).min(1) +}); + +export async function GET(request: Request) { + const session = await getServerSession(); + if (!session?.user?.tenantId) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const endpoints = await db.webhookEndpoint.findMany({ + where: { tenantId: session.user.tenantId }, + select: { + id: true, + url: true, + events: true, + active: true, + createdAt: true, + _count: { + select: { + deliveries: { + where: { status: 'failed' } + } + } + } + } + }); + + return NextResponse.json({ data: endpoints }); +} + +export async function POST(request: Request) { + const session = await getServerSession(); + if (!session?.user?.tenantId) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const body = await request.json(); + const parsed = createWebhookSchema.safeParse(body); + + if (!parsed.success) { + return NextResponse.json( + { error: 'Validation failed', details: parsed.error.errors }, + { status: 422 } + ); + } + + const secret = generateWebhookSecret(); + + const endpoint = await db.webhookEndpoint.create({ + data: { + tenantId: session.user.tenantId, + url: parsed.data.url, + events: parsed.data.events, + secret, + active: true + } + }); + + // Return secret only on creation + return NextResponse.json({ + data: { + id: endpoint.id, + url: endpoint.url, + events: endpoint.events, + secret // Show once! + } + }, { status: 201 }); +} + +// app/api/webhooks/[id]/route.ts +export async function DELETE( + request: Request, + { params }: { params: { id: string } } +) { + const session = await getServerSession(); + if (!session?.user?.tenantId) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + await db.webhookEndpoint.deleteMany({ + where: { + id: params.id, + tenantId: session.user.tenantId + } + }); + + return new NextResponse(null, { status: 204 }); +} + +// app/api/webhooks/[id]/test/route.ts +export async function POST( + request: Request, + { params }: { params: { id: string } } +) { + const session = await getServerSession(); + if (!session?.user?.tenantId) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const endpoint = await db.webhookEndpoint.findFirst({ + where: { + id: params.id, + tenantId: session.user.tenantId + } + }); + + if (!endpoint) { + return NextResponse.json({ error: 'Not found' }, { status: 404 }); + } + + // Send test event + const testEvent = { + id: 'evt_test', + type: 'webhook.test', + timestamp: new Date().toISOString(), + data: { message: 'This is a test webhook' } + }; + + const payload = JSON.stringify(testEvent); + const timestamp = Math.floor(Date.now() / 1000); + const signature = signWebhookPayload(payload, endpoint.secret, timestamp); + + try { + const response = await fetch(endpoint.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Webhook-Signature': signature, + 'X-Webhook-Timestamp': timestamp.toString() + }, + body: payload, + signal: AbortSignal.timeout(10000) + }); + + return NextResponse.json({ + success: response.ok, + statusCode: response.status + }); + } catch (error) { + return NextResponse.json({ + success: false, + error: (error as Error).message + }); + } +} +``` + +--- + +## Receiving Webhooks + +```typescript +// Example: Receiving webhooks in your app +// app/api/webhooks/external/route.ts +import { NextResponse } from 'next/server'; +import { verifyWebhookSignature } from '@/lib/webhooks/signature'; + +export async function POST(request: Request) { + const body = await request.text(); + const signature = request.headers.get('X-Webhook-Signature'); + const timestamp = parseInt(request.headers.get('X-Webhook-Timestamp') || '0'); + + if (!signature || !timestamp) { + return NextResponse.json({ error: 'Missing signature' }, { status: 400 }); + } + + const secret = process.env.WEBHOOK_SECRET!; + + try { + const isValid = verifyWebhookSignature(body, signature, secret, timestamp); + if (!isValid) { + return NextResponse.json({ error: 'Invalid signature' }, { status: 401 }); + } + } catch (error) { + return NextResponse.json({ error: (error as Error).message }, { status: 401 }); + } + + const event = JSON.parse(body); + + // Process event + switch (event.type) { + case 'project.created': + await handleProjectCreated(event.data); + break; + case 'user.created': + await handleUserCreated(event.data); + break; + default: + console.log('Unhandled event type:', event.type); + } + + return NextResponse.json({ received: true }); +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **Signing** | HMAC-SHA256 with timestamp | +| **Timeouts** | 30 seconds max | +| **Retries** | Exponential backoff, max 5 | +| **Idempotency** | Include event ID for dedup | +| **Logging** | Log all attempts | +| **Testing** | Provide test endpoint | + +--- + +## Fazit + +Webhooks erfordern: + +1. **Security**: Signierung und Verifikation +2. **Reliability**: Retry-Logic mit Backoff +3. **Observability**: Logging und Monitoring +4. **Developer UX**: Einfache Konfiguration + +Webhooks sind die Basis für Event-Driven Integrations. + +--- + +## Bildprompts + +1. "Webhook delivery flow diagram, event to endpoint" +2. "Webhook management dashboard, endpoints and delivery status" +3. "Retry strategy visualization, exponential backoff" + +--- + +## Quellen + +- [Stripe Webhooks Best Practices](https://stripe.com/docs/webhooks/best-practices) +- [GitHub Webhooks](https://docs.github.com/en/webhooks) +- [Webhook.site for Testing](https://webhook.site/) +- [Standard Webhooks Specification](https://www.standardwebhooks.com/) diff --git a/blog-posts/93-rate-limiting-throttling.md b/blog-posts/93-rate-limiting-throttling.md new file mode 100644 index 0000000..ffbe03f --- /dev/null +++ b/blog-posts/93-rate-limiting-throttling.md @@ -0,0 +1,615 @@ +# Rate Limiting & Throttling + +**Meta-Description:** API Rate Limiting implementieren. Token Bucket, Sliding Window und Redis-basierte Rate Limiter für sichere APIs. + +**Keywords:** Rate Limiting, API Throttling, Token Bucket, Sliding Window, Redis, API Security, DDoS Protection + +--- + +## Einführung + +**Rate Limiting** schützt APIs vor Überlastung und Missbrauch. Von **Token Bucket** bis **Sliding Window** – verschiedene Algorithmen haben unterschiedliche Stärken. Dieser Guide zeigt Implementation mit Redis und Best Practices. + +--- + +## Rate Limiting Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ RATE LIMITING ALGORITHMS │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 1. FIXED WINDOW: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ |----Window 1----|----Window 2----| │ │ +│ │ |▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓|░░░░░░░░░░░░░░░| │ │ +│ │ 100 requests/min Reset at boundary │ │ +│ │ ✓ Simple │ │ +│ │ ✗ Burst at window boundary │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 2. SLIDING WINDOW LOG: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Store timestamps of all requests │ │ +│ │ [t1, t2, t3, ... tn] │ │ +│ │ Count requests in last N seconds │ │ +│ │ ✓ Accurate │ │ +│ │ ✗ Memory intensive │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 3. SLIDING WINDOW COUNTER: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Previous window: 80 requests │ │ +│ │ Current window: 20 requests │ │ +│ │ Position: 25% into current window │ │ +│ │ Weighted: 80 * 0.75 + 20 = 80 requests │ │ +│ │ ✓ Memory efficient │ │ +│ │ ✓ Smooth rate limiting │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 4. TOKEN BUCKET: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Bucket capacity: 100 tokens │ │ +│ │ Refill rate: 10 tokens/second │ │ +│ │ Request consumes 1 token │ │ +│ │ ✓ Allows controlled bursts │ │ +│ │ ✓ Smooth average rate │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 5. LEAKY BUCKET: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Requests enter bucket (queue) │ │ +│ │ Processed at fixed rate (leak) │ │ +│ │ Overflow rejected │ │ +│ │ ✓ Smooth output rate │ │ +│ │ ✗ Doesn't allow bursts │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Redis Rate Limiter + +```typescript +// lib/rate-limit/redis.ts +import { Redis } from 'ioredis'; + +const redis = new Redis(process.env.REDIS_URL!); + +interface RateLimitConfig { + identifier: string; + limit: number; + window: number; // seconds +} + +interface RateLimitResult { + allowed: boolean; + remaining: number; + reset: number; + retryAfter?: number; +} + +// Sliding Window Counter +export async function slidingWindowRateLimit( + config: RateLimitConfig +): Promise<RateLimitResult> { + const { identifier, limit, window } = config; + const now = Date.now(); + const windowStart = now - window * 1000; + const currentWindow = Math.floor(now / (window * 1000)); + const previousWindow = currentWindow - 1; + + const keys = { + current: `ratelimit:${identifier}:${currentWindow}`, + previous: `ratelimit:${identifier}:${previousWindow}` + }; + + // Get counts from both windows + const [currentCount, previousCount] = await redis.mget(keys.current, keys.previous); + const current = parseInt(currentCount || '0'); + const previous = parseInt(previousCount || '0'); + + // Calculate weighted count + const windowPosition = (now % (window * 1000)) / (window * 1000); + const weightedCount = Math.floor(previous * (1 - windowPosition) + current); + + if (weightedCount >= limit) { + const reset = (currentWindow + 1) * window * 1000; + return { + allowed: false, + remaining: 0, + reset, + retryAfter: Math.ceil((reset - now) / 1000) + }; + } + + // Increment current window + await redis + .multi() + .incr(keys.current) + .expire(keys.current, window * 2) + .exec(); + + return { + allowed: true, + remaining: limit - weightedCount - 1, + reset: (currentWindow + 1) * window * 1000 + }; +} + +// Token Bucket +export async function tokenBucketRateLimit( + identifier: string, + bucketSize: number, + refillRate: number // tokens per second +): Promise<RateLimitResult> { + const key = `ratelimit:bucket:${identifier}`; + const now = Date.now(); + + // Lua script for atomic operation + const luaScript = ` + local key = KEYS[1] + local bucket_size = tonumber(ARGV[1]) + local refill_rate = tonumber(ARGV[2]) + local now = tonumber(ARGV[3]) + + local bucket = redis.call('HMGET', key, 'tokens', 'last_refill') + local tokens = tonumber(bucket[1]) or bucket_size + local last_refill = tonumber(bucket[2]) or now + + -- Calculate tokens to add + local elapsed = (now - last_refill) / 1000 + local tokens_to_add = elapsed * refill_rate + tokens = math.min(bucket_size, tokens + tokens_to_add) + + -- Try to consume a token + if tokens >= 1 then + tokens = tokens - 1 + redis.call('HMSET', key, 'tokens', tokens, 'last_refill', now) + redis.call('EXPIRE', key, bucket_size / refill_rate * 2) + return {1, tokens, 0} + else + local wait_time = (1 - tokens) / refill_rate + return {0, tokens, wait_time} + end + `; + + const result = await redis.eval( + luaScript, + 1, + key, + bucketSize, + refillRate, + now + ) as [number, number, number]; + + const [allowed, remaining, waitTime] = result; + + return { + allowed: allowed === 1, + remaining: Math.floor(remaining), + reset: now + Math.ceil((bucketSize - remaining) / refillRate) * 1000, + retryAfter: waitTime > 0 ? Math.ceil(waitTime) : undefined + }; +} +``` + +--- + +## Rate Limit Middleware + +```typescript +// lib/rate-limit/middleware.ts +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { slidingWindowRateLimit } from './redis'; + +interface RateLimitTier { + identifier: (req: NextRequest) => string; + limit: number; + window: number; +} + +const rateLimitTiers: Record<string, RateLimitTier> = { + // Public API - by IP + public: { + identifier: (req) => req.ip || req.headers.get('x-forwarded-for') || 'unknown', + limit: 100, + window: 60 // 100 requests per minute + }, + // Authenticated API - by user + authenticated: { + identifier: (req) => req.headers.get('x-user-id') || 'unknown', + limit: 1000, + window: 60 // 1000 requests per minute + }, + // Premium API - by API key + premium: { + identifier: (req) => req.headers.get('x-api-key') || 'unknown', + limit: 10000, + window: 60 // 10000 requests per minute + } +}; + +export async function rateLimitMiddleware( + request: NextRequest, + tier: keyof typeof rateLimitTiers = 'public' +): Promise<NextResponse | null> { + const config = rateLimitTiers[tier]; + const identifier = config.identifier(request); + + const result = await slidingWindowRateLimit({ + identifier: `${tier}:${identifier}`, + limit: config.limit, + window: config.window + }); + + // Set rate limit headers + const headers = new Headers(); + headers.set('X-RateLimit-Limit', config.limit.toString()); + headers.set('X-RateLimit-Remaining', result.remaining.toString()); + headers.set('X-RateLimit-Reset', result.reset.toString()); + + if (!result.allowed) { + headers.set('Retry-After', result.retryAfter!.toString()); + + return NextResponse.json( + { + error: { + code: 'RATE_LIMIT_EXCEEDED', + message: 'Too many requests', + retryAfter: result.retryAfter + } + }, + { + status: 429, + headers + } + ); + } + + return null; // Continue to handler +} + +// Usage in API route +// app/api/[...]/route.ts +export async function GET(request: NextRequest) { + const rateLimitResponse = await rateLimitMiddleware(request, 'authenticated'); + if (rateLimitResponse) return rateLimitResponse; + + // Handle request... +} +``` + +--- + +## Plan-Based Rate Limits + +```typescript +// lib/rate-limit/plans.ts +import { db } from '@/lib/db'; +import { slidingWindowRateLimit } from './redis'; + +interface PlanLimits { + requestsPerMinute: number; + requestsPerDay: number; + burstSize: number; +} + +const planLimits: Record<string, PlanLimits> = { + free: { + requestsPerMinute: 60, + requestsPerDay: 1000, + burstSize: 10 + }, + pro: { + requestsPerMinute: 600, + requestsPerDay: 50000, + burstSize: 100 + }, + enterprise: { + requestsPerMinute: 6000, + requestsPerDay: 1000000, + burstSize: 1000 + } +}; + +export async function checkPlanRateLimit( + userId: string, + endpoint: string +): Promise<{ allowed: boolean; error?: string; headers: Record<string, string> }> { + // Get user's plan + const user = await db.user.findUnique({ + where: { id: userId }, + select: { plan: true } + }); + + const plan = user?.plan || 'free'; + const limits = planLimits[plan]; + + // Check per-minute limit + const minuteResult = await slidingWindowRateLimit({ + identifier: `user:${userId}:minute`, + limit: limits.requestsPerMinute, + window: 60 + }); + + // Check daily limit + const dailyResult = await slidingWindowRateLimit({ + identifier: `user:${userId}:day`, + limit: limits.requestsPerDay, + window: 86400 + }); + + const headers = { + 'X-RateLimit-Limit-Minute': limits.requestsPerMinute.toString(), + 'X-RateLimit-Remaining-Minute': minuteResult.remaining.toString(), + 'X-RateLimit-Limit-Day': limits.requestsPerDay.toString(), + 'X-RateLimit-Remaining-Day': dailyResult.remaining.toString() + }; + + if (!minuteResult.allowed) { + return { + allowed: false, + error: 'Rate limit exceeded (per minute)', + headers: { + ...headers, + 'Retry-After': minuteResult.retryAfter!.toString() + } + }; + } + + if (!dailyResult.allowed) { + return { + allowed: false, + error: 'Daily rate limit exceeded. Please upgrade your plan.', + headers: { + ...headers, + 'Retry-After': dailyResult.retryAfter!.toString() + } + }; + } + + return { allowed: true, headers }; +} +``` + +--- + +## Endpoint-Specific Limits + +```typescript +// lib/rate-limit/endpoint.ts +interface EndpointRateLimit { + path: string | RegExp; + method?: string; + limit: number; + window: number; + keyGenerator?: (req: Request) => string; +} + +const endpointLimits: EndpointRateLimit[] = [ + // Strict limit for auth endpoints + { + path: '/api/auth/login', + method: 'POST', + limit: 5, + window: 300, // 5 attempts per 5 minutes + keyGenerator: (req) => req.headers.get('x-forwarded-for') || 'unknown' + }, + // Password reset + { + path: '/api/auth/reset-password', + method: 'POST', + limit: 3, + window: 3600 // 3 per hour + }, + // File uploads + { + path: /^\/api\/files\/upload/, + limit: 10, + window: 60 // 10 per minute + }, + // Search (expensive) + { + path: '/api/search', + limit: 30, + window: 60 + }, + // AI/LLM endpoints + { + path: /^\/api\/ai\//, + limit: 10, + window: 60 + } +]; + +export async function getEndpointRateLimit( + path: string, + method: string +): Promise<EndpointRateLimit | null> { + return endpointLimits.find(limit => { + const pathMatch = typeof limit.path === 'string' + ? path === limit.path + : limit.path.test(path); + + const methodMatch = !limit.method || limit.method === method; + + return pathMatch && methodMatch; + }) || null; +} +``` + +--- + +## Distributed Rate Limiting + +```typescript +// lib/rate-limit/distributed.ts +import { Redis } from 'ioredis'; + +// Cluster-aware rate limiting +export class DistributedRateLimiter { + private redis: Redis; + private localCounter: Map<string, number> = new Map(); + private syncInterval: number = 1000; // 1 second + + constructor(redisUrl: string) { + this.redis = new Redis(redisUrl); + this.startSync(); + } + + private startSync() { + setInterval(async () => { + // Sync local counters to Redis + const pipeline = this.redis.pipeline(); + + this.localCounter.forEach((count, key) => { + if (count > 0) { + pipeline.incrby(key, count); + } + }); + + await pipeline.exec(); + this.localCounter.clear(); + }, this.syncInterval); + } + + async checkLimit( + key: string, + limit: number, + window: number + ): Promise<boolean> { + // Quick local check (approximate) + const localCount = this.localCounter.get(key) || 0; + + // Get current count from Redis + const redisCount = parseInt(await this.redis.get(key) || '0'); + const totalCount = redisCount + localCount; + + if (totalCount >= limit) { + return false; + } + + // Increment locally + this.localCounter.set(key, localCount + 1); + + // Set expiry if new key + if (redisCount === 0) { + await this.redis.setex(key, window, '0'); + } + + return true; + } +} +``` + +--- + +## Rate Limit Dashboard + +```tsx +// components/RateLimitStatus.tsx +'use client'; + +import { useState, useEffect } from 'react'; + +interface RateLimitStatus { + endpoint: string; + limit: number; + used: number; + remaining: number; + resetsAt: Date; +} + +export function RateLimitStatus({ apiKey }: { apiKey: string }) { + const [status, setStatus] = useState<RateLimitStatus[]>([]); + + useEffect(() => { + fetch(`/api/rate-limits/status?apiKey=${apiKey}`) + .then(res => res.json()) + .then(setStatus); + }, [apiKey]); + + return ( + <div className="bg-white rounded-lg shadow p-6"> + <h2 className="text-lg font-semibold mb-4">Rate Limit Status</h2> + + <div className="space-y-4"> + {status.map((item) => ( + <div key={item.endpoint} className="border-b pb-4"> + <div className="flex justify-between mb-2"> + <span className="font-medium">{item.endpoint}</span> + <span className="text-gray-500"> + {item.remaining} / {item.limit} remaining + </span> + </div> + + <div className="w-full bg-gray-200 rounded-full h-2"> + <div + className={`h-2 rounded-full ${ + item.remaining < item.limit * 0.1 + ? 'bg-red-500' + : item.remaining < item.limit * 0.3 + ? 'bg-yellow-500' + : 'bg-green-500' + }`} + style={{ width: `${(item.remaining / item.limit) * 100}%` }} + /> + </div> + + <p className="text-sm text-gray-500 mt-1"> + Resets: {new Date(item.resetsAt).toLocaleTimeString()} + </p> + </div> + ))} + </div> + </div> + ); +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **Algorithm** | Sliding window for accuracy | +| **Storage** | Redis for distributed systems | +| **Headers** | Always return limit headers | +| **Granularity** | Per-user + per-IP | +| **Grace Period** | Soft limits before hard block | +| **Documentation** | Clear rate limit docs | + +--- + +## Fazit + +Rate Limiting erfordert: + +1. **Algorithmus**: Sliding Window oder Token Bucket +2. **Skalierung**: Redis für verteilte Systeme +3. **Granularität**: User, IP, Endpoint-spezifisch +4. **Transparenz**: Headers und Dokumentation + +Rate Limiting schützt APIs und verbessert Fairness. + +--- + +## Bildprompts + +1. "Rate limiting algorithm comparison diagram, token bucket and sliding window" +2. "API rate limit dashboard, usage gauges and quotas" +3. "Distributed rate limiting architecture, Redis cluster" + +--- + +## Quellen + +- [Stripe Rate Limiting](https://stripe.com/blog/rate-limiters) +- [Cloudflare Rate Limiting](https://blog.cloudflare.com/counting-things-a-lot-of-different-things/) +- [Redis Rate Limiting Patterns](https://redis.io/commands/incr#pattern-rate-limiter) +- [Kong Rate Limiting](https://docs.konghq.com/hub/kong-inc/rate-limiting/) diff --git a/blog-posts/94-developer-experience-dx.md b/blog-posts/94-developer-experience-dx.md new file mode 100644 index 0000000..9452a0d --- /dev/null +++ b/blog-posts/94-developer-experience-dx.md @@ -0,0 +1,643 @@ +# Developer Experience (DX) + +**Meta-Description:** Developer Experience für APIs optimieren. SDKs, CLI Tools, Interactive Docs und Developer Portals für bessere Adoption. + +**Keywords:** Developer Experience, DX, SDK, CLI, API Documentation, Developer Portal, Onboarding, API Adoption + +--- + +## Einführung + +**Developer Experience (DX)** entscheidet über API-Adoption. Gute SDKs, intuitive Dokumentation und hilfreiche Error Messages machen den Unterschied. Dieser Guide zeigt, wie man APIs developer-friendly gestaltet. + +--- + +## DX Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ DEVELOPER EXPERIENCE PILLARS │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 1. DOCUMENTATION: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ├── Quick Start Guide (< 5 min to first call) │ │ +│ │ ├── Interactive API Reference (Try it!) │ │ +│ │ ├── Code Examples (Copy-paste ready) │ │ +│ │ ├── Tutorials & Guides │ │ +│ │ └── Changelog & Migration Guides │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 2. SDKs & TOOLS: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ├── Official SDKs (JS, Python, Go, etc.) │ │ +│ │ ├── CLI Tool │ │ +│ │ ├── Postman/Insomnia Collection │ │ +│ │ ├── VS Code Extension │ │ +│ │ └── Testing Tools │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 3. ERROR HANDLING: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ├── Clear error messages │ │ +│ │ ├── Error codes & documentation │ │ +│ │ ├── Suggested fixes │ │ +│ │ └── Stack traces in dev mode │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 4. DEVELOPER PORTAL: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ├── API Key Management │ │ +│ │ ├── Usage Dashboard │ │ +│ │ ├── Webhook Management │ │ +│ │ └── Support & Community │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Time to First API Call: │ +│ ├── Excellent: < 5 minutes │ +│ ├── Good: 5-15 minutes │ +│ ├── Needs Work: 15-30 minutes │ +│ └── Poor: > 30 minutes │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## TypeScript SDK + +```typescript +// sdk/src/client.ts +import { z } from 'zod'; + +interface ClientConfig { + apiKey: string; + baseUrl?: string; + timeout?: number; + retries?: number; + onError?: (error: APIError) => void; +} + +interface RequestOptions { + method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; + path: string; + body?: unknown; + query?: Record<string, string | number | boolean | undefined>; + headers?: Record<string, string>; +} + +export class APIError extends Error { + constructor( + public code: string, + message: string, + public statusCode: number, + public details?: unknown + ) { + super(message); + this.name = 'APIError'; + } +} + +export class MyAPIClient { + private config: Required<ClientConfig>; + + // Resources + public projects: ProjectsResource; + public users: UsersResource; + public webhooks: WebhooksResource; + + constructor(config: ClientConfig) { + this.config = { + apiKey: config.apiKey, + baseUrl: config.baseUrl || 'https://api.example.com/v1', + timeout: config.timeout || 30000, + retries: config.retries || 3, + onError: config.onError || (() => {}) + }; + + // Initialize resources + this.projects = new ProjectsResource(this); + this.users = new UsersResource(this); + this.webhooks = new WebhooksResource(this); + } + + async request<T>(options: RequestOptions): Promise<T> { + const url = new URL(options.path, this.config.baseUrl); + + // Add query parameters + if (options.query) { + Object.entries(options.query).forEach(([key, value]) => { + if (value !== undefined) { + url.searchParams.set(key, String(value)); + } + }); + } + + const headers: Record<string, string> = { + 'Authorization': `Bearer ${this.config.apiKey}`, + 'Content-Type': 'application/json', + 'User-Agent': 'myapi-sdk-js/1.0.0', + ...options.headers + }; + + let lastError: Error | null = null; + + for (let attempt = 0; attempt < this.config.retries; attempt++) { + try { + const response = await fetch(url.toString(), { + method: options.method, + headers, + body: options.body ? JSON.stringify(options.body) : undefined, + signal: AbortSignal.timeout(this.config.timeout) + }); + + const data = await response.json(); + + if (!response.ok) { + const error = new APIError( + data.error?.code || 'UNKNOWN_ERROR', + data.error?.message || 'An error occurred', + response.status, + data.error?.details + ); + this.config.onError(error); + throw error; + } + + return data.data as T; + } catch (error) { + lastError = error as Error; + + // Don't retry client errors + if (error instanceof APIError && error.statusCode < 500) { + throw error; + } + + // Wait before retry with exponential backoff + if (attempt < this.config.retries - 1) { + await new Promise(resolve => + setTimeout(resolve, Math.pow(2, attempt) * 1000) + ); + } + } + } + + throw lastError; + } +} + +// Resource classes +class ProjectsResource { + constructor(private client: MyAPIClient) {} + + async list(params?: { + page?: number; + limit?: number; + status?: 'active' | 'archived'; + }): Promise<{ data: Project[]; pagination: Pagination }> { + return this.client.request({ + method: 'GET', + path: '/projects', + query: params + }); + } + + async create(data: CreateProjectInput): Promise<Project> { + return this.client.request({ + method: 'POST', + path: '/projects', + body: data + }); + } + + async get(id: string): Promise<Project> { + return this.client.request({ + method: 'GET', + path: `/projects/${id}` + }); + } + + async update(id: string, data: UpdateProjectInput): Promise<Project> { + return this.client.request({ + method: 'PATCH', + path: `/projects/${id}`, + body: data + }); + } + + async delete(id: string): Promise<void> { + return this.client.request({ + method: 'DELETE', + path: `/projects/${id}` + }); + } +} + +// Usage example +const client = new MyAPIClient({ + apiKey: 'sk_live_...', + onError: (error) => { + console.error(`API Error: ${error.code} - ${error.message}`); + } +}); + +const projects = await client.projects.list({ status: 'active' }); +const newProject = await client.projects.create({ name: 'My Project' }); +``` + +--- + +## CLI Tool + +```typescript +// cli/src/index.ts +#!/usr/bin/env node +import { Command } from 'commander'; +import chalk from 'chalk'; +import ora from 'ora'; +import inquirer from 'inquirer'; +import { MyAPIClient } from '@myapi/sdk'; + +const program = new Command(); + +// Config management +const config = { + getApiKey: () => process.env.MYAPI_KEY || loadConfig().apiKey, + setApiKey: (key: string) => saveConfig({ apiKey: key }) +}; + +program + .name('myapi') + .description('CLI tool for MyAPI') + .version('1.0.0'); + +// Auth command +program + .command('login') + .description('Configure API key') + .action(async () => { + const { apiKey } = await inquirer.prompt([ + { + type: 'password', + name: 'apiKey', + message: 'Enter your API key:', + validate: (input) => input.length > 0 || 'API key is required' + } + ]); + + config.setApiKey(apiKey); + console.log(chalk.green('✓ API key saved')); + }); + +// Projects commands +const projects = program.command('projects').description('Manage projects'); + +projects + .command('list') + .description('List all projects') + .option('-s, --status <status>', 'Filter by status') + .option('--json', 'Output as JSON') + .action(async (options) => { + const spinner = ora('Fetching projects...').start(); + + try { + const client = new MyAPIClient({ apiKey: config.getApiKey() }); + const result = await client.projects.list({ + status: options.status + }); + + spinner.stop(); + + if (options.json) { + console.log(JSON.stringify(result.data, null, 2)); + } else { + console.log(chalk.bold('\nProjects:\n')); + result.data.forEach(project => { + const status = project.status === 'active' + ? chalk.green('●') + : chalk.gray('○'); + console.log(` ${status} ${project.name} (${project.id})`); + }); + console.log(`\n Total: ${result.pagination.total}`); + } + } catch (error) { + spinner.fail('Failed to fetch projects'); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +projects + .command('create') + .description('Create a new project') + .option('-n, --name <name>', 'Project name') + .option('-d, --description <description>', 'Project description') + .action(async (options) => { + let { name, description } = options; + + // Interactive mode if not provided + if (!name) { + const answers = await inquirer.prompt([ + { + type: 'input', + name: 'name', + message: 'Project name:', + validate: (input) => input.length > 0 || 'Name is required' + }, + { + type: 'input', + name: 'description', + message: 'Description (optional):' + } + ]); + name = answers.name; + description = answers.description; + } + + const spinner = ora('Creating project...').start(); + + try { + const client = new MyAPIClient({ apiKey: config.getApiKey() }); + const project = await client.projects.create({ name, description }); + + spinner.succeed('Project created'); + console.log(chalk.gray(` ID: ${project.id}`)); + } catch (error) { + spinner.fail('Failed to create project'); + console.error(chalk.red(error.message)); + process.exit(1); + } + }); + +// API request command for debugging +program + .command('api <method> <path>') + .description('Make a raw API request') + .option('-d, --data <json>', 'Request body (JSON)') + .option('-v, --verbose', 'Show full response') + .action(async (method, path, options) => { + const client = new MyAPIClient({ apiKey: config.getApiKey() }); + + try { + const response = await client.request({ + method: method.toUpperCase(), + path, + body: options.data ? JSON.parse(options.data) : undefined + }); + + console.log(JSON.stringify(response, null, 2)); + } catch (error) { + console.error(chalk.red(error.message)); + if (options.verbose && error.details) { + console.error(JSON.stringify(error.details, null, 2)); + } + process.exit(1); + } + }); + +program.parse(); +``` + +--- + +## Error Messages + +```typescript +// lib/errors/messages.ts +interface ErrorDefinition { + code: string; + message: string; + httpStatus: number; + suggestion?: string; + docsUrl?: string; +} + +export const errors: Record<string, ErrorDefinition> = { + AUTHENTICATION_FAILED: { + code: 'AUTHENTICATION_FAILED', + message: 'Invalid API key provided', + httpStatus: 401, + suggestion: 'Check that your API key is correct and has not expired. You can find your API keys in the dashboard.', + docsUrl: '/docs/authentication' + }, + INSUFFICIENT_PERMISSIONS: { + code: 'INSUFFICIENT_PERMISSIONS', + message: 'Your API key does not have permission for this action', + httpStatus: 403, + suggestion: 'Ensure your API key has the required scopes. You may need to generate a new key with additional permissions.', + docsUrl: '/docs/api-keys#scopes' + }, + RESOURCE_NOT_FOUND: { + code: 'RESOURCE_NOT_FOUND', + message: 'The requested resource was not found', + httpStatus: 404, + suggestion: 'Verify the resource ID is correct and that you have access to this resource.' + }, + VALIDATION_ERROR: { + code: 'VALIDATION_ERROR', + message: 'The request body contains invalid data', + httpStatus: 422, + suggestion: 'Check the error details for specific field errors.', + docsUrl: '/docs/error-handling#validation' + }, + RATE_LIMIT_EXCEEDED: { + code: 'RATE_LIMIT_EXCEEDED', + message: 'Too many requests', + httpStatus: 429, + suggestion: 'Wait before retrying. Check the Retry-After header. Consider upgrading your plan for higher limits.', + docsUrl: '/docs/rate-limits' + }, + INTERNAL_ERROR: { + code: 'INTERNAL_ERROR', + message: 'An internal error occurred', + httpStatus: 500, + suggestion: 'This is likely a temporary issue. Please try again. If the problem persists, contact support with the request ID.' + } +}; + +export function createErrorResponse( + errorCode: string, + details?: unknown, + requestId?: string +) { + const errorDef = errors[errorCode] || errors.INTERNAL_ERROR; + + return { + error: { + code: errorDef.code, + message: errorDef.message, + suggestion: errorDef.suggestion, + docsUrl: errorDef.docsUrl ? `https://example.com${errorDef.docsUrl}` : undefined, + details, + requestId + } + }; +} + +// Example error response: +// { +// "error": { +// "code": "VALIDATION_ERROR", +// "message": "The request body contains invalid data", +// "suggestion": "Check the error details for specific field errors.", +// "docsUrl": "https://example.com/docs/error-handling#validation", +// "details": { +// "errors": [ +// { "field": "name", "message": "Name is required" }, +// { "field": "email", "message": "Invalid email format" } +// ] +// }, +// "requestId": "req_abc123" +// } +// } +``` + +--- + +## Interactive Code Examples + +```tsx +// components/CodeExample.tsx +'use client'; + +import { useState } from 'react'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; + +interface CodeExampleProps { + examples: { + language: string; + label: string; + code: string; + }[]; + endpoint: string; + method: string; +} + +export function CodeExample({ examples, endpoint, method }: CodeExampleProps) { + const [selectedLang, setSelectedLang] = useState(examples[0].language); + const [response, setResponse] = useState<string | null>(null); + const [loading, setLoading] = useState(false); + + const currentExample = examples.find(e => e.language === selectedLang)!; + + const tryIt = async () => { + setLoading(true); + try { + const res = await fetch(`/api/docs/try-it`, { + method: 'POST', + body: JSON.stringify({ endpoint, method }), + headers: { 'Content-Type': 'application/json' } + }); + const data = await res.json(); + setResponse(JSON.stringify(data, null, 2)); + } catch (error) { + setResponse(`Error: ${error.message}`); + } + setLoading(false); + }; + + return ( + <div className="rounded-lg overflow-hidden border"> + {/* Language tabs */} + <div className="flex border-b bg-gray-100"> + {examples.map((example) => ( + <button + key={example.language} + onClick={() => setSelectedLang(example.language)} + className={`px-4 py-2 text-sm ${ + selectedLang === example.language + ? 'bg-white border-b-2 border-blue-500' + : 'text-gray-600' + }`} + > + {example.label} + </button> + ))} + </div> + + {/* Code */} + <div className="relative"> + <SyntaxHighlighter + language={selectedLang} + style={vscDarkPlus} + customStyle={{ margin: 0, borderRadius: 0 }} + > + {currentExample.code} + </SyntaxHighlighter> + + <button + onClick={() => navigator.clipboard.writeText(currentExample.code)} + className="absolute top-2 right-2 p-2 bg-gray-700 rounded hover:bg-gray-600" + title="Copy code" + > + 📋 + </button> + </div> + + {/* Try it button */} + <div className="p-4 bg-gray-50 border-t"> + <button + onClick={tryIt} + disabled={loading} + className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50" + > + {loading ? 'Running...' : 'Try it →'} + </button> + + {response && ( + <div className="mt-4"> + <h4 className="text-sm font-medium mb-2">Response:</h4> + <pre className="bg-gray-900 text-gray-100 p-4 rounded text-sm overflow-auto"> + {response} + </pre> + </div> + )} + </div> + </div> + ); +} +``` + +--- + +## DX Checklist + +| Area | Metric | Target | +|------|--------|--------| +| **Documentation** | Time to first call | < 5 minutes | +| **SDKs** | Languages covered | Top 5+ | +| **Errors** | Message clarity | 100% actionable | +| **Examples** | Code snippets | Every endpoint | +| **Support** | Response time | < 24 hours | +| **Status** | Uptime page | 99.9%+ | + +--- + +## Fazit + +Exzellente Developer Experience erfordert: + +1. **Documentation**: Klar, aktuell, interaktiv +2. **SDKs**: Type-safe, gut dokumentiert +3. **Errors**: Hilfreich, nicht kryptisch +4. **Tools**: CLI, Postman, Testing + +DX ist Investition in Adoption und Retention. + +--- + +## Bildprompts + +1. "Developer portal dashboard, API keys and usage stats" +2. "Interactive API documentation, code examples with try-it button" +3. "CLI tool in terminal, colorful output with progress indicators" + +--- + +## Quellen + +- [Stripe API Design](https://stripe.com/docs/api) +- [Twilio Developer Experience](https://www.twilio.com/docs) +- [GitHub REST API](https://docs.github.com/en/rest) +- [Developer Experience Principles](https://developerexperience.io/) diff --git a/blog-posts/95-api-versioning.md b/blog-posts/95-api-versioning.md new file mode 100644 index 0000000..43bfafc --- /dev/null +++ b/blog-posts/95-api-versioning.md @@ -0,0 +1,563 @@ +# API Versioning Strategies + +**Meta-Description:** API Versioning implementieren. URL Versioning, Header Versioning und Breaking Changes ohne Downtime managen. + +**Keywords:** API Versioning, Semantic Versioning, Breaking Changes, Backward Compatibility, API Evolution, Deprecation + +--- + +## Einführung + +**API Versioning** ermöglicht Evolution ohne Breaking Changes für bestehende Clients. Von **URL-based** bis **Header-based** – verschiedene Strategien haben unterschiedliche Trade-offs. Dieser Guide zeigt Best Practices für nachhaltige API-Entwicklung. + +--- + +## Versioning Strategies + +``` +┌─────────────────────────────────────────────────────────────┐ +│ API VERSIONING STRATEGIES │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 1. URL PATH VERSIONING (Recommended): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ /api/v1/users │ │ +│ │ /api/v2/users │ │ +│ │ │ │ +│ │ ✓ Explicit, easy to understand │ │ +│ │ ✓ Easy to route and cache │ │ +│ │ ✓ Can run versions in parallel │ │ +│ │ ✗ URL changes for version bumps │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 2. HEADER VERSIONING: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Accept: application/vnd.myapi.v2+json │ │ +│ │ X-API-Version: 2 │ │ +│ │ │ │ +│ │ ✓ Clean URLs │ │ +│ │ ✓ Follows HTTP semantics │ │ +│ │ ✗ Harder to test (need headers) │ │ +│ │ ✗ Not visible in URL │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 3. QUERY PARAMETER: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ /api/users?version=2 │ │ +│ │ │ │ +│ │ ✓ Easy to implement │ │ +│ │ ✗ Optional parameter = default version issues │ │ +│ │ ✗ Caching complications │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 4. DATE-BASED VERSIONING (Stripe-style): │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Stripe-Version: 2024-01-15 │ │ +│ │ │ │ +│ │ ✓ Fine-grained control │ │ +│ │ ✓ Clear timeline │ │ +│ │ ✗ Many versions to maintain │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Semantic Versioning (for SDKs): │ +│ ├── MAJOR.MINOR.PATCH (1.2.3) │ +│ ├── MAJOR: Breaking changes │ +│ ├── MINOR: New features, backward compatible │ +│ └── PATCH: Bug fixes │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## URL Versioning Implementation + +```typescript +// app/api/v1/users/route.ts +import { NextResponse } from 'next/server'; + +export async function GET() { + // V1 response format + const users = await db.user.findMany({ + select: { + id: true, + name: true, + email: true + } + }); + + return NextResponse.json({ + users, // Array at root level (v1 format) + count: users.length + }); +} + +// app/api/v2/users/route.ts +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const page = parseInt(searchParams.get('page') || '1'); + const limit = parseInt(searchParams.get('limit') || '20'); + + const [users, total] = await Promise.all([ + db.user.findMany({ + skip: (page - 1) * limit, + take: limit, + select: { + id: true, + name: true, + email: true, + createdAt: true, // New field in v2 + avatar: true // New field in v2 + } + }), + db.user.count() + ]); + + // V2 response format (standardized) + return NextResponse.json({ + data: users, + pagination: { + page, + limit, + total, + totalPages: Math.ceil(total / limit) + } + }); +} +``` + +--- + +## Version Router + +```typescript +// lib/api/version-router.ts +import { NextRequest, NextResponse } from 'next/server'; + +interface VersionConfig { + current: string; + supported: string[]; + deprecated: string[]; + sunset: Record<string, Date>; +} + +const versionConfig: VersionConfig = { + current: 'v3', + supported: ['v2', 'v3'], + deprecated: ['v1'], + sunset: { + v1: new Date('2024-06-01') + } +}; + +type VersionHandler = (req: NextRequest) => Promise<NextResponse>; + +interface VersionedHandlers { + v1?: VersionHandler; + v2?: VersionHandler; + v3?: VersionHandler; +} + +export function versionedRoute(handlers: VersionedHandlers) { + return async (req: NextRequest): Promise<NextResponse> => { + // Extract version from URL + const version = extractVersion(req.nextUrl.pathname); + + // Check if version is supported + if (!versionConfig.supported.includes(version) && + !versionConfig.deprecated.includes(version)) { + return NextResponse.json( + { error: `API version ${version} is not supported` }, + { status: 400 } + ); + } + + // Get handler for version + const handler = handlers[version as keyof VersionedHandlers]; + if (!handler) { + return NextResponse.json( + { error: `No handler for version ${version}` }, + { status: 501 } + ); + } + + // Execute handler + const response = await handler(req); + + // Add deprecation headers if needed + if (versionConfig.deprecated.includes(version)) { + const sunsetDate = versionConfig.sunset[version]; + response.headers.set('Deprecation', 'true'); + response.headers.set('Sunset', sunsetDate.toUTCString()); + response.headers.set( + 'Link', + `</api/${versionConfig.current}>; rel="successor-version"` + ); + } + + // Add version header + response.headers.set('X-API-Version', version); + + return response; + }; +} + +function extractVersion(pathname: string): string { + const match = pathname.match(/\/api\/(v\d+)\//); + return match ? match[1] : versionConfig.current; +} + +// Usage +export const GET = versionedRoute({ + v1: async (req) => { + // V1 implementation + return NextResponse.json({ format: 'v1' }); + }, + v2: async (req) => { + // V2 implementation + return NextResponse.json({ data: [], format: 'v2' }); + }, + v3: async (req) => { + // V3 implementation + return NextResponse.json({ data: [], meta: {}, format: 'v3' }); + } +}); +``` + +--- + +## Breaking Changes Management + +```typescript +// lib/api/breaking-changes.ts +interface BreakingChange { + id: string; + version: string; + date: Date; + description: string; + migration: string; + affectedEndpoints: string[]; +} + +export const breakingChanges: BreakingChange[] = [ + { + id: 'bc-001', + version: 'v2', + date: new Date('2024-01-15'), + description: 'Response format changed to use data wrapper', + migration: ` + // Before (v1): + const users = response.users; + + // After (v2): + const users = response.data; + `, + affectedEndpoints: ['/users', '/projects', '/tasks'] + }, + { + id: 'bc-002', + version: 'v2', + date: new Date('2024-01-15'), + description: 'Pagination parameters renamed', + migration: ` + // Before (v1): + GET /users?offset=0&count=20 + + // After (v2): + GET /users?page=1&limit=20 + `, + affectedEndpoints: ['/users', '/projects'] + }, + { + id: 'bc-003', + version: 'v3', + date: new Date('2024-06-01'), + description: 'Date format changed to ISO 8601', + migration: ` + // Before (v2): + { "created": "2024-01-15 10:30:00" } + + // After (v3): + { "createdAt": "2024-01-15T10:30:00.000Z" } + `, + affectedEndpoints: ['*'] + } +]; + +// Helper to check compatibility +export function getBreakingChanges( + fromVersion: string, + toVersion: string +): BreakingChange[] { + const fromNum = parseInt(fromVersion.replace('v', '')); + const toNum = parseInt(toVersion.replace('v', '')); + + return breakingChanges.filter(change => { + const changeNum = parseInt(change.version.replace('v', '')); + return changeNum > fromNum && changeNum <= toNum; + }); +} +``` + +--- + +## Deprecation Workflow + +```typescript +// lib/api/deprecation.ts +import { db } from '@/lib/db'; + +interface DeprecationNotice { + endpoint: string; + version: string; + deprecatedAt: Date; + sunsetAt: Date; + replacement?: string; + reason: string; +} + +const deprecationNotices: DeprecationNotice[] = [ + { + endpoint: '/api/v1/users', + version: 'v1', + deprecatedAt: new Date('2024-01-01'), + sunsetAt: new Date('2024-06-01'), + replacement: '/api/v2/users', + reason: 'Migrating to standardized response format' + } +]; + +// Track deprecated endpoint usage +export async function trackDeprecatedUsage( + apiKeyId: string, + endpoint: string, + version: string +): Promise<void> { + await db.deprecatedUsage.upsert({ + where: { + apiKeyId_endpoint_version: { apiKeyId, endpoint, version } + }, + update: { + lastUsedAt: new Date(), + usageCount: { increment: 1 } + }, + create: { + apiKeyId, + endpoint, + version, + usageCount: 1, + firstUsedAt: new Date(), + lastUsedAt: new Date() + } + }); +} + +// Send deprecation warnings to users +export async function sendDeprecationWarnings(): Promise<void> { + const activeDeprecations = deprecationNotices.filter( + d => d.sunsetAt > new Date() + ); + + for (const notice of activeDeprecations) { + // Find users still using deprecated endpoints + const usages = await db.deprecatedUsage.findMany({ + where: { + endpoint: notice.endpoint, + version: notice.version, + lastUsedAt: { + gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Last 7 days + } + }, + include: { + apiKey: { include: { user: true } } + } + }); + + for (const usage of usages) { + await sendEmail(usage.apiKey.user.email, 'api-deprecation-warning', { + endpoint: notice.endpoint, + currentVersion: notice.version, + replacement: notice.replacement, + sunsetDate: notice.sunsetAt, + usageCount: usage.usageCount + }); + } + } +} + +// Middleware to add deprecation headers +export function deprecationMiddleware( + endpoint: string, + version: string +): Record<string, string> { + const notice = deprecationNotices.find( + d => d.endpoint === endpoint && d.version === version + ); + + if (!notice) return {}; + + return { + 'Deprecation': notice.deprecatedAt.toUTCString(), + 'Sunset': notice.sunsetAt.toUTCString(), + 'Link': notice.replacement + ? `<${notice.replacement}>; rel="successor-version"` + : '' + }; +} +``` + +--- + +## Migration Guide Generator + +```typescript +// lib/api/migration-guide.ts +export function generateMigrationGuide( + fromVersion: string, + toVersion: string +): string { + const changes = getBreakingChanges(fromVersion, toVersion); + + let guide = `# Migration Guide: ${fromVersion} to ${toVersion}\n\n`; + guide += `This guide covers all breaking changes when upgrading from API ${fromVersion} to ${toVersion}.\n\n`; + + guide += `## Summary\n\n`; + guide += `- ${changes.length} breaking changes\n`; + guide += `- Affected endpoints: ${[...new Set(changes.flatMap(c => c.affectedEndpoints))].join(', ')}\n\n`; + + guide += `## Changes\n\n`; + + changes.forEach((change, index) => { + guide += `### ${index + 1}. ${change.description}\n\n`; + guide += `**Version:** ${change.version}\n`; + guide += `**Date:** ${change.date.toISOString().split('T')[0]}\n`; + guide += `**Affected Endpoints:** ${change.affectedEndpoints.join(', ')}\n\n`; + guide += `#### Migration\n\n`; + guide += '```typescript\n'; + guide += change.migration; + guide += '\n```\n\n'; + }); + + guide += `## Need Help?\n\n`; + guide += `If you encounter issues during migration, please:\n`; + guide += `- Check our [API documentation](/docs/api/${toVersion})\n`; + guide += `- Contact support at api-support@example.com\n`; + + return guide; +} +``` + +--- + +## Client SDK Versioning + +```typescript +// sdk/src/versioned-client.ts +type APIVersion = 'v1' | 'v2' | 'v3'; + +interface VersionedClientConfig { + apiKey: string; + version?: APIVersion; + baseUrl?: string; +} + +export class VersionedClient { + private version: APIVersion; + private baseUrl: string; + + constructor(config: VersionedClientConfig) { + this.version = config.version || 'v3'; // Default to latest + this.baseUrl = config.baseUrl || `https://api.example.com/${this.version}`; + } + + // Response transformers for backward compatibility + private transformResponse<T>(data: unknown): T { + if (this.version === 'v1') { + // Transform v3 response to v1 format + return this.transformToV1(data) as T; + } + if (this.version === 'v2') { + return this.transformToV2(data) as T; + } + return data as T; + } + + private transformToV1(data: any): any { + // Remove data wrapper, convert dates, etc. + if (data.data) { + return { + ...data.data, + count: data.pagination?.total + }; + } + return data; + } + + private transformToV2(data: any): any { + // Keep data wrapper but adjust format + return data; + } + + async getUsers(): Promise<User[]> { + const response = await this.request('/users'); + return this.transformResponse(response); + } +} + +// Factory for version-specific clients +export function createClient(version: APIVersion, apiKey: string) { + switch (version) { + case 'v1': + console.warn('API v1 is deprecated. Please upgrade to v3.'); + return new VersionedClient({ apiKey, version: 'v1' }); + case 'v2': + return new VersionedClient({ apiKey, version: 'v2' }); + case 'v3': + default: + return new VersionedClient({ apiKey, version: 'v3' }); + } +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **Format** | URL versioning (most common) | +| **Increment** | Only for breaking changes | +| **Support** | Min 12-18 months per version | +| **Communication** | 6+ months deprecation notice | +| **Headers** | Deprecation & Sunset headers | +| **Docs** | Version-specific documentation | + +--- + +## Fazit + +API Versioning erfordert: + +1. **Strategie**: URL-based ist am klarsten +2. **Kommunikation**: Frühe Deprecation Notices +3. **Support**: Alte Versionen lang genug pflegen +4. **Migration**: Klare Guides und Tools + +Gutes Versioning ermöglicht Evolution ohne Client-Breakage. + +--- + +## Bildprompts + +1. "API versioning timeline, version lifecycle visualization" +2. "Breaking changes migration flowchart, before and after" +3. "Deprecation warning email template, sunset date highlighted" + +--- + +## Quellen + +- [Stripe API Versioning](https://stripe.com/docs/api/versioning) +- [Microsoft REST API Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md) +- [API Versioning Best Practices](https://www.freecodecamp.org/news/how-to-version-a-rest-api/) +- [Semantic Versioning](https://semver.org/) diff --git a/blog-posts/96-marketplace-platform.md b/blog-posts/96-marketplace-platform.md new file mode 100644 index 0000000..78fd557 --- /dev/null +++ b/blog-posts/96-marketplace-platform.md @@ -0,0 +1,665 @@ +# Marketplace Platform Development + +**Meta-Description:** Multi-Vendor Marketplace bauen. Seller Management, Product Listings, Escrow Payments und Commission Handling. + +**Keywords:** Marketplace, Multi-Vendor, E-Commerce, Seller Management, Commission, Escrow, Platform Business + +--- + +## Einführung + +**Marketplace Platforms** verbinden Käufer und Verkäufer. Von **Seller Onboarding** bis **Commission Splitting** – die technischen Herausforderungen sind komplex. Dieser Guide zeigt die Architektur und Implementation eines Multi-Vendor Marketplaces. + +--- + +## Marketplace Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ MARKETPLACE PLATFORM ARCHITECTURE │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Stakeholders: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Platform (You) │ │ +│ │ ├── Commission on sales │ │ +│ │ ├── Subscription fees │ │ +│ │ └── Value-added services │ │ +│ │ │ │ +│ │ Sellers │ │ +│ │ ├── List products │ │ +│ │ ├── Receive payouts │ │ +│ │ └── Manage inventory │ │ +│ │ │ │ +│ │ Buyers │ │ +│ │ ├── Browse & purchase │ │ +│ │ ├── Reviews & ratings │ │ +│ │ └── Dispute resolution │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Payment Flow: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Buyer pays $100 │ │ +│ │ ↓ │ │ +│ │ Platform receives $100 (Stripe Connect) │ │ +│ │ ↓ │ │ +│ │ Platform takes 15% commission ($15) │ │ +│ │ ↓ │ │ +│ │ Seller receives $85 (after payout delay) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Core Features: │ +│ ├── Seller Dashboard │ +│ ├── Product Catalog │ +│ ├── Shopping Cart │ +│ ├── Checkout (Split Payments) │ +│ ├── Order Management │ +│ ├── Reviews & Ratings │ +│ ├── Dispute Resolution │ +│ └── Analytics & Reporting │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Database Schema + +```typescript +// prisma/schema.prisma +model Seller { + id String @id @default(cuid()) + userId String @unique + user User @relation(fields: [userId], references: [id]) + + // Business Info + businessName String + businessType String // individual, company + taxId String? + description String? + logo String? + + // Stripe Connect + stripeAccountId String? @unique + stripeOnboarded Boolean @default(false) + payoutEnabled Boolean @default(false) + + // Settings + commissionRate Float @default(0.15) // 15% + payoutSchedule String @default("daily") // daily, weekly, monthly + + // Status + status SellerStatus @default(PENDING) + verifiedAt DateTime? + + // Relations + products Product[] + orders OrderItem[] + payouts Payout[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +enum SellerStatus { + PENDING + ACTIVE + SUSPENDED + BANNED +} + +model Product { + id String @id @default(cuid()) + sellerId String + seller Seller @relation(fields: [sellerId], references: [id]) + + // Product Info + name String + slug String + description String + price Int // in cents + compareAtPrice Int? + currency String @default("EUR") + + // Inventory + sku String? + inventory Int @default(0) + trackInventory Boolean @default(true) + + // Categorization + categoryId String? + category Category? @relation(fields: [categoryId], references: [id]) + tags String[] + + // Media + images ProductImage[] + + // Status + status ProductStatus @default(DRAFT) + publishedAt DateTime? + + // Stats + viewCount Int @default(0) + salesCount Int @default(0) + rating Float? + reviewCount Int @default(0) + + // Relations + variants ProductVariant[] + orderItems OrderItem[] + reviews Review[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([sellerId, slug]) + @@index([categoryId]) + @@index([status]) +} + +model Order { + id String @id @default(cuid()) + orderNumber String @unique + + // Buyer + userId String + user User @relation(fields: [userId], references: [id]) + + // Totals + subtotal Int // Sum of items + platformFee Int // Platform commission + shippingTotal Int + taxTotal Int + total Int + + // Payment + stripePaymentId String? + paymentStatus PaymentStatus @default(PENDING) + paidAt DateTime? + + // Shipping + shippingAddress Json + shippingMethod String? + + // Status + status OrderStatus @default(PENDING) + + // Items (can be from multiple sellers) + items OrderItem[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model OrderItem { + id String @id @default(cuid()) + orderId String + order Order @relation(fields: [orderId], references: [id]) + + // Product + productId String + product Product @relation(fields: [productId], references: [id]) + variantId String? + + // Seller + sellerId String + seller Seller @relation(fields: [sellerId], references: [id]) + + // Pricing + price Int + quantity Int + subtotal Int // price * quantity + commission Int // Platform commission + sellerPayout Int // subtotal - commission + + // Status (per-seller fulfillment) + status OrderItemStatus @default(PENDING) + shippedAt DateTime? + trackingNumber String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Payout { + id String @id @default(cuid()) + sellerId String + seller Seller @relation(fields: [sellerId], references: [id]) + + amount Int + currency String @default("EUR") + status PayoutStatus @default(PENDING) + + stripePayoutId String? + paidAt DateTime? + failedAt DateTime? + failureReason String? + + // Payout period + periodStart DateTime + periodEnd DateTime + + createdAt DateTime @default(now()) +} +``` + +--- + +## Seller Onboarding + +```typescript +// lib/marketplace/seller-onboarding.ts +import { stripe } from '@/lib/stripe'; +import { db } from '@/lib/db'; + +export async function createSellerAccount( + userId: string, + data: { + businessName: string; + businessType: 'individual' | 'company'; + country: string; + email: string; + } +): Promise<string> { + // Create Stripe Connect account + const account = await stripe.accounts.create({ + type: 'express', + country: data.country, + email: data.email, + business_type: data.businessType, + business_profile: { + name: data.businessName + }, + capabilities: { + card_payments: { requested: true }, + transfers: { requested: true } + } + }); + + // Create seller record + await db.seller.create({ + data: { + userId, + businessName: data.businessName, + businessType: data.businessType, + stripeAccountId: account.id, + status: 'PENDING' + } + }); + + // Generate onboarding link + const accountLink = await stripe.accountLinks.create({ + account: account.id, + refresh_url: `${process.env.NEXT_PUBLIC_URL}/seller/onboarding/refresh`, + return_url: `${process.env.NEXT_PUBLIC_URL}/seller/onboarding/complete`, + type: 'account_onboarding' + }); + + return accountLink.url; +} + +export async function completeSellerOnboarding( + stripeAccountId: string +): Promise<void> { + // Check account status + const account = await stripe.accounts.retrieve(stripeAccountId); + + if (account.charges_enabled && account.payouts_enabled) { + await db.seller.update({ + where: { stripeAccountId }, + data: { + stripeOnboarded: true, + payoutEnabled: true, + status: 'ACTIVE', + verifiedAt: new Date() + } + }); + } +} + +// Webhook handler for account updates +export async function handleAccountUpdated( + account: Stripe.Account +): Promise<void> { + await db.seller.update({ + where: { stripeAccountId: account.id }, + data: { + payoutEnabled: account.payouts_enabled || false, + stripeOnboarded: account.details_submitted || false + } + }); +} +``` + +--- + +## Split Payments + +```typescript +// lib/marketplace/checkout.ts +import { stripe } from '@/lib/stripe'; +import { db } from '@/lib/db'; +import { nanoid } from 'nanoid'; + +interface CartItem { + productId: string; + quantity: number; + variantId?: string; +} + +export async function createMarketplaceCheckout( + userId: string, + items: CartItem[], + shippingAddress: any +): Promise<string> { + // Group items by seller + const itemsBySeller = await groupItemsBySeller(items); + + // Calculate totals + const { subtotal, platformFee, lineItems, transfers } = + await calculateOrderTotals(itemsBySeller); + + // Create order + const order = await db.order.create({ + data: { + orderNumber: `ORD-${nanoid(10).toUpperCase()}`, + userId, + subtotal, + platformFee, + shippingTotal: 0, // Calculate based on sellers + taxTotal: 0, + total: subtotal, + shippingAddress, + status: 'PENDING', + items: { + create: lineItems.map(item => ({ + productId: item.productId, + sellerId: item.sellerId, + variantId: item.variantId, + price: item.price, + quantity: item.quantity, + subtotal: item.subtotal, + commission: item.commission, + sellerPayout: item.sellerPayout, + status: 'PENDING' + })) + } + } + }); + + // Create Stripe Checkout with transfers + const session = await stripe.checkout.sessions.create({ + mode: 'payment', + customer_email: (await db.user.findUnique({ where: { id: userId } }))?.email, + line_items: lineItems.map(item => ({ + price_data: { + currency: 'eur', + unit_amount: item.price, + product_data: { + name: item.productName, + images: item.images + } + }, + quantity: item.quantity + })), + payment_intent_data: { + // Split payment to sellers + transfer_group: order.id, + metadata: { + orderId: order.id + } + }, + success_url: `${process.env.NEXT_PUBLIC_URL}/orders/${order.id}/success`, + cancel_url: `${process.env.NEXT_PUBLIC_URL}/cart`, + metadata: { + orderId: order.id + } + }); + + return session.url!; +} + +async function calculateOrderTotals(itemsBySeller: Map<string, CartItem[]>) { + const lineItems: any[] = []; + let subtotal = 0; + let platformFee = 0; + const transfers: any[] = []; + + for (const [sellerId, items] of itemsBySeller) { + const seller = await db.seller.findUnique({ + where: { id: sellerId } + }); + + for (const item of items) { + const product = await db.product.findUnique({ + where: { id: item.productId } + }); + + if (!product) continue; + + const itemSubtotal = product.price * item.quantity; + const commission = Math.round(itemSubtotal * (seller?.commissionRate || 0.15)); + const sellerPayout = itemSubtotal - commission; + + subtotal += itemSubtotal; + platformFee += commission; + + lineItems.push({ + productId: product.id, + productName: product.name, + sellerId, + variantId: item.variantId, + price: product.price, + quantity: item.quantity, + subtotal: itemSubtotal, + commission, + sellerPayout, + images: [] // Add product images + }); + + // Prepare transfer for seller + transfers.push({ + sellerId, + stripeAccountId: seller?.stripeAccountId, + amount: sellerPayout + }); + } + } + + return { subtotal, platformFee, lineItems, transfers }; +} + +// After successful payment, create transfers +export async function createSellerTransfers(orderId: string): Promise<void> { + const order = await db.order.findUnique({ + where: { id: orderId }, + include: { + items: { + include: { seller: true } + } + } + }); + + if (!order) return; + + // Group payouts by seller + const payoutsBySeller = new Map<string, number>(); + + for (const item of order.items) { + const current = payoutsBySeller.get(item.sellerId) || 0; + payoutsBySeller.set(item.sellerId, current + item.sellerPayout); + } + + // Create transfers + for (const [sellerId, amount] of payoutsBySeller) { + const seller = order.items.find(i => i.sellerId === sellerId)?.seller; + + if (!seller?.stripeAccountId) continue; + + await stripe.transfers.create({ + amount, + currency: 'eur', + destination: seller.stripeAccountId, + transfer_group: orderId, + metadata: { + orderId, + sellerId + } + }); + } + + // Update order status + await db.order.update({ + where: { id: orderId }, + data: { + paymentStatus: 'PAID', + paidAt: new Date() + } + }); +} +``` + +--- + +## Seller Dashboard + +```tsx +// app/seller/dashboard/page.tsx +import { getServerSession } from 'next-auth'; +import { db } from '@/lib/db'; + +export default async function SellerDashboard() { + const session = await getServerSession(); + const seller = await db.seller.findUnique({ + where: { userId: session!.user.id } + }); + + const [stats, recentOrders, topProducts] = await Promise.all([ + getSellerStats(seller!.id), + getRecentOrders(seller!.id), + getTopProducts(seller!.id) + ]); + + return ( + <div className="p-6 space-y-6"> + <h1 className="text-2xl font-bold">Seller Dashboard</h1> + + {/* Stats Cards */} + <div className="grid grid-cols-4 gap-4"> + <StatCard + title="Revenue (30d)" + value={`€${(stats.revenue / 100).toFixed(2)}`} + change={stats.revenueChange} + /> + <StatCard + title="Orders" + value={stats.orderCount} + change={stats.orderChange} + /> + <StatCard + title="Products" + value={stats.productCount} + /> + <StatCard + title="Pending Payout" + value={`€${(stats.pendingPayout / 100).toFixed(2)}`} + /> + </div> + + <div className="grid grid-cols-2 gap-6"> + {/* Recent Orders */} + <div className="bg-white rounded-lg shadow p-6"> + <h2 className="text-lg font-semibold mb-4">Recent Orders</h2> + <OrderList orders={recentOrders} /> + </div> + + {/* Top Products */} + <div className="bg-white rounded-lg shadow p-6"> + <h2 className="text-lg font-semibold mb-4">Top Products</h2> + <ProductList products={topProducts} /> + </div> + </div> + </div> + ); +} + +async function getSellerStats(sellerId: string) { + const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); + + const [revenue, orders, products, pendingPayout] = await Promise.all([ + db.orderItem.aggregate({ + where: { + sellerId, + order: { paidAt: { gte: thirtyDaysAgo } } + }, + _sum: { sellerPayout: true } + }), + db.orderItem.count({ + where: { + sellerId, + createdAt: { gte: thirtyDaysAgo } + } + }), + db.product.count({ where: { sellerId } }), + db.orderItem.aggregate({ + where: { + sellerId, + order: { paymentStatus: 'PAID' }, + // Not yet paid out + }, + _sum: { sellerPayout: true } + }) + ]); + + return { + revenue: revenue._sum.sellerPayout || 0, + revenueChange: 0, // Calculate vs previous period + orderCount: orders, + orderChange: 0, + productCount: products, + pendingPayout: pendingPayout._sum.sellerPayout || 0 + }; +} +``` + +--- + +## Platform Economics + +| Revenue Stream | Example | +|---------------|---------| +| **Commission** | 10-20% per sale | +| **Listing Fee** | $0.20 per listing | +| **Subscription** | $29/mo for premium | +| **Promoted Listings** | $0.10 per click | +| **Transaction Fee** | 2.9% + $0.30 | + +--- + +## Fazit + +Marketplace Platform Development erfordert: + +1. **Multi-Party Payments**: Stripe Connect für Split Payments +2. **Seller Management**: Onboarding und Verification +3. **Trust & Safety**: Reviews, Disputes, Moderation +4. **Scalability**: Handling vieler Verkäufer und Produkte + +Marketplaces sind komplex, aber hochprofitabel. + +--- + +## Bildprompts + +1. "Marketplace platform architecture diagram, buyers sellers and platform" +2. "Seller dashboard interface, revenue charts and order list" +3. "Payment flow visualization, split payment to multiple sellers" + +--- + +## Quellen + +- [Stripe Connect](https://stripe.com/docs/connect) +- [Marketplace Business Models](https://a16z.com/marketplace-100/) +- [Sharetribe Marketplace Guide](https://www.sharetribe.com/academy/) +- [Building Marketplaces](https://www.nfx.com/post/marketplace-handbook) diff --git a/blog-posts/97-payment-integration-providers.md b/blog-posts/97-payment-integration-providers.md new file mode 100644 index 0000000..48c5485 --- /dev/null +++ b/blog-posts/97-payment-integration-providers.md @@ -0,0 +1,703 @@ +# Payment Integration (Multiple Providers) + +**Meta-Description:** Payment Provider Integration. Stripe, PayPal, Klarna und lokale Zahlungsmethoden für globale E-Commerce Anwendungen. + +**Keywords:** Payment Integration, Stripe, PayPal, Klarna, Payment Gateway, Checkout, E-Commerce, Payment Methods + +--- + +## Einführung + +**Globale E-Commerce** erfordert multiple Payment Provider. Kunden erwarten ihre bevorzugte Zahlungsmethode – von **Kreditkarte** über **PayPal** bis **Klarna**. Dieser Guide zeigt die Integration verschiedener Provider in einer einheitlichen Architektur. + +--- + +## Payment Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ PAYMENT PROVIDER ABSTRACTION │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Unified Interface: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ PaymentService │ │ +│ │ ├── createPayment(order, method) │ │ +│ │ ├── capturePayment(paymentId) │ │ +│ │ ├── refundPayment(paymentId, amount) │ │ +│ │ └── getPaymentStatus(paymentId) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ ↓ │ +│ Provider Adapters: │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ Stripe │ │ PayPal │ │ Klarna │ │ Local │ │ +│ │ Adapter │ │ Adapter │ │ Adapter │ │ Adapter │ │ +│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ +│ ↓ ↓ ↓ ↓ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ Stripe │ │ PayPal │ │ Klarna │ │ SEPA/ │ │ +│ │ API │ │ API │ │ API │ │ iDEAL/etc│ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +│ │ +│ Payment Methods by Region: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Germany: Klarna, SEPA, Giropay, Credit Card │ │ +│ │ Netherlands: iDEAL, Credit Card, PayPal │ │ +│ │ USA: Credit Card, PayPal, Apple Pay, Google Pay │ │ +│ │ UK: Credit Card, PayPal, Klarna │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Payment Service Interface + +```typescript +// lib/payments/types.ts +export interface PaymentMethod { + id: string; + type: PaymentMethodType; + name: string; + icon: string; + countries: string[]; + currencies: string[]; + minAmount?: number; + maxAmount?: number; +} + +export type PaymentMethodType = + | 'card' + | 'paypal' + | 'klarna' + | 'sepa_debit' + | 'ideal' + | 'giropay' + | 'sofort' + | 'bancontact' + | 'eps' + | 'apple_pay' + | 'google_pay'; + +export interface CreatePaymentInput { + orderId: string; + amount: number; + currency: string; + method: PaymentMethodType; + customer: { + email: string; + name?: string; + phone?: string; + }; + billing?: Address; + shipping?: Address; + metadata?: Record<string, string>; + returnUrl: string; + cancelUrl?: string; +} + +export interface PaymentResult { + id: string; + provider: string; + status: PaymentStatus; + redirectUrl?: string; + clientSecret?: string; + error?: string; +} + +export type PaymentStatus = + | 'pending' + | 'processing' + | 'requires_action' + | 'succeeded' + | 'failed' + | 'canceled' + | 'refunded'; + +export interface PaymentProvider { + name: string; + supportedMethods: PaymentMethodType[]; + createPayment(input: CreatePaymentInput): Promise<PaymentResult>; + capturePayment(paymentId: string): Promise<PaymentResult>; + refundPayment(paymentId: string, amount?: number): Promise<PaymentResult>; + getPaymentStatus(paymentId: string): Promise<PaymentStatus>; + handleWebhook(payload: unknown, signature: string): Promise<void>; +} +``` + +--- + +## Stripe Adapter + +```typescript +// lib/payments/providers/stripe.ts +import Stripe from 'stripe'; +import { PaymentProvider, CreatePaymentInput, PaymentResult, PaymentStatus } from '../types'; + +const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { + apiVersion: '2024-06-20' +}); + +export class StripeProvider implements PaymentProvider { + name = 'stripe'; + + supportedMethods: PaymentMethodType[] = [ + 'card', 'sepa_debit', 'ideal', 'giropay', + 'sofort', 'bancontact', 'eps', 'klarna' + ]; + + async createPayment(input: CreatePaymentInput): Promise<PaymentResult> { + try { + // Map method to Stripe payment method type + const paymentMethodTypes = this.mapPaymentMethod(input.method); + + const session = await stripe.checkout.sessions.create({ + mode: 'payment', + payment_method_types: paymentMethodTypes, + customer_email: input.customer.email, + line_items: [ + { + price_data: { + currency: input.currency.toLowerCase(), + unit_amount: input.amount, + product_data: { + name: `Order ${input.orderId}` + } + }, + quantity: 1 + } + ], + success_url: input.returnUrl, + cancel_url: input.cancelUrl || input.returnUrl, + metadata: { + orderId: input.orderId, + ...input.metadata + } + }); + + return { + id: session.payment_intent as string, + provider: 'stripe', + status: 'pending', + redirectUrl: session.url! + }; + } catch (error) { + return { + id: '', + provider: 'stripe', + status: 'failed', + error: (error as Error).message + }; + } + } + + async capturePayment(paymentId: string): Promise<PaymentResult> { + const paymentIntent = await stripe.paymentIntents.capture(paymentId); + + return { + id: paymentIntent.id, + provider: 'stripe', + status: this.mapStatus(paymentIntent.status) + }; + } + + async refundPayment(paymentId: string, amount?: number): Promise<PaymentResult> { + const refund = await stripe.refunds.create({ + payment_intent: paymentId, + amount + }); + + return { + id: refund.id, + provider: 'stripe', + status: refund.status === 'succeeded' ? 'refunded' : 'pending' + }; + } + + async getPaymentStatus(paymentId: string): Promise<PaymentStatus> { + const paymentIntent = await stripe.paymentIntents.retrieve(paymentId); + return this.mapStatus(paymentIntent.status); + } + + async handleWebhook(payload: string, signature: string): Promise<void> { + const event = stripe.webhooks.constructEvent( + payload, + signature, + process.env.STRIPE_WEBHOOK_SECRET! + ); + + switch (event.type) { + case 'payment_intent.succeeded': + await this.handlePaymentSucceeded(event.data.object as Stripe.PaymentIntent); + break; + case 'payment_intent.payment_failed': + await this.handlePaymentFailed(event.data.object as Stripe.PaymentIntent); + break; + } + } + + private mapPaymentMethod(method: PaymentMethodType): Stripe.Checkout.SessionCreateParams.PaymentMethodType[] { + const mapping: Record<PaymentMethodType, Stripe.Checkout.SessionCreateParams.PaymentMethodType[]> = { + card: ['card'], + sepa_debit: ['sepa_debit'], + ideal: ['ideal'], + giropay: ['giropay'], + sofort: ['sofort'], + bancontact: ['bancontact'], + eps: ['eps'], + klarna: ['klarna'], + paypal: ['paypal'], + apple_pay: ['card'], + google_pay: ['card'] + }; + + return mapping[method] || ['card']; + } + + private mapStatus(status: Stripe.PaymentIntent.Status): PaymentStatus { + const mapping: Record<string, PaymentStatus> = { + requires_payment_method: 'pending', + requires_confirmation: 'pending', + requires_action: 'requires_action', + processing: 'processing', + requires_capture: 'processing', + canceled: 'canceled', + succeeded: 'succeeded' + }; + + return mapping[status] || 'pending'; + } +} +``` + +--- + +## PayPal Adapter + +```typescript +// lib/payments/providers/paypal.ts +import { PaymentProvider, CreatePaymentInput, PaymentResult } from '../types'; + +const PAYPAL_API = process.env.PAYPAL_MODE === 'live' + ? 'https://api-m.paypal.com' + : 'https://api-m.sandbox.paypal.com'; + +export class PayPalProvider implements PaymentProvider { + name = 'paypal'; + supportedMethods: PaymentMethodType[] = ['paypal']; + + private async getAccessToken(): Promise<string> { + const auth = Buffer.from( + `${process.env.PAYPAL_CLIENT_ID}:${process.env.PAYPAL_SECRET}` + ).toString('base64'); + + const response = await fetch(`${PAYPAL_API}/v1/oauth2/token`, { + method: 'POST', + headers: { + 'Authorization': `Basic ${auth}`, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: 'grant_type=client_credentials' + }); + + const data = await response.json(); + return data.access_token; + } + + async createPayment(input: CreatePaymentInput): Promise<PaymentResult> { + const accessToken = await this.getAccessToken(); + + const response = await fetch(`${PAYPAL_API}/v2/checkout/orders`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + intent: 'CAPTURE', + purchase_units: [ + { + reference_id: input.orderId, + amount: { + currency_code: input.currency.toUpperCase(), + value: (input.amount / 100).toFixed(2) + } + } + ], + application_context: { + return_url: input.returnUrl, + cancel_url: input.cancelUrl || input.returnUrl, + brand_name: 'Your Store', + user_action: 'PAY_NOW' + } + }) + }); + + const order = await response.json(); + + if (order.error) { + return { + id: '', + provider: 'paypal', + status: 'failed', + error: order.error.message + }; + } + + const approveLink = order.links.find((l: any) => l.rel === 'approve'); + + return { + id: order.id, + provider: 'paypal', + status: 'pending', + redirectUrl: approveLink?.href + }; + } + + async capturePayment(paymentId: string): Promise<PaymentResult> { + const accessToken = await this.getAccessToken(); + + const response = await fetch( + `${PAYPAL_API}/v2/checkout/orders/${paymentId}/capture`, + { + method: 'POST', + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json' + } + } + ); + + const result = await response.json(); + + return { + id: result.id, + provider: 'paypal', + status: result.status === 'COMPLETED' ? 'succeeded' : 'failed' + }; + } + + async refundPayment(paymentId: string, amount?: number): Promise<PaymentResult> { + const accessToken = await this.getAccessToken(); + + // Get capture ID from order + const orderResponse = await fetch( + `${PAYPAL_API}/v2/checkout/orders/${paymentId}`, + { + headers: { 'Authorization': `Bearer ${accessToken}` } + } + ); + + const order = await orderResponse.json(); + const captureId = order.purchase_units[0]?.payments?.captures?.[0]?.id; + + if (!captureId) { + return { + id: '', + provider: 'paypal', + status: 'failed', + error: 'No capture found' + }; + } + + const refundResponse = await fetch( + `${PAYPAL_API}/v2/payments/captures/${captureId}/refund`, + { + method: 'POST', + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json' + }, + body: amount ? JSON.stringify({ + amount: { + value: (amount / 100).toFixed(2), + currency_code: order.purchase_units[0].amount.currency_code + } + }) : undefined + } + ); + + const refund = await refundResponse.json(); + + return { + id: refund.id, + provider: 'paypal', + status: refund.status === 'COMPLETED' ? 'refunded' : 'pending' + }; + } + + async getPaymentStatus(paymentId: string): Promise<PaymentStatus> { + const accessToken = await this.getAccessToken(); + + const response = await fetch( + `${PAYPAL_API}/v2/checkout/orders/${paymentId}`, + { + headers: { 'Authorization': `Bearer ${accessToken}` } + } + ); + + const order = await response.json(); + + const statusMap: Record<string, PaymentStatus> = { + CREATED: 'pending', + SAVED: 'pending', + APPROVED: 'requires_action', + COMPLETED: 'succeeded', + VOIDED: 'canceled' + }; + + return statusMap[order.status] || 'pending'; + } + + async handleWebhook(payload: unknown, signature: string): Promise<void> { + // Verify webhook signature + // Process webhook events + } +} +``` + +--- + +## Payment Service + +```typescript +// lib/payments/service.ts +import { StripeProvider } from './providers/stripe'; +import { PayPalProvider } from './providers/paypal'; +import { PaymentProvider, CreatePaymentInput, PaymentResult, PaymentMethodType } from './types'; +import { db } from '@/lib/db'; + +const providers: Record<string, PaymentProvider> = { + stripe: new StripeProvider(), + paypal: new PayPalProvider() +}; + +function getProviderForMethod(method: PaymentMethodType): PaymentProvider { + for (const provider of Object.values(providers)) { + if (provider.supportedMethods.includes(method)) { + return provider; + } + } + throw new Error(`No provider supports payment method: ${method}`); +} + +export async function createPayment(input: CreatePaymentInput): Promise<PaymentResult> { + const provider = getProviderForMethod(input.method); + const result = await provider.createPayment(input); + + // Store payment record + await db.payment.create({ + data: { + id: result.id, + orderId: input.orderId, + provider: provider.name, + method: input.method, + amount: input.amount, + currency: input.currency, + status: result.status, + metadata: input.metadata + } + }); + + return result; +} + +export async function capturePayment(paymentId: string): Promise<PaymentResult> { + const payment = await db.payment.findUnique({ where: { id: paymentId } }); + if (!payment) throw new Error('Payment not found'); + + const provider = providers[payment.provider]; + const result = await provider.capturePayment(paymentId); + + await db.payment.update({ + where: { id: paymentId }, + data: { status: result.status } + }); + + return result; +} + +export async function refundPayment( + paymentId: string, + amount?: number +): Promise<PaymentResult> { + const payment = await db.payment.findUnique({ where: { id: paymentId } }); + if (!payment) throw new Error('Payment not found'); + + const provider = providers[payment.provider]; + const result = await provider.refundPayment(paymentId, amount); + + await db.payment.update({ + where: { id: paymentId }, + data: { + status: result.status, + refundedAmount: amount || payment.amount + } + }); + + return result; +} + +export function getAvailablePaymentMethods( + country: string, + currency: string, + amount: number +): PaymentMethod[] { + const methods: PaymentMethod[] = [ + { + id: 'card', + type: 'card', + name: 'Credit/Debit Card', + icon: '/icons/card.svg', + countries: ['*'], + currencies: ['*'] + }, + { + id: 'paypal', + type: 'paypal', + name: 'PayPal', + icon: '/icons/paypal.svg', + countries: ['*'], + currencies: ['EUR', 'USD', 'GBP'] + }, + { + id: 'klarna', + type: 'klarna', + name: 'Klarna', + icon: '/icons/klarna.svg', + countries: ['DE', 'AT', 'NL', 'SE', 'FI', 'NO', 'DK', 'GB', 'US'], + currencies: ['EUR', 'USD', 'GBP', 'SEK', 'NOK', 'DKK'], + minAmount: 100, + maxAmount: 500000 + }, + { + id: 'ideal', + type: 'ideal', + name: 'iDEAL', + icon: '/icons/ideal.svg', + countries: ['NL'], + currencies: ['EUR'] + }, + { + id: 'giropay', + type: 'giropay', + name: 'Giropay', + icon: '/icons/giropay.svg', + countries: ['DE'], + currencies: ['EUR'] + }, + { + id: 'sepa_debit', + type: 'sepa_debit', + name: 'SEPA Direct Debit', + icon: '/icons/sepa.svg', + countries: ['DE', 'AT', 'NL', 'BE', 'FR', 'ES', 'IT'], + currencies: ['EUR'] + } + ]; + + return methods.filter(method => { + const countryMatch = method.countries.includes('*') || method.countries.includes(country); + const currencyMatch = method.currencies.includes('*') || method.currencies.includes(currency); + const amountMatch = (!method.minAmount || amount >= method.minAmount) && + (!method.maxAmount || amount <= method.maxAmount); + + return countryMatch && currencyMatch && amountMatch; + }); +} +``` + +--- + +## Checkout UI + +```tsx +// components/checkout/PaymentMethodSelector.tsx +'use client'; + +import { useState } from 'react'; +import Image from 'next/image'; +import { PaymentMethod } from '@/lib/payments/types'; + +interface Props { + methods: PaymentMethod[]; + selected: string | null; + onSelect: (methodId: string) => void; +} + +export function PaymentMethodSelector({ methods, selected, onSelect }: Props) { + return ( + <div className="space-y-3"> + <h3 className="font-semibold">Payment Method</h3> + + <div className="grid gap-2"> + {methods.map((method) => ( + <button + key={method.id} + onClick={() => onSelect(method.id)} + className={`flex items-center gap-3 p-4 border rounded-lg transition ${ + selected === method.id + ? 'border-blue-500 bg-blue-50' + : 'border-gray-200 hover:border-gray-300' + }`} + > + <Image + src={method.icon} + alt={method.name} + width={40} + height={24} + className="object-contain" + /> + <span className="font-medium">{method.name}</span> + {selected === method.id && ( + <span className="ml-auto text-blue-500">✓</span> + )} + </button> + ))} + </div> + </div> + ); +} +``` + +--- + +## Provider Comparison + +| Provider | Card | PayPal | Klarna | Local Methods | Fees | +|----------|------|--------|--------|---------------|------| +| **Stripe** | ✓ | ✓ | ✓ | iDEAL, SEPA, etc. | 1.4%+ | +| **PayPal** | ✓ | ✓ | - | - | 2.9%+ | +| **Adyen** | ✓ | ✓ | ✓ | 250+ methods | Custom | +| **Mollie** | ✓ | ✓ | ✓ | iDEAL, Bancontact | 1.8%+ | + +--- + +## Fazit + +Payment Integration erfordert: + +1. **Abstraction Layer**: Einheitliches Interface +2. **Multiple Providers**: Regionale Methoden +3. **Error Handling**: Graceful Degradation +4. **Webhooks**: Asynchrone Statusupdates + +Die richtige Payment-Strategie erhöht Conversion. + +--- + +## Bildprompts + +1. "Payment method selection UI, card icons and options" +2. "Checkout flow diagram, multiple payment providers" +3. "Payment dashboard, transaction history and analytics" + +--- + +## Quellen + +- [Stripe Payment Methods](https://stripe.com/docs/payments/payment-methods) +- [PayPal Checkout](https://developer.paypal.com/docs/checkout/) +- [Klarna Integration](https://docs.klarna.com/) +- [Payment Provider Comparison](https://www.merchantmaverick.com/) diff --git a/blog-posts/98-digital-product-delivery.md b/blog-posts/98-digital-product-delivery.md new file mode 100644 index 0000000..3e57fce --- /dev/null +++ b/blog-posts/98-digital-product-delivery.md @@ -0,0 +1,1230 @@ +# Digital Product Delivery + +**Meta-Description:** Digitale Produkte sicher ausliefern. Download-Links, License Keys, E-Books und Software mit Node.js und Next.js automatisiert bereitstellen. + +**Keywords:** Digital Products, Download Links, License Keys, E-Book Delivery, Software Distribution, Signed URLs, Content Protection + +--- + +## Einführung + +**Digital Product Delivery** automatisiert die Auslieferung von Downloads, Lizenzen und digitalen Gütern nach dem Kauf. Von **signierten URLs** bis **License Key Generation** – sichere Delivery ist entscheidend für digitale Geschäftsmodelle. Dieser Guide zeigt Production-ready Implementierungen. + +--- + +## Delivery Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ DIGITAL PRODUCT DELIVERY SYSTEM │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ PRODUCT TYPES │ │ +│ ├─────────────────────────────────────────────────────┤ │ +│ │ │ │ +│ │ 📁 Downloadable Files 📋 License Keys │ │ +│ │ ├── E-Books (PDF/EPUB) ├── Software Licenses │ │ +│ │ ├── Audio/Video ├── API Access Keys │ │ +│ │ ├── Software Packages └── Subscription Codes │ │ +│ │ └── Templates/Assets │ │ +│ │ │ │ +│ │ 🔗 Access Grants 📧 Email Delivery │ │ +│ │ ├── Course Access ├── Welcome Emails │ │ +│ │ ├── Membership Areas ├── Download Links │ │ +│ │ └── Private Content └── License Delivery │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ DELIVERY PIPELINE │ │ +│ │ │ │ +│ │ Payment ──► Fulfillment ──► Delivery ──► Access │ │ +│ │ │ │ │ │ │ │ +│ │ Webhook Generate Send Email Track │ │ +│ │ Received Assets/Keys with Links Downloads │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Security Measures: │ +│ ├── Signed URLs with expiration │ +│ ├── Download limits per purchase │ +│ ├── IP tracking and fraud detection │ +│ └── License key validation │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Product & License Models + +```typescript +// prisma/schema.prisma +model DigitalProduct { + id String @id @default(cuid()) + name String + slug String @unique + description String? + type ProductType + price Int // cents + currency String @default("EUR") + + // File-based products + fileKey String? // S3 key + fileName String? + fileSize Int? + fileMimeType String? + + // License-based products + licenseType LicenseType? + maxActivations Int? + + // Metadata + metadata Json? + active Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + purchases Purchase[] + licenses License[] +} + +model Purchase { + id String @id @default(cuid()) + userId String + productId String + product DigitalProduct @relation(fields: [productId], references: [id]) + + // Payment info + stripePaymentId String? + amount Int + currency String + status PurchaseStatus @default(PENDING) + + // Delivery tracking + deliveredAt DateTime? + downloadCount Int @default(0) + maxDownloads Int @default(5) + + // Download links + downloads Download[] + licenses License[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id]) +} + +model Download { + id String @id @default(cuid()) + purchaseId String + purchase Purchase @relation(fields: [purchaseId], references: [id]) + + token String @unique + expiresAt DateTime + downloadedAt DateTime? + ipAddress String? + userAgent String? + + createdAt DateTime @default(now()) +} + +model License { + id String @id @default(cuid()) + purchaseId String + purchase Purchase @relation(fields: [purchaseId], references: [id]) + productId String + product DigitalProduct @relation(fields: [productId], references: [id]) + + key String @unique + status LicenseStatus @default(ACTIVE) + activations Int @default(0) + maxActivations Int + + // Activation tracking + activationRecords LicenseActivation[] + + expiresAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model LicenseActivation { + id String @id @default(cuid()) + licenseId String + license License @relation(fields: [licenseId], references: [id]) + + machineId String + machineName String? + ipAddress String? + activatedAt DateTime @default(now()) + deactivatedAt DateTime? + + @@unique([licenseId, machineId]) +} + +enum ProductType { + DOWNLOAD // Downloadable file + LICENSE // Software license + SUBSCRIPTION // Recurring access + COURSE // Online course access + MEMBERSHIP // Membership access +} + +enum LicenseType { + PERPETUAL // Forever + SUBSCRIPTION // Time-limited + TRIAL // Trial period +} + +enum PurchaseStatus { + PENDING + COMPLETED + REFUNDED + FAILED +} + +enum LicenseStatus { + ACTIVE + SUSPENDED + REVOKED + EXPIRED +} +``` + +--- + +## Signed URL Generation + +```typescript +// lib/delivery/signed-urls.ts +import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; +import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; +import crypto from 'crypto'; + +const s3 = new S3Client({ + region: process.env.AWS_REGION!, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID!, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY! + } +}); + +interface SignedUrlOptions { + expiresIn?: number; // seconds + downloadFilename?: string; +} + +export async function generateSignedDownloadUrl( + fileKey: string, + options: SignedUrlOptions = {} +): Promise<string> { + const { expiresIn = 3600, downloadFilename } = options; + + const command = new GetObjectCommand({ + Bucket: process.env.S3_BUCKET!, + Key: fileKey, + ResponseContentDisposition: downloadFilename + ? `attachment; filename="${downloadFilename}"` + : undefined + }); + + return getSignedUrl(s3, command, { expiresIn }); +} + +// Custom signed URL for self-hosted files +export function generateCustomSignedUrl( + path: string, + expiresIn: number = 3600 +): string { + const expires = Math.floor(Date.now() / 1000) + expiresIn; + const signature = crypto + .createHmac('sha256', process.env.DOWNLOAD_SECRET!) + .update(`${path}:${expires}`) + .digest('hex'); + + const baseUrl = process.env.NEXT_PUBLIC_APP_URL; + return `${baseUrl}/api/download/${path}?expires=${expires}&sig=${signature}`; +} + +export function verifySignedUrl( + path: string, + expires: string, + signature: string +): boolean { + const expiresNum = parseInt(expires, 10); + + // Check expiration + if (Date.now() / 1000 > expiresNum) { + return false; + } + + // Verify signature + const expectedSig = crypto + .createHmac('sha256', process.env.DOWNLOAD_SECRET!) + .update(`${path}:${expiresNum}`) + .digest('hex'); + + return crypto.timingSafeEqual( + Buffer.from(signature), + Buffer.from(expectedSig) + ); +} +``` + +--- + +## Download Token System + +```typescript +// lib/delivery/download-tokens.ts +import { db } from '@/lib/db'; +import crypto from 'crypto'; +import { addHours } from 'date-fns'; + +interface CreateDownloadTokenResult { + token: string; + downloadUrl: string; + expiresAt: Date; +} + +export async function createDownloadToken( + purchaseId: string, + expirationHours: number = 24 +): Promise<CreateDownloadTokenResult> { + const purchase = await db.purchase.findUnique({ + where: { id: purchaseId }, + include: { product: true } + }); + + if (!purchase) { + throw new Error('Purchase not found'); + } + + if (purchase.downloadCount >= purchase.maxDownloads) { + throw new Error('Download limit exceeded'); + } + + const token = crypto.randomBytes(32).toString('hex'); + const expiresAt = addHours(new Date(), expirationHours); + + await db.download.create({ + data: { + purchaseId, + token, + expiresAt + } + }); + + const downloadUrl = `${process.env.NEXT_PUBLIC_APP_URL}/api/download/${token}`; + + return { token, downloadUrl, expiresAt }; +} + +export async function validateAndConsumeToken( + token: string, + ipAddress?: string, + userAgent?: string +): Promise<{ fileKey: string; fileName: string } | null> { + const download = await db.download.findUnique({ + where: { token }, + include: { + purchase: { + include: { product: true } + } + } + }); + + if (!download) { + return null; + } + + // Check expiration + if (download.expiresAt < new Date()) { + return null; + } + + // Check if already used + if (download.downloadedAt) { + return null; + } + + // Check purchase download limits + if (download.purchase.downloadCount >= download.purchase.maxDownloads) { + return null; + } + + // Mark as used and increment counter + await db.$transaction([ + db.download.update({ + where: { id: download.id }, + data: { + downloadedAt: new Date(), + ipAddress, + userAgent + } + }), + db.purchase.update({ + where: { id: download.purchaseId }, + data: { + downloadCount: { increment: 1 } + } + }) + ]); + + return { + fileKey: download.purchase.product.fileKey!, + fileName: download.purchase.product.fileName! + }; +} +``` + +--- + +## License Key Generation + +```typescript +// lib/delivery/license-keys.ts +import crypto from 'crypto'; +import { db } from '@/lib/db'; + +interface LicenseKeyOptions { + prefix?: string; + segments?: number; + segmentLength?: number; +} + +export function generateLicenseKey(options: LicenseKeyOptions = {}): string { + const { + prefix = 'PRO', + segments = 4, + segmentLength = 5 + } = options; + + const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // No I, O, 0, 1 + const keySegments: string[] = []; + + for (let i = 0; i < segments; i++) { + let segment = ''; + for (let j = 0; j < segmentLength; j++) { + const randomIndex = crypto.randomInt(0, chars.length); + segment += chars[randomIndex]; + } + keySegments.push(segment); + } + + return `${prefix}-${keySegments.join('-')}`; +} + +// Checksum-validated license key +export function generateValidatedLicenseKey(productCode: string): string { + const key = generateLicenseKey({ prefix: productCode }); + const checksum = calculateChecksum(key); + return `${key}-${checksum}`; +} + +function calculateChecksum(key: string): string { + const hash = crypto + .createHash('sha256') + .update(key + process.env.LICENSE_SECRET!) + .digest('hex'); + return hash.substring(0, 4).toUpperCase(); +} + +export function validateLicenseKeyFormat(key: string): boolean { + // Format: XXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXX + const pattern = /^[A-Z]{2,4}-([A-Z0-9]{5}-){3,4}[A-Z0-9]{4}$/; + + if (!pattern.test(key)) { + return false; + } + + // Validate checksum + const parts = key.split('-'); + const checksum = parts.pop()!; + const keyWithoutChecksum = parts.join('-'); + + return calculateChecksum(keyWithoutChecksum) === checksum; +} + +// Create license in database +export async function createLicense( + purchaseId: string, + productId: string +): Promise<License> { + const product = await db.digitalProduct.findUnique({ + where: { id: productId } + }); + + if (!product) { + throw new Error('Product not found'); + } + + const key = generateValidatedLicenseKey( + product.slug.substring(0, 3).toUpperCase() + ); + + return db.license.create({ + data: { + purchaseId, + productId, + key, + maxActivations: product.maxActivations || 3, + expiresAt: product.licenseType === 'SUBSCRIPTION' + ? new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1 year + : null + } + }); +} +``` + +--- + +## License Activation API + +```typescript +// app/api/licenses/activate/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { db } from '@/lib/db'; +import { validateLicenseKeyFormat } from '@/lib/delivery/license-keys'; + +interface ActivationRequest { + licenseKey: string; + machineId: string; + machineName?: string; +} + +export async function POST(request: NextRequest) { + try { + const body: ActivationRequest = await request.json(); + const { licenseKey, machineId, machineName } = body; + + // Validate format + if (!validateLicenseKeyFormat(licenseKey)) { + return NextResponse.json( + { error: 'Invalid license key format' }, + { status: 400 } + ); + } + + // Find license + const license = await db.license.findUnique({ + where: { key: licenseKey }, + include: { + product: true, + activationRecords: true + } + }); + + if (!license) { + return NextResponse.json( + { error: 'License not found' }, + { status: 404 } + ); + } + + // Check status + if (license.status !== 'ACTIVE') { + return NextResponse.json( + { error: `License is ${license.status.toLowerCase()}` }, + { status: 403 } + ); + } + + // Check expiration + if (license.expiresAt && license.expiresAt < new Date()) { + await db.license.update({ + where: { id: license.id }, + data: { status: 'EXPIRED' } + }); + + return NextResponse.json( + { error: 'License has expired' }, + { status: 403 } + ); + } + + // Check if already activated on this machine + const existingActivation = license.activationRecords.find( + a => a.machineId === machineId && !a.deactivatedAt + ); + + if (existingActivation) { + return NextResponse.json({ + success: true, + message: 'Already activated on this machine', + license: formatLicenseResponse(license) + }); + } + + // Check activation limit + const activeActivations = license.activationRecords.filter( + a => !a.deactivatedAt + ).length; + + if (activeActivations >= license.maxActivations) { + return NextResponse.json( + { + error: 'Maximum activations reached', + maxActivations: license.maxActivations, + currentActivations: activeActivations + }, + { status: 403 } + ); + } + + // Create activation + const ipAddress = request.headers.get('x-forwarded-for') || + request.headers.get('x-real-ip'); + + await db.$transaction([ + db.licenseActivation.create({ + data: { + licenseId: license.id, + machineId, + machineName, + ipAddress + } + }), + db.license.update({ + where: { id: license.id }, + data: { activations: { increment: 1 } } + }) + ]); + + return NextResponse.json({ + success: true, + message: 'License activated successfully', + license: formatLicenseResponse(license) + }); + + } catch (error) { + console.error('Activation error:', error); + return NextResponse.json( + { error: 'Activation failed' }, + { status: 500 } + ); + } +} + +function formatLicenseResponse(license: any) { + return { + productName: license.product.name, + licenseType: license.product.licenseType, + expiresAt: license.expiresAt, + features: license.product.metadata?.features || [] + }; +} + +// Deactivation endpoint +export async function DELETE(request: NextRequest) { + const { searchParams } = new URL(request.url); + const licenseKey = searchParams.get('key'); + const machineId = searchParams.get('machineId'); + + if (!licenseKey || !machineId) { + return NextResponse.json( + { error: 'License key and machine ID required' }, + { status: 400 } + ); + } + + const license = await db.license.findUnique({ + where: { key: licenseKey } + }); + + if (!license) { + return NextResponse.json( + { error: 'License not found' }, + { status: 404 } + ); + } + + const activation = await db.licenseActivation.findUnique({ + where: { + licenseId_machineId: { + licenseId: license.id, + machineId + } + } + }); + + if (!activation || activation.deactivatedAt) { + return NextResponse.json( + { error: 'No active activation found' }, + { status: 404 } + ); + } + + await db.$transaction([ + db.licenseActivation.update({ + where: { id: activation.id }, + data: { deactivatedAt: new Date() } + }), + db.license.update({ + where: { id: license.id }, + data: { activations: { decrement: 1 } } + }) + ]); + + return NextResponse.json({ + success: true, + message: 'License deactivated successfully' + }); +} +``` + +--- + +## Fulfillment Service + +```typescript +// lib/delivery/fulfillment.ts +import { db } from '@/lib/db'; +import { createDownloadToken } from './download-tokens'; +import { createLicense } from './license-keys'; +import { sendEmail } from '@/lib/email'; + +interface FulfillmentResult { + success: boolean; + downloadUrl?: string; + licenseKey?: string; + error?: string; +} + +export async function fulfillPurchase( + purchaseId: string +): Promise<FulfillmentResult> { + const purchase = await db.purchase.findUnique({ + where: { id: purchaseId }, + include: { + product: true, + user: true + } + }); + + if (!purchase) { + return { success: false, error: 'Purchase not found' }; + } + + if (purchase.status !== 'COMPLETED') { + return { success: false, error: 'Purchase not completed' }; + } + + if (purchase.deliveredAt) { + return { success: false, error: 'Already delivered' }; + } + + try { + let downloadUrl: string | undefined; + let licenseKey: string | undefined; + + const { product } = purchase; + + // Handle different product types + switch (product.type) { + case 'DOWNLOAD': + const downloadResult = await createDownloadToken(purchaseId); + downloadUrl = downloadResult.downloadUrl; + break; + + case 'LICENSE': + const license = await createLicense(purchaseId, product.id); + licenseKey = license.key; + break; + + case 'SUBSCRIPTION': + case 'MEMBERSHIP': + // Grant access in database + await grantAccess(purchase.userId, product.id); + break; + + case 'COURSE': + await enrollInCourse(purchase.userId, product.id); + break; + } + + // Mark as delivered + await db.purchase.update({ + where: { id: purchaseId }, + data: { deliveredAt: new Date() } + }); + + // Send delivery email + await sendDeliveryEmail(purchase, downloadUrl, licenseKey); + + return { success: true, downloadUrl, licenseKey }; + + } catch (error) { + console.error('Fulfillment error:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Fulfillment failed' + }; + } +} + +async function grantAccess(userId: string, productId: string): Promise<void> { + await db.userAccess.create({ + data: { + userId, + productId, + grantedAt: new Date() + } + }); +} + +async function enrollInCourse(userId: string, productId: string): Promise<void> { + const product = await db.digitalProduct.findUnique({ + where: { id: productId } + }); + + const courseId = product?.metadata?.courseId; + if (!courseId) { + throw new Error('Course ID not found in product metadata'); + } + + await db.courseEnrollment.create({ + data: { + userId, + courseId, + enrolledAt: new Date() + } + }); +} + +async function sendDeliveryEmail( + purchase: any, + downloadUrl?: string, + licenseKey?: string +): Promise<void> { + const { user, product } = purchase; + + await sendEmail({ + to: user.email, + template: 'product-delivery', + data: { + userName: user.name, + productName: product.name, + productType: product.type, + downloadUrl, + licenseKey, + supportEmail: 'support@example.com' + } + }); +} +``` + +--- + +## Download API Endpoint + +```typescript +// app/api/download/[token]/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; +import { validateAndConsumeToken } from '@/lib/delivery/download-tokens'; + +const s3 = new S3Client({ + region: process.env.AWS_REGION!, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID!, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY! + } +}); + +export async function GET( + request: NextRequest, + { params }: { params: { token: string } } +) { + const { token } = params; + + const ipAddress = request.headers.get('x-forwarded-for') || + request.headers.get('x-real-ip') || + undefined; + const userAgent = request.headers.get('user-agent') || undefined; + + // Validate and consume token + const result = await validateAndConsumeToken(token, ipAddress, userAgent); + + if (!result) { + return NextResponse.json( + { error: 'Invalid or expired download link' }, + { status: 403 } + ); + } + + try { + // Fetch file from S3 + const command = new GetObjectCommand({ + Bucket: process.env.S3_BUCKET!, + Key: result.fileKey + }); + + const s3Response = await s3.send(command); + + if (!s3Response.Body) { + throw new Error('File not found'); + } + + // Stream the file + const stream = s3Response.Body as ReadableStream; + + return new NextResponse(stream, { + headers: { + 'Content-Type': s3Response.ContentType || 'application/octet-stream', + 'Content-Disposition': `attachment; filename="${result.fileName}"`, + 'Content-Length': s3Response.ContentLength?.toString() || '', + 'Cache-Control': 'no-store' + } + }); + + } catch (error) { + console.error('Download error:', error); + return NextResponse.json( + { error: 'Download failed' }, + { status: 500 } + ); + } +} +``` + +--- + +## Stripe Webhook Integration + +```typescript +// app/api/webhooks/stripe/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import Stripe from 'stripe'; +import { db } from '@/lib/db'; +import { fulfillPurchase } from '@/lib/delivery/fulfillment'; + +const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); + +export async function POST(request: NextRequest) { + const body = await request.text(); + const signature = request.headers.get('stripe-signature')!; + + let event: Stripe.Event; + + try { + event = stripe.webhooks.constructEvent( + body, + signature, + process.env.STRIPE_WEBHOOK_SECRET! + ); + } catch (error) { + return NextResponse.json( + { error: 'Invalid signature' }, + { status: 400 } + ); + } + + switch (event.type) { + case 'checkout.session.completed': + await handleCheckoutComplete(event.data.object as Stripe.Checkout.Session); + break; + + case 'payment_intent.succeeded': + await handlePaymentSucceeded(event.data.object as Stripe.PaymentIntent); + break; + + case 'charge.refunded': + await handleRefund(event.data.object as Stripe.Charge); + break; + } + + return NextResponse.json({ received: true }); +} + +async function handleCheckoutComplete(session: Stripe.Checkout.Session) { + const purchaseId = session.metadata?.purchaseId; + + if (!purchaseId) { + console.error('No purchase ID in session metadata'); + return; + } + + // Update purchase status + await db.purchase.update({ + where: { id: purchaseId }, + data: { + status: 'COMPLETED', + stripePaymentId: session.payment_intent as string + } + }); + + // Fulfill the purchase + const result = await fulfillPurchase(purchaseId); + + if (!result.success) { + console.error('Fulfillment failed:', result.error); + // Alert for manual intervention + await alertFulfillmentFailure(purchaseId, result.error); + } +} + +async function handlePaymentSucceeded(paymentIntent: Stripe.PaymentIntent) { + // Handle one-time payments + const purchase = await db.purchase.findFirst({ + where: { stripePaymentId: paymentIntent.id } + }); + + if (purchase && purchase.status !== 'COMPLETED') { + await db.purchase.update({ + where: { id: purchase.id }, + data: { status: 'COMPLETED' } + }); + + await fulfillPurchase(purchase.id); + } +} + +async function handleRefund(charge: Stripe.Charge) { + const purchase = await db.purchase.findFirst({ + where: { stripePaymentId: charge.payment_intent as string } + }); + + if (purchase) { + await db.purchase.update({ + where: { id: purchase.id }, + data: { status: 'REFUNDED' } + }); + + // Revoke licenses + await db.license.updateMany({ + where: { purchaseId: purchase.id }, + data: { status: 'REVOKED' } + }); + + // Revoke access + await db.userAccess.deleteMany({ + where: { + userId: purchase.userId, + productId: purchase.productId + } + }); + } +} + +async function alertFulfillmentFailure(purchaseId: string, error?: string) { + // Send alert to admin + await sendEmail({ + to: process.env.ADMIN_EMAIL!, + template: 'fulfillment-failure', + data: { purchaseId, error } + }); +} +``` + +--- + +## Customer Download Page + +```typescript +// app/purchases/[id]/page.tsx +import { db } from '@/lib/db'; +import { auth } from '@/lib/auth'; +import { createDownloadToken } from '@/lib/delivery/download-tokens'; +import { redirect, notFound } from 'next/navigation'; + +interface Props { + params: { id: string }; +} + +export default async function PurchasePage({ params }: Props) { + const session = await auth(); + if (!session?.user) redirect('/login'); + + const purchase = await db.purchase.findUnique({ + where: { + id: params.id, + userId: session.user.id + }, + include: { + product: true, + licenses: true, + downloads: { + orderBy: { createdAt: 'desc' }, + take: 5 + } + } + }); + + if (!purchase) notFound(); + + return ( + <div className="max-w-2xl mx-auto p-6"> + <h1 className="text-2xl font-bold mb-6">{purchase.product.name}</h1> + + <div className="bg-white rounded-lg shadow p-6 space-y-6"> + {/* Purchase Info */} + <div className="border-b pb-4"> + <p className="text-sm text-gray-500">Purchase Date</p> + <p>{new Date(purchase.createdAt).toLocaleDateString()}</p> + </div> + + {/* Download Section */} + {purchase.product.type === 'DOWNLOAD' && ( + <DownloadSection purchase={purchase} /> + )} + + {/* License Section */} + {purchase.product.type === 'LICENSE' && ( + <LicenseSection licenses={purchase.licenses} /> + )} + + {/* Access Section */} + {['SUBSCRIPTION', 'MEMBERSHIP', 'COURSE'].includes(purchase.product.type) && ( + <AccessSection product={purchase.product} /> + )} + </div> + </div> + ); +} + +function DownloadSection({ purchase }: { purchase: any }) { + const remainingDownloads = purchase.maxDownloads - purchase.downloadCount; + + return ( + <div> + <h2 className="font-semibold mb-2">Download</h2> + <p className="text-sm text-gray-600 mb-4"> + {remainingDownloads} downloads remaining + </p> + + {remainingDownloads > 0 ? ( + <form action={generateNewDownload}> + <input type="hidden" name="purchaseId" value={purchase.id} /> + <button + type="submit" + className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700" + > + Generate Download Link + </button> + </form> + ) : ( + <p className="text-red-600">Download limit reached. Contact support.</p> + )} + + {/* Recent Downloads */} + {purchase.downloads.length > 0 && ( + <div className="mt-4"> + <h3 className="text-sm font-medium mb-2">Recent Downloads</h3> + <ul className="text-sm text-gray-600 space-y-1"> + {purchase.downloads.map((d: any) => ( + <li key={d.id}> + {new Date(d.createdAt).toLocaleString()} - + {d.downloadedAt ? ' Downloaded' : ' Pending'} + </li> + ))} + </ul> + </div> + )} + </div> + ); +} + +function LicenseSection({ licenses }: { licenses: any[] }) { + return ( + <div> + <h2 className="font-semibold mb-2">License Keys</h2> + {licenses.map(license => ( + <div key={license.id} className="bg-gray-50 p-4 rounded mb-2"> + <code className="text-lg font-mono">{license.key}</code> + <div className="text-sm text-gray-600 mt-2"> + <p>Status: {license.status}</p> + <p>Activations: {license.activations} / {license.maxActivations}</p> + {license.expiresAt && ( + <p>Expires: {new Date(license.expiresAt).toLocaleDateString()}</p> + )} + </div> + </div> + ))} + </div> + ); +} + +function AccessSection({ product }: { product: any }) { + const accessUrl = getAccessUrl(product); + + return ( + <div> + <h2 className="font-semibold mb-2">Access</h2> + <a + href={accessUrl} + className="inline-block bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" + > + Access {product.name} + </a> + </div> + ); +} + +async function generateNewDownload(formData: FormData) { + 'use server'; + + const purchaseId = formData.get('purchaseId') as string; + const result = await createDownloadToken(purchaseId); + + redirect(result.downloadUrl); +} + +function getAccessUrl(product: any): string { + switch (product.type) { + case 'COURSE': + return `/courses/${product.metadata?.courseSlug}`; + case 'MEMBERSHIP': + return `/members`; + default: + return `/dashboard`; + } +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **URLs** | Signed, time-limited, single-use | +| **Limits** | 3-5 downloads per purchase | +| **Licenses** | Checksum validation, activation limits | +| **Email** | Immediate delivery confirmation | +| **Refunds** | Auto-revoke access and licenses | +| **Monitoring** | Track failed deliveries, alert admins | + +--- + +## Fazit + +Digital Product Delivery erfordert: + +1. **Security**: Signierte URLs, Token, validierte Keys +2. **Automation**: Webhook-triggered Fulfillment +3. **Limits**: Download-Counts, Activation-Limits +4. **UX**: Klare Download-Pages, License-Management + +Sichere Delivery schützt digitale Produkte und sorgt für gute Customer Experience. + +--- + +## Bildprompts + +1. "Digital product delivery flow diagram, download and license paths" +2. "License key activation interface, software registration screen" +3. "E-commerce download page, purchase confirmation with download button" + +--- + +## Quellen + +- [AWS S3 Presigned URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html) +- [Stripe Fulfillment](https://stripe.com/docs/payments/checkout/fulfill-orders) +- [Software Licensing Best Practices](https://www.keygen.sh/docs/guides) +- [Gumroad Digital Products](https://gumroad.com/features/digital-products) diff --git a/blog-posts/99-affiliate-referral-systems.md b/blog-posts/99-affiliate-referral-systems.md new file mode 100644 index 0000000..dc71de9 --- /dev/null +++ b/blog-posts/99-affiliate-referral-systems.md @@ -0,0 +1,1255 @@ +# Affiliate & Referral Systems + +**Meta-Description:** Affiliate und Referral Programme implementieren. Tracking Links, Provisionsberechnung und Auszahlungen mit Node.js und Stripe automatisieren. + +**Keywords:** Affiliate Marketing, Referral Program, Commission Tracking, Partner Program, Referral Links, Payout System, Revenue Share + +--- + +## Einführung + +**Affiliate und Referral Systeme** treiben Wachstum durch Empfehlungen. Von **Tracking-Links** über **Commission-Berechnung** bis **automatisierte Payouts** – ein gut implementiertes System skaliert Kundenakquise kosteneffizient. Dieser Guide zeigt Production-ready Implementierungen. + +--- + +## System Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ AFFILIATE & REFERRAL SYSTEM │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ AFFILIATE │ │ REFERRAL │ │ +│ │ (B2B/Partner) │ │ (Customer) │ │ +│ ├─────────────────┤ ├─────────────────┤ │ +│ │ • Influencers │ │ • Invite friends│ │ +│ │ • Bloggers │ │ • Share links │ │ +│ │ • Review sites │ │ • Earn rewards │ │ +│ │ • Partners │ │ • Both benefit │ │ +│ └────────┬────────┘ └────────┬────────┘ │ +│ │ │ │ +│ └───────────┬───────────────┘ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ TRACKING SYSTEM │ │ +│ ├─────────────────────────────────────────────────────┤ │ +│ │ │ │ +│ │ Unique Link ──► Click Track ──► Cookie/Store │ │ +│ │ │ │ │ │ │ +│ │ ?ref=ABC123 IP, Device, 30-90 day │ │ +│ │ Timestamp attribution │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ CONVERSION & COMMISSION │ │ +│ │ │ │ +│ │ Purchase ──► Attribution ──► Commission ──► Payout│ │ +│ │ │ │ │ │ │ │ +│ │ Order ID Match Cookie Calculate Monthly │ │ +│ │ completed to Affiliate 20-50% Stripe │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Commission Models: │ +│ ├── One-time: Single payment per conversion │ +│ ├── Recurring: % of subscription for lifetime │ +│ ├── Tiered: Higher rates at volume thresholds │ +│ └── Hybrid: One-time + recurring combined │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Database Schema + +```typescript +// prisma/schema.prisma +model Affiliate { + id String @id @default(cuid()) + userId String @unique + user User @relation(fields: [userId], references: [id]) + + // Affiliate identity + code String @unique // ref=ABC123 + slug String? @unique // custom vanity URL + + // Commission settings + commissionType CommissionType @default(PERCENTAGE) + commissionRate Decimal @default(20) // 20% + recurringRate Decimal? // For subscription commissions + + // Tier (affects commission rates) + tier AffiliateTier @default(STANDARD) + + // Payout info + payoutMethod PayoutMethod @default(STRIPE) + stripeAccountId String? + paypalEmail String? + minimumPayout Int @default(5000) // $50.00 minimum + + // Status + status AffiliateStatus @default(PENDING) + approvedAt DateTime? + suspendedAt DateTime? + + // Tracking + clicks Click[] + conversions Conversion[] + payouts Payout[] + + // Stats (denormalized for performance) + totalClicks Int @default(0) + totalConversions Int @default(0) + totalEarnings Int @default(0) // cents + pendingBalance Int @default(0) // cents + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Click { + id String @id @default(cuid()) + affiliateId String + affiliate Affiliate @relation(fields: [affiliateId], references: [id]) + + // Tracking data + ipAddress String? + userAgent String? + referer String? + landingPage String + + // UTM parameters + utmSource String? + utmMedium String? + utmCampaign String? + + // Visitor identification + visitorId String // Fingerprint or cookie ID + sessionId String? + + // Conversion tracking + converted Boolean @default(false) + conversionId String? + + createdAt DateTime @default(now()) + + @@index([affiliateId, createdAt]) + @@index([visitorId]) +} + +model Conversion { + id String @id @default(cuid()) + affiliateId String + affiliate Affiliate @relation(fields: [affiliateId], references: [id]) + + // Order info + orderId String @unique + customerId String + orderAmount Int // cents + currency String @default("EUR") + + // Commission + commissionAmount Int // cents + commissionRate Decimal + commissionType CommissionType + + // Status + status ConversionStatus @default(PENDING) + approvedAt DateTime? + rejectedAt DateTime? + rejectionReason String? + + // Payout tracking + payoutId String? + payout Payout? @relation(fields: [payoutId], references: [id]) + paidAt DateTime? + + // Attribution + clickId String? + attributionWindow Int // days + + // For recurring commissions + isRecurring Boolean @default(false) + parentConversionId String? + recurringMonth Int? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([affiliateId, status]) + @@index([affiliateId, createdAt]) +} + +model Payout { + id String @id @default(cuid()) + affiliateId String + affiliate Affiliate @relation(fields: [affiliateId], references: [id]) + + amount Int // cents + currency String @default("EUR") + fee Int @default(0) // Processing fee + + method PayoutMethod + stripeTransferId String? + paypalPayoutId String? + + status PayoutStatus @default(PENDING) + processedAt DateTime? + failedAt DateTime? + failureReason String? + + conversions Conversion[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// Referral-specific (customer referrals) +model Referral { + id String @id @default(cuid()) + referrerId String // User who referred + referrer User @relation("ReferralsMade", fields: [referrerId], references: [id]) + + referredId String? // User who signed up + referred User? @relation("ReferralsReceived", fields: [referredId], references: [id]) + + code String @unique + email String? // Invited email (before signup) + + // Rewards + referrerReward Int? // cents + referredReward Int? // cents + rewardType RewardType @default(CREDIT) + + // Status + status ReferralStatus @default(PENDING) + signedUpAt DateTime? + qualifiedAt DateTime? // Met qualification criteria + rewardedAt DateTime? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +enum CommissionType { + PERCENTAGE + FIXED + TIERED +} + +enum AffiliateTier { + STANDARD // 20% + SILVER // 25% + GOLD // 30% + PLATINUM // 35% +} + +enum AffiliateStatus { + PENDING + ACTIVE + SUSPENDED + TERMINATED +} + +enum PayoutMethod { + STRIPE + PAYPAL + BANK_TRANSFER +} + +enum ConversionStatus { + PENDING // Waiting for approval period + APPROVED // Commission confirmed + REJECTED // Refund, fraud, etc. + PAID // Included in payout +} + +enum PayoutStatus { + PENDING + PROCESSING + COMPLETED + FAILED +} + +enum RewardType { + CREDIT // Account credit + CASH // Cash payout + DISCOUNT // Discount code +} + +enum ReferralStatus { + PENDING // Invited, not signed up + SIGNED_UP // Created account + QUALIFIED // Met criteria (purchase, etc.) + REWARDED // Rewards distributed + EXPIRED // Never signed up +} +``` + +--- + +## Affiliate Link Generation + +```typescript +// lib/affiliate/links.ts +import { db } from '@/lib/db'; +import crypto from 'crypto'; + +export async function generateAffiliateCode(): Promise<string> { + const code = crypto.randomBytes(4).toString('hex').toUpperCase(); + + // Ensure uniqueness + const existing = await db.affiliate.findUnique({ + where: { code } + }); + + if (existing) { + return generateAffiliateCode(); // Retry + } + + return code; +} + +export function buildAffiliateLink( + baseUrl: string, + affiliateCode: string, + campaign?: string +): string { + const url = new URL(baseUrl); + url.searchParams.set('ref', affiliateCode); + + if (campaign) { + url.searchParams.set('utm_campaign', campaign); + url.searchParams.set('utm_source', 'affiliate'); + url.searchParams.set('utm_medium', 'referral'); + } + + return url.toString(); +} + +// Generate vanity URL +export async function setAffiliateSlug( + affiliateId: string, + slug: string +): Promise<boolean> { + // Validate slug format + if (!/^[a-z0-9-]{3,30}$/.test(slug)) { + throw new Error('Invalid slug format'); + } + + // Check reserved words + const reserved = ['admin', 'api', 'app', 'www', 'help', 'support']; + if (reserved.includes(slug)) { + throw new Error('Slug is reserved'); + } + + try { + await db.affiliate.update({ + where: { id: affiliateId }, + data: { slug } + }); + return true; + } catch { + throw new Error('Slug already taken'); + } +} + +// Link resolver +export async function resolveAffiliateLink( + refCode: string +): Promise<Affiliate | null> { + // Try code first + let affiliate = await db.affiliate.findUnique({ + where: { code: refCode, status: 'ACTIVE' } + }); + + // Try slug + if (!affiliate) { + affiliate = await db.affiliate.findUnique({ + where: { slug: refCode, status: 'ACTIVE' } + }); + } + + return affiliate; +} +``` + +--- + +## Click Tracking + +```typescript +// lib/affiliate/tracking.ts +import { db } from '@/lib/db'; +import { cookies } from 'next/headers'; +import { NextRequest } from 'next/server'; +import crypto from 'crypto'; + +const COOKIE_NAME = 'aff_ref'; +const COOKIE_MAX_AGE = 30 * 24 * 60 * 60; // 30 days + +interface TrackClickParams { + affiliateId: string; + request: NextRequest; + landingPage: string; +} + +export async function trackClick(params: TrackClickParams): Promise<Click> { + const { affiliateId, request, landingPage } = params; + + const visitorId = getVisitorId(request); + const searchParams = new URL(request.url).searchParams; + + const click = await db.click.create({ + data: { + affiliateId, + ipAddress: request.headers.get('x-forwarded-for'), + userAgent: request.headers.get('user-agent'), + referer: request.headers.get('referer'), + landingPage, + utmSource: searchParams.get('utm_source'), + utmMedium: searchParams.get('utm_medium'), + utmCampaign: searchParams.get('utm_campaign'), + visitorId, + sessionId: crypto.randomUUID() + } + }); + + // Update affiliate stats + await db.affiliate.update({ + where: { id: affiliateId }, + data: { totalClicks: { increment: 1 } } + }); + + return click; +} + +function getVisitorId(request: NextRequest): string { + // Try to get existing visitor ID from cookie + const existingId = request.cookies.get('visitor_id')?.value; + if (existingId) return existingId; + + // Generate fingerprint from request headers + const components = [ + request.headers.get('user-agent'), + request.headers.get('accept-language'), + request.headers.get('accept-encoding') + ].filter(Boolean).join('|'); + + return crypto.createHash('sha256').update(components).digest('hex').slice(0, 16); +} + +// Set affiliate cookie +export function setAffiliateCookie(affiliateCode: string): void { + const cookieStore = cookies(); + + cookieStore.set(COOKIE_NAME, affiliateCode, { + maxAge: COOKIE_MAX_AGE, + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + path: '/' + }); +} + +// Get affiliate from cookie +export function getAffiliateFromCookie(): string | null { + const cookieStore = cookies(); + return cookieStore.get(COOKIE_NAME)?.value || null; +} + +// Attribution window check +export async function findAttributableClick( + visitorId: string, + affiliateId: string, + windowDays: number = 30 +): Promise<Click | null> { + const windowStart = new Date(); + windowStart.setDate(windowStart.getDate() - windowDays); + + return db.click.findFirst({ + where: { + affiliateId, + visitorId, + createdAt: { gte: windowStart }, + converted: false + }, + orderBy: { createdAt: 'desc' } + }); +} +``` + +--- + +## Tracking Middleware + +```typescript +// middleware.ts +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export async function middleware(request: NextRequest) { + const response = NextResponse.next(); + const refCode = request.nextUrl.searchParams.get('ref'); + + if (refCode) { + // Validate affiliate exists and is active + const isValid = await validateAffiliateCode(refCode); + + if (isValid) { + // Set cookie + response.cookies.set('aff_ref', refCode, { + maxAge: 30 * 24 * 60 * 60, // 30 days + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax' + }); + + // Track click asynchronously + trackClickAsync(refCode, request); + + // Remove ref from URL for cleaner UX + const cleanUrl = new URL(request.url); + cleanUrl.searchParams.delete('ref'); + + if (cleanUrl.toString() !== request.url) { + return NextResponse.redirect(cleanUrl); + } + } + } + + return response; +} + +async function validateAffiliateCode(code: string): Promise<boolean> { + // Fast validation via edge-compatible check + const response = await fetch( + `${process.env.NEXT_PUBLIC_APP_URL}/api/affiliate/validate?code=${code}` + ); + return response.ok; +} + +function trackClickAsync(refCode: string, request: NextRequest): void { + // Fire and forget + fetch(`${process.env.NEXT_PUBLIC_APP_URL}/api/affiliate/track`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + refCode, + landingPage: request.nextUrl.pathname, + userAgent: request.headers.get('user-agent'), + referer: request.headers.get('referer'), + ip: request.headers.get('x-forwarded-for') + }) + }).catch(console.error); +} + +export const config = { + matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'] +}; +``` + +--- + +## Commission Calculation + +```typescript +// lib/affiliate/commission.ts +import { db } from '@/lib/db'; +import { Decimal } from '@prisma/client/runtime/library'; + +interface CommissionResult { + amount: number; + rate: Decimal; + type: CommissionType; +} + +// Tier-based commission rates +const TIER_RATES: Record<AffiliateTier, number> = { + STANDARD: 20, + SILVER: 25, + GOLD: 30, + PLATINUM: 35 +}; + +export function calculateCommission( + orderAmount: number, + affiliate: Affiliate +): CommissionResult { + let rate: Decimal; + let amount: number; + + switch (affiliate.commissionType) { + case 'PERCENTAGE': + rate = affiliate.tier + ? new Decimal(TIER_RATES[affiliate.tier]) + : affiliate.commissionRate; + amount = Math.floor(orderAmount * rate.toNumber() / 100); + break; + + case 'FIXED': + rate = affiliate.commissionRate; + amount = rate.toNumber() * 100; // Convert to cents + break; + + case 'TIERED': + const tierResult = calculateTieredCommission(orderAmount, affiliate); + rate = tierResult.rate; + amount = tierResult.amount; + break; + + default: + rate = new Decimal(20); + amount = Math.floor(orderAmount * 0.2); + } + + return { + amount, + rate, + type: affiliate.commissionType + }; +} + +function calculateTieredCommission( + orderAmount: number, + affiliate: Affiliate +): { amount: number; rate: Decimal } { + // Get affiliate's total conversions this month + const monthStart = new Date(); + monthStart.setDate(1); + monthStart.setHours(0, 0, 0, 0); + + // Tiered rates based on monthly volume + const tiers = [ + { threshold: 10000_00, rate: 20 }, // $10k+ = 20% + { threshold: 5000_00, rate: 25 }, // $5k+ = 25% + { threshold: 1000_00, rate: 30 }, // $1k+ = 30% + { threshold: 0, rate: 35 } // Base = 35% + ]; + + // For simplicity, use the affiliate's total earnings to determine tier + const volumeTier = tiers.find(t => affiliate.totalEarnings >= t.threshold); + const rate = volumeTier?.rate || 20; + + return { + amount: Math.floor(orderAmount * rate / 100), + rate: new Decimal(rate) + }; +} + +// Calculate recurring commission for subscriptions +export function calculateRecurringCommission( + subscriptionAmount: number, + affiliate: Affiliate, + month: number +): CommissionResult | null { + // Most programs cap recurring at 12 months + if (month > 12) return null; + + // Recurring rate might be lower than initial + const recurringRate = affiliate.recurringRate || affiliate.commissionRate; + const amount = Math.floor(subscriptionAmount * recurringRate.toNumber() / 100); + + return { + amount, + rate: recurringRate, + type: 'PERCENTAGE' + }; +} +``` + +--- + +## Conversion Recording + +```typescript +// lib/affiliate/conversions.ts +import { db } from '@/lib/db'; +import { calculateCommission } from './commission'; +import { getAffiliateFromCookie } from './tracking'; + +interface RecordConversionParams { + orderId: string; + customerId: string; + orderAmount: number; + currency?: string; + isSubscription?: boolean; +} + +export async function recordConversion( + params: RecordConversionParams +): Promise<Conversion | null> { + const { orderId, customerId, orderAmount, currency = 'EUR', isSubscription } = params; + + // Check for existing conversion (prevent duplicates) + const existing = await db.conversion.findUnique({ + where: { orderId } + }); + + if (existing) return existing; + + // Get affiliate from cookie or customer's first touch + const affiliateCode = getAffiliateFromCookie(); + if (!affiliateCode) return null; + + const affiliate = await db.affiliate.findUnique({ + where: { code: affiliateCode, status: 'ACTIVE' } + }); + + if (!affiliate) return null; + + // Calculate commission + const commission = calculateCommission(orderAmount, affiliate); + + // Find attributable click + const click = await db.click.findFirst({ + where: { + affiliateId: affiliate.id, + converted: false, + createdAt: { + gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // 30 days + } + }, + orderBy: { createdAt: 'desc' } + }); + + // Create conversion + const conversion = await db.$transaction(async (tx) => { + // Create conversion record + const conv = await tx.conversion.create({ + data: { + affiliateId: affiliate.id, + orderId, + customerId, + orderAmount, + currency, + commissionAmount: commission.amount, + commissionRate: commission.rate, + commissionType: commission.type, + clickId: click?.id, + attributionWindow: 30, + isRecurring: isSubscription + } + }); + + // Mark click as converted + if (click) { + await tx.click.update({ + where: { id: click.id }, + data: { converted: true, conversionId: conv.id } + }); + } + + // Update affiliate stats + await tx.affiliate.update({ + where: { id: affiliate.id }, + data: { + totalConversions: { increment: 1 }, + pendingBalance: { increment: commission.amount } + } + }); + + return conv; + }); + + return conversion; +} + +// Auto-approve conversions after hold period +export async function approveConversions(): Promise<number> { + const holdPeriod = 14; // days + const cutoffDate = new Date(); + cutoffDate.setDate(cutoffDate.getDate() - holdPeriod); + + const pendingConversions = await db.conversion.findMany({ + where: { + status: 'PENDING', + createdAt: { lte: cutoffDate } + } + }); + + let approved = 0; + + for (const conversion of pendingConversions) { + // Check if order was refunded + const wasRefunded = await checkOrderRefunded(conversion.orderId); + + if (wasRefunded) { + await db.conversion.update({ + where: { id: conversion.id }, + data: { + status: 'REJECTED', + rejectedAt: new Date(), + rejectionReason: 'Order refunded' + } + }); + + // Reduce pending balance + await db.affiliate.update({ + where: { id: conversion.affiliateId }, + data: { pendingBalance: { decrement: conversion.commissionAmount } } + }); + } else { + await db.conversion.update({ + where: { id: conversion.id }, + data: { + status: 'APPROVED', + approvedAt: new Date() + } + }); + + // Move from pending to earned + await db.affiliate.update({ + where: { id: conversion.affiliateId }, + data: { + pendingBalance: { decrement: conversion.commissionAmount }, + totalEarnings: { increment: conversion.commissionAmount } + } + }); + + approved++; + } + } + + return approved; +} + +async function checkOrderRefunded(orderId: string): Promise<boolean> { + // Check with payment provider + const order = await db.order.findUnique({ + where: { id: orderId } + }); + return order?.status === 'REFUNDED'; +} +``` + +--- + +## Payout System + +```typescript +// lib/affiliate/payouts.ts +import { db } from '@/lib/db'; +import Stripe from 'stripe'; + +const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); + +interface ProcessPayoutsResult { + processed: number; + failed: number; + totalAmount: number; +} + +export async function processPayouts(): Promise<ProcessPayoutsResult> { + const result = { processed: 0, failed: 0, totalAmount: 0 }; + + // Find affiliates with approved conversions ready for payout + const affiliatesWithBalance = await db.affiliate.findMany({ + where: { + status: 'ACTIVE', + totalEarnings: { gte: db.affiliate.fields.minimumPayout } + }, + include: { + conversions: { + where: { + status: 'APPROVED', + payoutId: null + } + } + } + }); + + for (const affiliate of affiliatesWithBalance) { + if (affiliate.conversions.length === 0) continue; + + const payoutAmount = affiliate.conversions.reduce( + (sum, c) => sum + c.commissionAmount, + 0 + ); + + if (payoutAmount < affiliate.minimumPayout) continue; + + try { + const payout = await createPayout(affiliate, payoutAmount); + + // Link conversions to payout + await db.conversion.updateMany({ + where: { + id: { in: affiliate.conversions.map(c => c.id) } + }, + data: { + payoutId: payout.id, + status: 'PAID', + paidAt: new Date() + } + }); + + // Reset affiliate balance + await db.affiliate.update({ + where: { id: affiliate.id }, + data: { totalEarnings: { decrement: payoutAmount } } + }); + + result.processed++; + result.totalAmount += payoutAmount; + + } catch (error) { + console.error(`Payout failed for ${affiliate.id}:`, error); + result.failed++; + } + } + + return result; +} + +async function createPayout( + affiliate: Affiliate, + amount: number +): Promise<Payout> { + // Create payout record + const payout = await db.payout.create({ + data: { + affiliateId: affiliate.id, + amount, + currency: 'EUR', + method: affiliate.payoutMethod, + status: 'PROCESSING' + } + }); + + try { + switch (affiliate.payoutMethod) { + case 'STRIPE': + await processStripePayout(affiliate, payout); + break; + case 'PAYPAL': + await processPayPalPayout(affiliate, payout); + break; + default: + throw new Error('Unsupported payout method'); + } + + await db.payout.update({ + where: { id: payout.id }, + data: { + status: 'COMPLETED', + processedAt: new Date() + } + }); + + } catch (error) { + await db.payout.update({ + where: { id: payout.id }, + data: { + status: 'FAILED', + failedAt: new Date(), + failureReason: error instanceof Error ? error.message : 'Unknown error' + } + }); + throw error; + } + + return payout; +} + +async function processStripePayout( + affiliate: Affiliate, + payout: Payout +): Promise<void> { + if (!affiliate.stripeAccountId) { + throw new Error('No Stripe account connected'); + } + + const transfer = await stripe.transfers.create({ + amount: payout.amount, + currency: payout.currency.toLowerCase(), + destination: affiliate.stripeAccountId, + metadata: { + payoutId: payout.id, + affiliateId: affiliate.id + } + }); + + await db.payout.update({ + where: { id: payout.id }, + data: { stripeTransferId: transfer.id } + }); +} + +async function processPayPalPayout( + affiliate: Affiliate, + payout: Payout +): Promise<void> { + if (!affiliate.paypalEmail) { + throw new Error('No PayPal email configured'); + } + + // PayPal Payouts API integration + const paypalPayout = await createPayPalPayout({ + email: affiliate.paypalEmail, + amount: (payout.amount / 100).toFixed(2), + currency: payout.currency, + note: `Affiliate commission payout #${payout.id}` + }); + + await db.payout.update({ + where: { id: payout.id }, + data: { paypalPayoutId: paypalPayout.batch_id } + }); +} +``` + +--- + +## Customer Referral System + +```typescript +// lib/referral/referrals.ts +import { db } from '@/lib/db'; +import crypto from 'crypto'; + +const REFERRAL_REWARDS = { + referrer: 1000, // $10 credit + referred: 500 // $5 credit +}; + +export async function createReferralCode(userId: string): Promise<string> { + const existing = await db.referral.findFirst({ + where: { referrerId: userId } + }); + + if (existing) return existing.code; + + const code = crypto.randomBytes(4).toString('hex').toUpperCase(); + + await db.referral.create({ + data: { + referrerId: userId, + code, + status: 'PENDING' + } + }); + + return code; +} + +export async function processReferralSignup( + newUserId: string, + referralCode: string +): Promise<boolean> { + const referral = await db.referral.findUnique({ + where: { code: referralCode } + }); + + if (!referral || referral.status !== 'PENDING') { + return false; + } + + // Prevent self-referral + if (referral.referrerId === newUserId) { + return false; + } + + await db.referral.update({ + where: { id: referral.id }, + data: { + referredId: newUserId, + status: 'SIGNED_UP', + signedUpAt: new Date() + } + }); + + return true; +} + +export async function qualifyReferral( + referredUserId: string +): Promise<void> { + const referral = await db.referral.findFirst({ + where: { + referredId: referredUserId, + status: 'SIGNED_UP' + } + }); + + if (!referral) return; + + await db.$transaction([ + // Update referral status + db.referral.update({ + where: { id: referral.id }, + data: { + status: 'QUALIFIED', + qualifiedAt: new Date(), + referrerReward: REFERRAL_REWARDS.referrer, + referredReward: REFERRAL_REWARDS.referred + } + }), + + // Credit referrer + db.user.update({ + where: { id: referral.referrerId }, + data: { credit: { increment: REFERRAL_REWARDS.referrer } } + }), + + // Credit referred user + db.user.update({ + where: { id: referredUserId }, + data: { credit: { increment: REFERRAL_REWARDS.referred } } + }) + ]); + + // Send notifications + await notifyReferralReward(referral.referrerId, REFERRAL_REWARDS.referrer); + await notifyReferralReward(referredUserId, REFERRAL_REWARDS.referred); +} +``` + +--- + +## Affiliate Dashboard API + +```typescript +// app/api/affiliate/dashboard/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { db } from '@/lib/db'; +import { auth } from '@/lib/auth'; + +export async function GET(request: NextRequest) { + const session = await auth(); + if (!session?.user) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const affiliate = await db.affiliate.findUnique({ + where: { userId: session.user.id } + }); + + if (!affiliate) { + return NextResponse.json({ error: 'Not an affiliate' }, { status: 404 }); + } + + // Get date range + const { searchParams } = new URL(request.url); + const range = searchParams.get('range') || '30d'; + const startDate = getStartDate(range); + + // Fetch stats + const [clicks, conversions, recentConversions] = await Promise.all([ + db.click.count({ + where: { + affiliateId: affiliate.id, + createdAt: { gte: startDate } + } + }), + + db.conversion.aggregate({ + where: { + affiliateId: affiliate.id, + createdAt: { gte: startDate } + }, + _count: true, + _sum: { commissionAmount: true, orderAmount: true } + }), + + db.conversion.findMany({ + where: { affiliateId: affiliate.id }, + orderBy: { createdAt: 'desc' }, + take: 10, + select: { + id: true, + orderAmount: true, + commissionAmount: true, + status: true, + createdAt: true + } + }) + ]); + + // Calculate conversion rate + const conversionRate = clicks > 0 + ? ((conversions._count || 0) / clicks * 100).toFixed(2) + : '0.00'; + + return NextResponse.json({ + affiliate: { + code: affiliate.code, + tier: affiliate.tier, + commissionRate: affiliate.commissionRate, + status: affiliate.status + }, + stats: { + clicks, + conversions: conversions._count || 0, + conversionRate: `${conversionRate}%`, + revenue: conversions._sum.orderAmount || 0, + earnings: conversions._sum.commissionAmount || 0, + pendingBalance: affiliate.pendingBalance, + totalEarnings: affiliate.totalEarnings + }, + recentConversions, + links: { + main: `${process.env.NEXT_PUBLIC_APP_URL}?ref=${affiliate.code}`, + pricing: `${process.env.NEXT_PUBLIC_APP_URL}/pricing?ref=${affiliate.code}` + } + }); +} + +function getStartDate(range: string): Date { + const now = new Date(); + switch (range) { + case '7d': return new Date(now.setDate(now.getDate() - 7)); + case '30d': return new Date(now.setDate(now.getDate() - 30)); + case '90d': return new Date(now.setDate(now.getDate() - 90)); + case 'ytd': return new Date(now.getFullYear(), 0, 1); + default: return new Date(now.setDate(now.getDate() - 30)); + } +} +``` + +--- + +## Best Practices + +| Aspect | Recommendation | +|--------|----------------| +| **Attribution** | 30-90 day cookie window | +| **Hold Period** | 14-30 days before approval | +| **Commission** | 20-50% for digital products | +| **Minimum Payout** | $50-100 threshold | +| **Fraud Prevention** | IP tracking, refund checks | +| **Communication** | Real-time dashboard, email alerts | + +--- + +## Fazit + +Affiliate & Referral Systems erfordern: + +1. **Tracking**: Cookies, Attribution-Windows +2. **Commission**: Faire, klare Berechnung +3. **Payouts**: Automatisiert, zuverlässig +4. **Dashboard**: Transparenz für Partner + +Gute Partner-Programme wachsen organisch durch zufriedene Affiliates. + +--- + +## Bildprompts + +1. "Affiliate dashboard interface, earnings chart and conversion metrics" +2. "Referral flow diagram, invite friend reward visualization" +3. "Commission payout system, money transfer illustration" + +--- + +## Quellen + +- [Stripe Connect for Marketplaces](https://stripe.com/docs/connect) +- [First Promoter](https://firstpromoter.com/) +- [Rewardful API](https://www.rewardful.com/) +- [Affiliate Marketing Best Practices](https://www.refersion.com/blog/affiliate-marketing-best-practices/) diff --git a/dev-dist/registerSW.js b/dev-dist/registerSW.js deleted file mode 100644 index 1d5625f..0000000 --- a/dev-dist/registerSW.js +++ /dev/null @@ -1 +0,0 @@ -if('serviceWorker' in navigator) navigator.serviceWorker.register('/dev-sw.js?dev-sw', { scope: '/', type: 'classic' }) \ No newline at end of file diff --git a/dev-dist/sw.js b/dev-dist/sw.js deleted file mode 100644 index f0c6828..0000000 --- a/dev-dist/sw.js +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright 2018 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// If the loader is already loaded, just stop. -if (!self.define) { - let registry = {}; - - // Used for `eval` and `importScripts` where we can't get script URL by other means. - // In both cases, it's safe to use a global var because those functions are synchronous. - let nextDefineUri; - - const singleRequire = (uri, parentUri) => { - uri = new URL(uri + ".js", parentUri).href; - return registry[uri] || ( - - new Promise(resolve => { - if ("document" in self) { - const script = document.createElement("script"); - script.src = uri; - script.onload = resolve; - document.head.appendChild(script); - } else { - nextDefineUri = uri; - importScripts(uri); - resolve(); - } - }) - - .then(() => { - let promise = registry[uri]; - if (!promise) { - throw new Error(`Module ${uri} didn’t register its module`); - } - return promise; - }) - ); - }; - - self.define = (depsNames, factory) => { - const uri = nextDefineUri || ("document" in self ? document.currentScript.src : "") || location.href; - if (registry[uri]) { - // Module is already loading or loaded. - return; - } - let exports = {}; - const require = depUri => singleRequire(depUri, uri); - const specialDeps = { - module: { uri }, - exports, - require - }; - registry[uri] = Promise.all(depsNames.map( - depName => specialDeps[depName] || require(depName) - )).then(deps => { - factory(...deps); - return exports; - }); - }; -} -define(['./workbox-6f774a69'], (function (workbox) { 'use strict'; - - self.addEventListener('message', event => { - if (event.data && event.data.type === 'SKIP_WAITING') { - self.skipWaiting(); - } - }); - - /** - * The precacheAndRoute() method efficiently caches and responds to - * requests for URLs in the manifest. - * See https://goo.gl/S9QRab - */ - workbox.precacheAndRoute([{ - "url": "registerSW.js", - "revision": "3ca0b8505b4bec776b69afdba2768812" - }, { - "url": "index.html", - "revision": "0.7vq3026gg5" - }], {}); - workbox.cleanupOutdatedCaches(); - workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { - allowlist: [/^\/$/] - })); - workbox.registerRoute(/^https:\/\/fonts\.googleapis\.com\/.*/i, new workbox.CacheFirst({ - "cacheName": "google-fonts-cache", - plugins: [new workbox.CacheableResponsePlugin({ - statuses: [0, 200] - })] - }), 'GET'); - workbox.registerRoute(/^https:\/\/fonts\.gstatic\.com\/.*/i, new workbox.CacheFirst({ - "cacheName": "gstatic-fonts-cache", - plugins: [new workbox.CacheableResponsePlugin({ - statuses: [0, 200] - })] - }), 'GET'); - workbox.registerRoute(/^https:\/\/www\.googletagmanager\.com\/.*/i, new workbox.NetworkOnly(), 'GET'); - -})); -//# sourceMappingURL=sw.js.map diff --git a/dev-dist/sw.js.map b/dev-dist/sw.js.map deleted file mode 100644 index 99d3c28..0000000 --- a/dev-dist/sw.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"sw.js","sources":["../../AppData/Local/Temp/f1fc38369a91c8b5f38b6bfa3fa44690/sw.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from 'C:/Users/damja/WebstormProjects/Portfolio/node_modules/workbox-routing/registerRoute.mjs';\nimport {CacheableResponsePlugin as workbox_cacheable_response_CacheableResponsePlugin} from 'C:/Users/damja/WebstormProjects/Portfolio/node_modules/workbox-cacheable-response/CacheableResponsePlugin.mjs';\nimport {CacheFirst as workbox_strategies_CacheFirst} from 'C:/Users/damja/WebstormProjects/Portfolio/node_modules/workbox-strategies/CacheFirst.mjs';\nimport {NetworkOnly as workbox_strategies_NetworkOnly} from 'C:/Users/damja/WebstormProjects/Portfolio/node_modules/workbox-strategies/NetworkOnly.mjs';\nimport {precacheAndRoute as workbox_precaching_precacheAndRoute} from 'C:/Users/damja/WebstormProjects/Portfolio/node_modules/workbox-precaching/precacheAndRoute.mjs';\nimport {cleanupOutdatedCaches as workbox_precaching_cleanupOutdatedCaches} from 'C:/Users/damja/WebstormProjects/Portfolio/node_modules/workbox-precaching/cleanupOutdatedCaches.mjs';\nimport {NavigationRoute as workbox_routing_NavigationRoute} from 'C:/Users/damja/WebstormProjects/Portfolio/node_modules/workbox-routing/NavigationRoute.mjs';\nimport {createHandlerBoundToURL as workbox_precaching_createHandlerBoundToURL} from 'C:/Users/damja/WebstormProjects/Portfolio/node_modules/workbox-precaching/createHandlerBoundToURL.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\n\n\n\n\nself.addEventListener('message', (event) => {\n if (event.data && event.data.type === 'SKIP_WAITING') {\n self.skipWaiting();\n }\n});\n\n\n\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"registerSW.js\",\n \"revision\": \"3ca0b8505b4bec776b69afdba2768812\"\n },\n {\n \"url\": \"index.html\",\n \"revision\": \"0.7vq3026gg5\"\n }\n], {});\nworkbox_precaching_cleanupOutdatedCaches();\nworkbox_routing_registerRoute(new workbox_routing_NavigationRoute(workbox_precaching_createHandlerBoundToURL(\"index.html\"), {\n allowlist: [/^\\/$/],\n \n}));\n\n\nworkbox_routing_registerRoute(/^https:\\/\\/fonts\\.googleapis\\.com\\/.*/i, new workbox_strategies_CacheFirst({ \"cacheName\":\"google-fonts-cache\", plugins: [new workbox_cacheable_response_CacheableResponsePlugin({ statuses: [ 0, 200 ] })] }), 'GET');\nworkbox_routing_registerRoute(/^https:\\/\\/fonts\\.gstatic\\.com\\/.*/i, new workbox_strategies_CacheFirst({ \"cacheName\":\"gstatic-fonts-cache\", plugins: [new workbox_cacheable_response_CacheableResponsePlugin({ statuses: [ 0, 200 ] })] }), 'GET');\nworkbox_routing_registerRoute(/^https:\\/\\/www\\.googletagmanager\\.com\\/.*/i, new workbox_strategies_NetworkOnly(), 'GET');\n\n\n\n\n"],"names":["self","addEventListener","event","data","type","skipWaiting","workbox_precaching_precacheAndRoute","workbox_precaching_cleanupOutdatedCaches","workbox_routing_registerRoute","workbox_routing_NavigationRoute","workbox_precaching_createHandlerBoundToURL","allowlist","workbox_strategies_CacheFirst","plugins","workbox_cacheable_response_CacheableResponsePlugin","statuses","workbox_strategies_NetworkOnly"],"mappings":"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"} \ No newline at end of file diff --git a/dev-dist/workbox-4a2e5f00.js b/dev-dist/workbox-4a2e5f00.js deleted file mode 100644 index 73b7e9f..0000000 --- a/dev-dist/workbox-4a2e5f00.js +++ /dev/null @@ -1,3749 +0,0 @@ -define(['exports'], (function (exports) { 'use strict'; - - // @ts-ignore - try { - self['workbox:core:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const logger = (() => { - // Don't overwrite this value if it's already set. - // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923 - if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) { - self.__WB_DISABLE_DEV_LOGS = false; - } - let inGroup = false; - const methodToColorMap = { - debug: `#7f8c8d`, - log: `#2ecc71`, - warn: `#f39c12`, - error: `#c0392b`, - groupCollapsed: `#3498db`, - groupEnd: null // No colored prefix on groupEnd - }; - const print = function (method, args) { - if (self.__WB_DISABLE_DEV_LOGS) { - return; - } - if (method === 'groupCollapsed') { - // Safari doesn't print all console.groupCollapsed() arguments: - // https://bugs.webkit.org/show_bug.cgi?id=182754 - if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - console[method](...args); - return; - } - } - const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`]; - // When in a group, the workbox prefix is not displayed. - const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')]; - console[method](...logPrefix, ...args); - if (method === 'groupCollapsed') { - inGroup = true; - } - if (method === 'groupEnd') { - inGroup = false; - } - }; - // eslint-disable-next-line @typescript-eslint/ban-types - const api = {}; - const loggerMethods = Object.keys(methodToColorMap); - for (const key of loggerMethods) { - const method = key; - api[method] = (...args) => { - print(method, args); - }; - } - return api; - })(); - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const messages$1 = { - 'invalid-value': ({ - paramName, - validValueDescription, - value - }) => { - if (!paramName || !validValueDescription) { - throw new Error(`Unexpected input to 'invalid-value' error.`); - } - return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`; - }, - 'not-an-array': ({ - moduleName, - className, - funcName, - paramName - }) => { - if (!moduleName || !className || !funcName || !paramName) { - throw new Error(`Unexpected input to 'not-an-array' error.`); - } - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`; - }, - 'incorrect-type': ({ - expectedType, - paramName, - moduleName, - className, - funcName - }) => { - if (!expectedType || !paramName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'incorrect-type' error.`); - } - const classNameStr = className ? `${className}.` : ''; - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}` + `${funcName}()' must be of type ${expectedType}.`; - }, - 'incorrect-class': ({ - expectedClassName, - paramName, - moduleName, - className, - funcName, - isReturnValueProblem - }) => { - if (!expectedClassName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'incorrect-class' error.`); - } - const classNameStr = className ? `${className}.` : ''; - if (isReturnValueProblem) { - return `The return value from ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`; - } - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`; - }, - 'missing-a-method': ({ - expectedMethod, - paramName, - moduleName, - className, - funcName - }) => { - if (!expectedMethod || !paramName || !moduleName || !className || !funcName) { - throw new Error(`Unexpected input to 'missing-a-method' error.`); - } - return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`; - }, - 'add-to-cache-list-unexpected-type': ({ - entry - }) => { - return `An unexpected entry was passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` + `strings with one or more characters, objects with a url property or ` + `Request objects.`; - }, - 'add-to-cache-list-conflicting-entries': ({ - firstEntry, - secondEntry - }) => { - if (!firstEntry || !secondEntry) { - throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`); - } - return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${firstEntry} but different revision details. Workbox is ` + `unable to cache and version the asset correctly. Please remove one ` + `of the entries.`; - }, - 'plugin-error-request-will-fetch': ({ - thrownErrorMessage - }) => { - if (!thrownErrorMessage) { - throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`); - } - return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownErrorMessage}'.`; - }, - 'invalid-cache-name': ({ - cacheNameId, - value - }) => { - if (!cacheNameId) { - throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`); - } - return `You must provide a name containing at least one character for ` + `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`; - }, - 'unregister-route-but-not-found-with-method': ({ - method - }) => { - if (!method) { - throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`); - } - return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`; - }, - 'unregister-route-route-not-registered': () => { - return `The route you're trying to unregister was not previously ` + `registered.`; - }, - 'queue-replay-failed': ({ - name - }) => { - return `Replaying the background sync queue '${name}' failed.`; - }, - 'duplicate-queue-name': ({ - name - }) => { - return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`; - }, - 'expired-test-without-max-age': ({ - methodName, - paramName - }) => { - return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`; - }, - 'unsupported-route-type': ({ - moduleName, - className, - funcName, - paramName - }) => { - return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`; - }, - 'not-array-of-class': ({ - value, - expectedClass, - moduleName, - className, - funcName, - paramName - }) => { - return `The supplied '${paramName}' parameter must be an array of ` + `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` + `Please check the call to ${moduleName}.${className}.${funcName}() ` + `to fix the issue.`; - }, - 'max-entries-or-age-required': ({ - moduleName, - className, - funcName - }) => { - return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`; - }, - 'statuses-or-headers-required': ({ - moduleName, - className, - funcName - }) => { - return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`; - }, - 'invalid-string': ({ - moduleName, - funcName, - paramName - }) => { - if (!paramName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'invalid-string' error.`); - } - return `When using strings, the '${paramName}' parameter must start with ` + `'http' (for cross-origin matches) or '/' (for same-origin matches). ` + `Please see the docs for ${moduleName}.${funcName}() for ` + `more info.`; - }, - 'channel-name-required': () => { - return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`; - }, - 'invalid-responses-are-same-args': () => { - return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`; - }, - 'expire-custom-caches-only': () => { - return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`; - }, - 'unit-must-be-bytes': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`); - } - return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`; - }, - 'single-range-only': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'single-range-only' error.`); - } - return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`; - }, - 'invalid-range-values': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'invalid-range-values' error.`); - } - return `The Range header is missing both start and end values. At least ` + `one of those values is needed. The Range header provided was ` + `"${normalizedRangeHeader}"`; - }, - 'no-range-header': () => { - return `No Range header was found in the Request provided.`; - }, - 'range-not-satisfiable': ({ - size, - start, - end - }) => { - return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`; - }, - 'attempt-to-cache-non-get-request': ({ - url, - method - }) => { - return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`; - }, - 'cache-put-with-no-response': ({ - url - }) => { - return `There was an attempt to cache '${url}' but the response was not ` + `defined.`; - }, - 'no-response': ({ - url, - error - }) => { - let message = `The strategy could not generate a response for '${url}'.`; - if (error) { - message += ` The underlying error is ${error}.`; - } - return message; - }, - 'bad-precaching-response': ({ - url, - status - }) => { - return `The precaching request for '${url}' failed` + (status ? ` with an HTTP status of ${status}.` : `.`); - }, - 'non-precached-url': ({ - url - }) => { - return `createHandlerBoundToURL('${url}') was called, but that URL is ` + `not precached. Please pass in a URL that is precached instead.`; - }, - 'add-to-cache-list-conflicting-integrities': ({ - url - }) => { - return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${url} with different integrity values. Please remove one of them.`; - }, - 'missing-precache-entry': ({ - cacheName, - url - }) => { - return `Unable to find a precached response in ${cacheName} for ${url}.`; - }, - 'cross-origin-copy-response': ({ - origin - }) => { - return `workbox-core.copyResponse() can only be used with same-origin ` + `responses. It was passed a response with origin ${origin}.`; - }, - 'opaque-streams-source': ({ - type - }) => { - const message = `One of the workbox-streams sources resulted in an ` + `'${type}' response.`; - if (type === 'opaqueredirect') { - return `${message} Please do not use a navigation request that results ` + `in a redirect as a source.`; - } - return `${message} Please ensure your sources are CORS-enabled.`; - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const generatorFunction = (code, details = {}) => { - const message = messages$1[code]; - if (!message) { - throw new Error(`Unable to find message for code '${code}'.`); - } - return message(details); - }; - const messageGenerator = generatorFunction; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Workbox errors should be thrown with this class. - * This allows use to ensure the type easily in tests, - * helps developers identify errors from workbox - * easily and allows use to optimise error - * messages correctly. - * - * @private - */ - class WorkboxError extends Error { - /** - * - * @param {string} errorCode The error code that - * identifies this particular error. - * @param {Object=} details Any relevant arguments - * that will help developers identify issues should - * be added as a key on the context object. - */ - constructor(errorCode, details) { - const message = messageGenerator(errorCode, details); - super(message); - this.name = errorCode; - this.details = details; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /* - * This method throws if the supplied value is not an array. - * The destructed values are required to produce a meaningful error for users. - * The destructed and restructured object is so it's clear what is - * needed. - */ - const isArray = (value, details) => { - if (!Array.isArray(value)) { - throw new WorkboxError('not-an-array', details); - } - }; - const hasMethod = (object, expectedMethod, details) => { - const type = typeof object[expectedMethod]; - if (type !== 'function') { - details['expectedMethod'] = expectedMethod; - throw new WorkboxError('missing-a-method', details); - } - }; - const isType = (object, expectedType, details) => { - if (typeof object !== expectedType) { - details['expectedType'] = expectedType; - throw new WorkboxError('incorrect-type', details); - } - }; - const isInstance = (object, - // Need the general type to do the check later. - // eslint-disable-next-line @typescript-eslint/ban-types - expectedClass, details) => { - if (!(object instanceof expectedClass)) { - details['expectedClassName'] = expectedClass.name; - throw new WorkboxError('incorrect-class', details); - } - }; - const isOneOf = (value, validValues, details) => { - if (!validValues.includes(value)) { - details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`; - throw new WorkboxError('invalid-value', details); - } - }; - const isArrayOfClass = (value, - // Need general type to do check later. - expectedClass, - // eslint-disable-line - details) => { - const error = new WorkboxError('not-array-of-class', details); - if (!Array.isArray(value)) { - throw error; - } - for (const item of value) { - if (!(item instanceof expectedClass)) { - throw error; - } - } - }; - const finalAssertExports = { - hasMethod, - isArray, - isInstance, - isOneOf, - isType, - isArrayOfClass - }; - - // @ts-ignore - try { - self['workbox:routing:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The default HTTP method, 'GET', used when there's no specific method - * configured for a route. - * - * @type {string} - * - * @private - */ - const defaultMethod = 'GET'; - /** - * The list of valid HTTP methods associated with requests that could be routed. - * - * @type {Array<string>} - * - * @private - */ - const validMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {function()|Object} handler Either a function, or an object with a - * 'handle' method. - * @return {Object} An object with a handle method. - * - * @private - */ - const normalizeHandler = handler => { - if (handler && typeof handler === 'object') { - { - finalAssertExports.hasMethod(handler, 'handle', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'handler' - }); - } - return handler; - } else { - { - finalAssertExports.isType(handler, 'function', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'handler' - }); - } - return { - handle: handler - }; - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A `Route` consists of a pair of callback functions, "match" and "handler". - * The "match" callback determine if a route should be used to "handle" a - * request by returning a non-falsy value if it can. The "handler" callback - * is called when there is a match and should return a Promise that resolves - * to a `Response`. - * - * @memberof workbox-routing - */ - class Route { - /** - * Constructor for Route class. - * - * @param {workbox-routing~matchCallback} match - * A callback function that determines whether the route matches a given - * `fetch` event by returning a non-falsy value. - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resolving to a Response. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - */ - constructor(match, handler, method = defaultMethod) { - { - finalAssertExports.isType(match, 'function', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'match' - }); - if (method) { - finalAssertExports.isOneOf(method, validMethods, { - paramName: 'method' - }); - } - } - // These values are referenced directly by Router so cannot be - // altered by minificaton. - this.handler = normalizeHandler(handler); - this.match = match; - this.method = method; - } - /** - * - * @param {workbox-routing-handlerCallback} handler A callback - * function that returns a Promise resolving to a Response - */ - setCatchHandler(handler) { - this.catchHandler = normalizeHandler(handler); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * RegExpRoute makes it easy to create a regular expression based - * {@link workbox-routing.Route}. - * - * For same-origin requests the RegExp only needs to match part of the URL. For - * requests against third-party servers, you must define a RegExp that matches - * the start of the URL. - * - * @memberof workbox-routing - * @extends workbox-routing.Route - */ - class RegExpRoute extends Route { - /** - * If the regular expression contains - * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references}, - * the captured values will be passed to the - * {@link workbox-routing~handlerCallback} `params` - * argument. - * - * @param {RegExp} regExp The regular expression to match against URLs. - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - */ - constructor(regExp, handler, method) { - { - finalAssertExports.isInstance(regExp, RegExp, { - moduleName: 'workbox-routing', - className: 'RegExpRoute', - funcName: 'constructor', - paramName: 'pattern' - }); - } - const match = ({ - url - }) => { - const result = regExp.exec(url.href); - // Return immediately if there's no match. - if (!result) { - return; - } - // Require that the match start at the first character in the URL string - // if it's a cross-origin request. - // See https://github.com/GoogleChrome/workbox/issues/281 for the context - // behind this behavior. - if (url.origin !== location.origin && result.index !== 0) { - { - logger.debug(`The regular expression '${regExp.toString()}' only partially matched ` + `against the cross-origin URL '${url.toString()}'. RegExpRoute's will only ` + `handle cross-origin requests if they match the entire URL.`); - } - return; - } - // If the route matches, but there aren't any capture groups defined, then - // this will return [], which is truthy and therefore sufficient to - // indicate a match. - // If there are capture groups, then it will return their values. - return result.slice(1); - }; - super(match, handler, method); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const getFriendlyURL = url => { - const urlObj = new URL(String(url), location.href); - // See https://github.com/GoogleChrome/workbox/issues/2323 - // We want to include everything, except for the origin if it's same-origin. - return urlObj.href.replace(new RegExp(`^${location.origin}`), ''); - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The Router can be used to process a `FetchEvent` using one or more - * {@link workbox-routing.Route}, responding with a `Response` if - * a matching route exists. - * - * If no route matches a given a request, the Router will use a "default" - * handler if one is defined. - * - * Should the matching Route throw an error, the Router will use a "catch" - * handler if one is defined to gracefully deal with issues and respond with a - * Request. - * - * If a request matches multiple routes, the **earliest** registered route will - * be used to respond to the request. - * - * @memberof workbox-routing - */ - class Router { - /** - * Initializes a new Router. - */ - constructor() { - this._routes = new Map(); - this._defaultHandlerMap = new Map(); - } - /** - * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP - * method name ('GET', etc.) to an array of all the corresponding `Route` - * instances that are registered. - */ - get routes() { - return this._routes; - } - /** - * Adds a fetch event listener to respond to events when a route matches - * the event's request. - */ - addFetchListener() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('fetch', event => { - const { - request - } = event; - const responsePromise = this.handleRequest({ - request, - event - }); - if (responsePromise) { - event.respondWith(responsePromise); - } - }); - } - /** - * Adds a message event listener for URLs to cache from the window. - * This is useful to cache resources loaded on the page prior to when the - * service worker started controlling it. - * - * The format of the message data sent from the window should be as follows. - * Where the `urlsToCache` array may consist of URL strings or an array of - * URL string + `requestInit` object (the same as you'd pass to `fetch()`). - * - * ``` - * { - * type: 'CACHE_URLS', - * payload: { - * urlsToCache: [ - * './script1.js', - * './script2.js', - * ['./script3.js', {mode: 'no-cors'}], - * ], - * }, - * } - * ``` - */ - addCacheListener() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('message', event => { - // event.data is type 'any' - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (event.data && event.data.type === 'CACHE_URLS') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { - payload - } = event.data; - { - logger.debug(`Caching URLs from the window`, payload.urlsToCache); - } - const requestPromises = Promise.all(payload.urlsToCache.map(entry => { - if (typeof entry === 'string') { - entry = [entry]; - } - const request = new Request(...entry); - return this.handleRequest({ - request, - event - }); - // TODO(philipwalton): TypeScript errors without this typecast for - // some reason (probably a bug). The real type here should work but - // doesn't: `Array<Promise<Response> | undefined>`. - })); // TypeScript - event.waitUntil(requestPromises); - // If a MessageChannel was used, reply to the message on success. - if (event.ports && event.ports[0]) { - void requestPromises.then(() => event.ports[0].postMessage(true)); - } - } - }); - } - /** - * Apply the routing rules to a FetchEvent object to get a Response from an - * appropriate Route's handler. - * - * @param {Object} options - * @param {Request} options.request The request to handle. - * @param {ExtendableEvent} options.event The event that triggered the - * request. - * @return {Promise<Response>|undefined} A promise is returned if a - * registered route can handle the request. If there is no matching - * route and there's no `defaultHandler`, `undefined` is returned. - */ - handleRequest({ - request, - event - }) { - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'handleRequest', - paramName: 'options.request' - }); - } - const url = new URL(request.url, location.href); - if (!url.protocol.startsWith('http')) { - { - logger.debug(`Workbox Router only supports URLs that start with 'http'.`); - } - return; - } - const sameOrigin = url.origin === location.origin; - const { - params, - route - } = this.findMatchingRoute({ - event, - request, - sameOrigin, - url - }); - let handler = route && route.handler; - const debugMessages = []; - { - if (handler) { - debugMessages.push([`Found a route to handle this request:`, route]); - if (params) { - debugMessages.push([`Passing the following params to the route's handler:`, params]); - } - } - } - // If we don't have a handler because there was no matching route, then - // fall back to defaultHandler if that's defined. - const method = request.method; - if (!handler && this._defaultHandlerMap.has(method)) { - { - debugMessages.push(`Failed to find a matching route. Falling ` + `back to the default handler for ${method}.`); - } - handler = this._defaultHandlerMap.get(method); - } - if (!handler) { - { - // No handler so Workbox will do nothing. If logs is set of debug - // i.e. verbose, we should print out this information. - logger.debug(`No route found for: ${getFriendlyURL(url)}`); - } - return; - } - { - // We have a handler, meaning Workbox is going to handle the route. - // print the routing details to the console. - logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`); - debugMessages.forEach(msg => { - if (Array.isArray(msg)) { - logger.log(...msg); - } else { - logger.log(msg); - } - }); - logger.groupEnd(); - } - // Wrap in try and catch in case the handle method throws a synchronous - // error. It should still callback to the catch handler. - let responsePromise; - try { - responsePromise = handler.handle({ - url, - request, - event, - params - }); - } catch (err) { - responsePromise = Promise.reject(err); - } - // Get route's catch handler, if it exists - const catchHandler = route && route.catchHandler; - if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) { - responsePromise = responsePromise.catch(async err => { - // If there's a route catch handler, process that first - if (catchHandler) { - { - // Still include URL here as it will be async from the console group - // and may not make sense without the URL - logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`); - logger.error(`Error thrown by:`, route); - logger.error(err); - logger.groupEnd(); - } - try { - return await catchHandler.handle({ - url, - request, - event, - params - }); - } catch (catchErr) { - if (catchErr instanceof Error) { - err = catchErr; - } - } - } - if (this._catchHandler) { - { - // Still include URL here as it will be async from the console group - // and may not make sense without the URL - logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`); - logger.error(`Error thrown by:`, route); - logger.error(err); - logger.groupEnd(); - } - return this._catchHandler.handle({ - url, - request, - event - }); - } - throw err; - }); - } - return responsePromise; - } - /** - * Checks a request and URL (and optionally an event) against the list of - * registered routes, and if there's a match, returns the corresponding - * route along with any params generated by the match. - * - * @param {Object} options - * @param {URL} options.url - * @param {boolean} options.sameOrigin The result of comparing `url.origin` - * against the current origin. - * @param {Request} options.request The request to match. - * @param {Event} options.event The corresponding event. - * @return {Object} An object with `route` and `params` properties. - * They are populated if a matching route was found or `undefined` - * otherwise. - */ - findMatchingRoute({ - url, - sameOrigin, - request, - event - }) { - const routes = this._routes.get(request.method) || []; - for (const route of routes) { - let params; - // route.match returns type any, not possible to change right now. - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const matchResult = route.match({ - url, - sameOrigin, - request, - event - }); - if (matchResult) { - { - // Warn developers that using an async matchCallback is almost always - // not the right thing to do. - if (matchResult instanceof Promise) { - logger.warn(`While routing ${getFriendlyURL(url)}, an async ` + `matchCallback function was used. Please convert the ` + `following route to use a synchronous matchCallback function:`, route); - } - } - // See https://github.com/GoogleChrome/workbox/issues/2079 - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - params = matchResult; - if (Array.isArray(params) && params.length === 0) { - // Instead of passing an empty array in as params, use undefined. - params = undefined; - } else if (matchResult.constructor === Object && - // eslint-disable-line - Object.keys(matchResult).length === 0) { - // Instead of passing an empty object in as params, use undefined. - params = undefined; - } else if (typeof matchResult === 'boolean') { - // For the boolean value true (rather than just something truth-y), - // don't set params. - // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353 - params = undefined; - } - // Return early if have a match. - return { - route, - params - }; - } - } - // If no match was found above, return and empty object. - return {}; - } - /** - * Define a default `handler` that's called when no routes explicitly - * match the incoming request. - * - * Each HTTP method ('GET', 'POST', etc.) gets its own default handler. - * - * Without a default handler, unmatched requests will go against the - * network as if there were no service worker present. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {string} [method='GET'] The HTTP method to associate with this - * default handler. Each method has its own default. - */ - setDefaultHandler(handler, method = defaultMethod) { - this._defaultHandlerMap.set(method, normalizeHandler(handler)); - } - /** - * If a Route throws an error while handling a request, this `handler` - * will be called and given a chance to provide a response. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - */ - setCatchHandler(handler) { - this._catchHandler = normalizeHandler(handler); - } - /** - * Registers a route with the router. - * - * @param {workbox-routing.Route} route The route to register. - */ - registerRoute(route) { - { - finalAssertExports.isType(route, 'object', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.hasMethod(route, 'match', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.isType(route.handler, 'object', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.hasMethod(route.handler, 'handle', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route.handler' - }); - finalAssertExports.isType(route.method, 'string', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route.method' - }); - } - if (!this._routes.has(route.method)) { - this._routes.set(route.method, []); - } - // Give precedence to all of the earlier routes by adding this additional - // route to the end of the array. - this._routes.get(route.method).push(route); - } - /** - * Unregisters a route with the router. - * - * @param {workbox-routing.Route} route The route to unregister. - */ - unregisterRoute(route) { - if (!this._routes.has(route.method)) { - throw new WorkboxError('unregister-route-but-not-found-with-method', { - method: route.method - }); - } - const routeIndex = this._routes.get(route.method).indexOf(route); - if (routeIndex > -1) { - this._routes.get(route.method).splice(routeIndex, 1); - } else { - throw new WorkboxError('unregister-route-route-not-registered'); - } - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let defaultRouter; - /** - * Creates a new, singleton Router instance if one does not exist. If one - * does already exist, that instance is returned. - * - * @private - * @return {Router} - */ - const getOrCreateDefaultRouter = () => { - if (!defaultRouter) { - defaultRouter = new Router(); - // The helpers that use the default Router assume these listeners exist. - defaultRouter.addFetchListener(); - defaultRouter.addCacheListener(); - } - return defaultRouter; - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Easily register a RegExp, string, or function with a caching - * strategy to a singleton Router instance. - * - * This method will generate a Route for you if needed and - * call {@link workbox-routing.Router#registerRoute}. - * - * @param {RegExp|string|workbox-routing.Route~matchCallback|workbox-routing.Route} capture - * If the capture param is a `Route`, all other arguments will be ignored. - * @param {workbox-routing~handlerCallback} [handler] A callback - * function that returns a Promise resulting in a Response. This parameter - * is required if `capture` is not a `Route` object. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - * @return {workbox-routing.Route} The generated `Route`. - * - * @memberof workbox-routing - */ - function registerRoute(capture, handler, method) { - let route; - if (typeof capture === 'string') { - const captureUrl = new URL(capture, location.href); - { - if (!(capture.startsWith('/') || capture.startsWith('http'))) { - throw new WorkboxError('invalid-string', { - moduleName: 'workbox-routing', - funcName: 'registerRoute', - paramName: 'capture' - }); - } - // We want to check if Express-style wildcards are in the pathname only. - // TODO: Remove this log message in v4. - const valueToCheck = capture.startsWith('http') ? captureUrl.pathname : capture; - // See https://github.com/pillarjs/path-to-regexp#parameters - const wildcards = '[*:?+]'; - if (new RegExp(`${wildcards}`).exec(valueToCheck)) { - logger.debug(`The '$capture' parameter contains an Express-style wildcard ` + `character (${wildcards}). Strings are now always interpreted as ` + `exact matches; use a RegExp for partial or wildcard matches.`); - } - } - const matchCallback = ({ - url - }) => { - { - if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) { - logger.debug(`${capture} only partially matches the cross-origin URL ` + `${url.toString()}. This route will only handle cross-origin requests ` + `if they match the entire URL.`); - } - } - return url.href === captureUrl.href; - }; - // If `capture` is a string then `handler` and `method` must be present. - route = new Route(matchCallback, handler, method); - } else if (capture instanceof RegExp) { - // If `capture` is a `RegExp` then `handler` and `method` must be present. - route = new RegExpRoute(capture, handler, method); - } else if (typeof capture === 'function') { - // If `capture` is a function then `handler` and `method` must be present. - route = new Route(capture, handler, method); - } else if (capture instanceof Route) { - route = capture; - } else { - throw new WorkboxError('unsupported-route-type', { - moduleName: 'workbox-routing', - funcName: 'registerRoute', - paramName: 'capture' - }); - } - const defaultRouter = getOrCreateDefaultRouter(); - defaultRouter.registerRoute(route); - return route; - } - - // @ts-ignore - try { - self['workbox:cacheable-response:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * This class allows you to set up rules determining what - * status codes and/or headers need to be present in order for a - * [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) - * to be considered cacheable. - * - * @memberof workbox-cacheable-response - */ - class CacheableResponse { - /** - * To construct a new CacheableResponse instance you must provide at least - * one of the `config` properties. - * - * If both `statuses` and `headers` are specified, then both conditions must - * be met for the `Response` to be considered cacheable. - * - * @param {Object} config - * @param {Array<number>} [config.statuses] One or more status codes that a - * `Response` can have and be considered cacheable. - * @param {Object<string,string>} [config.headers] A mapping of header names - * and expected values that a `Response` can have and be considered cacheable. - * If multiple headers are provided, only one needs to be present. - */ - constructor(config = {}) { - { - if (!(config.statuses || config.headers)) { - throw new WorkboxError('statuses-or-headers-required', { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor' - }); - } - if (config.statuses) { - finalAssertExports.isArray(config.statuses, { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor', - paramName: 'config.statuses' - }); - } - if (config.headers) { - finalAssertExports.isType(config.headers, 'object', { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor', - paramName: 'config.headers' - }); - } - } - this._statuses = config.statuses; - this._headers = config.headers; - } - /** - * Checks a response to see whether it's cacheable or not, based on this - * object's configuration. - * - * @param {Response} response The response whose cacheability is being - * checked. - * @return {boolean} `true` if the `Response` is cacheable, and `false` - * otherwise. - */ - isResponseCacheable(response) { - { - finalAssertExports.isInstance(response, Response, { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'isResponseCacheable', - paramName: 'response' - }); - } - let cacheable = true; - if (this._statuses) { - cacheable = this._statuses.includes(response.status); - } - if (this._headers && cacheable) { - cacheable = Object.keys(this._headers).some(headerName => { - return response.headers.get(headerName) === this._headers[headerName]; - }); - } - { - if (!cacheable) { - logger.groupCollapsed(`The request for ` + `'${getFriendlyURL(response.url)}' returned a response that does ` + `not meet the criteria for being cached.`); - logger.groupCollapsed(`View cacheability criteria here.`); - logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses)); - logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2)); - logger.groupEnd(); - const logFriendlyHeaders = {}; - response.headers.forEach((value, key) => { - logFriendlyHeaders[key] = value; - }); - logger.groupCollapsed(`View response status and headers here.`); - logger.log(`Response status: ${response.status}`); - logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2)); - logger.groupEnd(); - logger.groupCollapsed(`View full response details here.`); - logger.log(response.headers); - logger.log(response); - logger.groupEnd(); - logger.groupEnd(); - } - } - return cacheable; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A class implementing the `cacheWillUpdate` lifecycle callback. This makes it - * easier to add in cacheability checks to requests made via Workbox's built-in - * strategies. - * - * @memberof workbox-cacheable-response - */ - class CacheableResponsePlugin { - /** - * To construct a new CacheableResponsePlugin instance you must provide at - * least one of the `config` properties. - * - * If both `statuses` and `headers` are specified, then both conditions must - * be met for the `Response` to be considered cacheable. - * - * @param {Object} config - * @param {Array<number>} [config.statuses] One or more status codes that a - * `Response` can have and be considered cacheable. - * @param {Object<string,string>} [config.headers] A mapping of header names - * and expected values that a `Response` can have and be considered cacheable. - * If multiple headers are provided, only one needs to be present. - */ - constructor(config) { - /** - * @param {Object} options - * @param {Response} options.response - * @return {Response|null} - * @private - */ - this.cacheWillUpdate = async ({ - response - }) => { - if (this._cacheableResponse.isResponseCacheable(response)) { - return response; - } - return null; - }; - this._cacheableResponse = new CacheableResponse(config); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const _cacheNameDetails = { - googleAnalytics: 'googleAnalytics', - precache: 'precache-v2', - prefix: 'workbox', - runtime: 'runtime', - suffix: typeof registration !== 'undefined' ? registration.scope : '' - }; - const _createCacheName = cacheName => { - return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value && value.length > 0).join('-'); - }; - const eachCacheNameDetail = fn => { - for (const key of Object.keys(_cacheNameDetails)) { - fn(key); - } - }; - const cacheNames = { - updateDetails: details => { - eachCacheNameDetail(key => { - if (typeof details[key] === 'string') { - _cacheNameDetails[key] = details[key]; - } - }); - }, - getGoogleAnalyticsName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics); - }, - getPrecacheName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.precache); - }, - getPrefix: () => { - return _cacheNameDetails.prefix; - }, - getRuntimeName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.runtime); - }, - getSuffix: () => { - return _cacheNameDetails.suffix; - } - }; - - /* - Copyright 2020 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - function stripParams(fullURL, ignoreParams) { - const strippedURL = new URL(fullURL); - for (const param of ignoreParams) { - strippedURL.searchParams.delete(param); - } - return strippedURL.href; - } - /** - * Matches an item in the cache, ignoring specific URL params. This is similar - * to the `ignoreSearch` option, but it allows you to ignore just specific - * params (while continuing to match on the others). - * - * @private - * @param {Cache} cache - * @param {Request} request - * @param {Object} matchOptions - * @param {Array<string>} ignoreParams - * @return {Promise<Response|undefined>} - */ - async function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) { - const strippedRequestURL = stripParams(request.url, ignoreParams); - // If the request doesn't include any ignored params, match as normal. - if (request.url === strippedRequestURL) { - return cache.match(request, matchOptions); - } - // Otherwise, match by comparing keys - const keysOptions = Object.assign(Object.assign({}, matchOptions), { - ignoreSearch: true - }); - const cacheKeys = await cache.keys(request, keysOptions); - for (const cacheKey of cacheKeys) { - const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams); - if (strippedRequestURL === strippedCacheKeyURL) { - return cache.match(cacheKey, matchOptions); - } - } - return; - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The Deferred class composes Promises in a way that allows for them to be - * resolved or rejected from outside the constructor. In most cases promises - * should be used directly, but Deferreds can be necessary when the logic to - * resolve a promise must be separate. - * - * @private - */ - class Deferred { - /** - * Creates a promise and exposes its resolve and reject functions as methods. - */ - constructor() { - this.promise = new Promise((resolve, reject) => { - this.resolve = resolve; - this.reject = reject; - }); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - // Callbacks to be executed whenever there's a quota error. - // Can't change Function type right now. - // eslint-disable-next-line @typescript-eslint/ban-types - const quotaErrorCallbacks = new Set(); - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Runs all of the callback functions, one at a time sequentially, in the order - * in which they were registered. - * - * @memberof workbox-core - * @private - */ - async function executeQuotaErrorCallbacks() { - { - logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`); - } - for (const callback of quotaErrorCallbacks) { - await callback(); - { - logger.log(callback, 'is complete.'); - } - } - { - logger.log('Finished running callbacks.'); - } - } - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Returns a promise that resolves and the passed number of milliseconds. - * This utility is an async/await-friendly version of `setTimeout`. - * - * @param {number} ms - * @return {Promise} - * @private - */ - function timeout(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); - } - - // @ts-ignore - try { - self['workbox:strategies:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - function toRequest(input) { - return typeof input === 'string' ? new Request(input) : input; - } - /** - * A class created every time a Strategy instance instance calls - * {@link workbox-strategies.Strategy~handle} or - * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and - * cache actions around plugin callbacks and keeps track of when the strategy - * is "done" (i.e. all added `event.waitUntil()` promises have resolved). - * - * @memberof workbox-strategies - */ - class StrategyHandler { - /** - * Creates a new instance associated with the passed strategy and event - * that's handling the request. - * - * The constructor also initializes the state that will be passed to each of - * the plugins handling this request. - * - * @param {workbox-strategies.Strategy} strategy - * @param {Object} options - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] The return value from the - * {@link workbox-routing~matchCallback} (if applicable). - */ - constructor(strategy, options) { - this._cacheKeys = {}; - /** - * The request the strategy is performing (passed to the strategy's - * `handle()` or `handleAll()` method). - * @name request - * @instance - * @type {Request} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * The event associated with this request. - * @name event - * @instance - * @type {ExtendableEvent} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * A `URL` instance of `request.url` (if passed to the strategy's - * `handle()` or `handleAll()` method). - * Note: the `url` param will be present if the strategy was invoked - * from a workbox `Route` object. - * @name url - * @instance - * @type {URL|undefined} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * A `param` value (if passed to the strategy's - * `handle()` or `handleAll()` method). - * Note: the `param` param will be present if the strategy was invoked - * from a workbox `Route` object and the - * {@link workbox-routing~matchCallback} returned - * a truthy value (it will be that value). - * @name params - * @instance - * @type {*|undefined} - * @memberof workbox-strategies.StrategyHandler - */ - { - finalAssertExports.isInstance(options.event, ExtendableEvent, { - moduleName: 'workbox-strategies', - className: 'StrategyHandler', - funcName: 'constructor', - paramName: 'options.event' - }); - } - Object.assign(this, options); - this.event = options.event; - this._strategy = strategy; - this._handlerDeferred = new Deferred(); - this._extendLifetimePromises = []; - // Copy the plugins list (since it's mutable on the strategy), - // so any mutations don't affect this handler instance. - this._plugins = [...strategy.plugins]; - this._pluginStateMap = new Map(); - for (const plugin of this._plugins) { - this._pluginStateMap.set(plugin, {}); - } - this.event.waitUntil(this._handlerDeferred.promise); - } - /** - * Fetches a given request (and invokes any applicable plugin callback - * methods) using the `fetchOptions` (for non-navigation requests) and - * `plugins` defined on the `Strategy` object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - `requestWillFetch()` - * - `fetchDidSucceed()` - * - `fetchDidFail()` - * - * @param {Request|string} input The URL or request to fetch. - * @return {Promise<Response>} - */ - async fetch(input) { - const { - event - } = this; - let request = toRequest(input); - if (request.mode === 'navigate' && event instanceof FetchEvent && event.preloadResponse) { - const possiblePreloadResponse = await event.preloadResponse; - if (possiblePreloadResponse) { - { - logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`); - } - return possiblePreloadResponse; - } - } - // If there is a fetchDidFail plugin, we need to save a clone of the - // original request before it's either modified by a requestWillFetch - // plugin or before the original request's body is consumed via fetch(). - const originalRequest = this.hasCallback('fetchDidFail') ? request.clone() : null; - try { - for (const cb of this.iterateCallbacks('requestWillFetch')) { - request = await cb({ - request: request.clone(), - event - }); - } - } catch (err) { - if (err instanceof Error) { - throw new WorkboxError('plugin-error-request-will-fetch', { - thrownErrorMessage: err.message - }); - } - } - // The request can be altered by plugins with `requestWillFetch` making - // the original request (most likely from a `fetch` event) different - // from the Request we make. Pass both to `fetchDidFail` to aid debugging. - const pluginFilteredRequest = request.clone(); - try { - let fetchResponse; - // See https://github.com/GoogleChrome/workbox/issues/1796 - fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions); - if ("development" !== 'production') { - logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`); - } - for (const callback of this.iterateCallbacks('fetchDidSucceed')) { - fetchResponse = await callback({ - event, - request: pluginFilteredRequest, - response: fetchResponse - }); - } - return fetchResponse; - } catch (error) { - { - logger.log(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error); - } - // `originalRequest` will only exist if a `fetchDidFail` callback - // is being used (see above). - if (originalRequest) { - await this.runCallbacks('fetchDidFail', { - error: error, - event, - originalRequest: originalRequest.clone(), - request: pluginFilteredRequest.clone() - }); - } - throw error; - } - } - /** - * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on - * the response generated by `this.fetch()`. - * - * The call to `this.cachePut()` automatically invokes `this.waitUntil()`, - * so you do not have to manually call `waitUntil()` on the event. - * - * @param {Request|string} input The request or URL to fetch and cache. - * @return {Promise<Response>} - */ - async fetchAndCachePut(input) { - const response = await this.fetch(input); - const responseClone = response.clone(); - void this.waitUntil(this.cachePut(input, responseClone)); - return response; - } - /** - * Matches a request from the cache (and invokes any applicable plugin - * callback methods) using the `cacheName`, `matchOptions`, and `plugins` - * defined on the strategy object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - cacheKeyWillBeUsed() - * - cachedResponseWillBeUsed() - * - * @param {Request|string} key The Request or URL to use as the cache key. - * @return {Promise<Response|undefined>} A matching response, if found. - */ - async cacheMatch(key) { - const request = toRequest(key); - let cachedResponse; - const { - cacheName, - matchOptions - } = this._strategy; - const effectiveRequest = await this.getCacheKey(request, 'read'); - const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), { - cacheName - }); - cachedResponse = await caches.match(effectiveRequest, multiMatchOptions); - { - if (cachedResponse) { - logger.debug(`Found a cached response in '${cacheName}'.`); - } else { - logger.debug(`No cached response found in '${cacheName}'.`); - } - } - for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) { - cachedResponse = (await callback({ - cacheName, - matchOptions, - cachedResponse, - request: effectiveRequest, - event: this.event - })) || undefined; - } - return cachedResponse; - } - /** - * Puts a request/response pair in the cache (and invokes any applicable - * plugin callback methods) using the `cacheName` and `plugins` defined on - * the strategy object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - cacheKeyWillBeUsed() - * - cacheWillUpdate() - * - cacheDidUpdate() - * - * @param {Request|string} key The request or URL to use as the cache key. - * @param {Response} response The response to cache. - * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response - * not be cached, and `true` otherwise. - */ - async cachePut(key, response) { - const request = toRequest(key); - // Run in the next task to avoid blocking other cache reads. - // https://github.com/w3c/ServiceWorker/issues/1397 - await timeout(0); - const effectiveRequest = await this.getCacheKey(request, 'write'); - { - if (effectiveRequest.method && effectiveRequest.method !== 'GET') { - throw new WorkboxError('attempt-to-cache-non-get-request', { - url: getFriendlyURL(effectiveRequest.url), - method: effectiveRequest.method - }); - } - // See https://github.com/GoogleChrome/workbox/issues/2818 - const vary = response.headers.get('Vary'); - if (vary) { - logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` + `has a 'Vary: ${vary}' header. ` + `Consider setting the {ignoreVary: true} option on your strategy ` + `to ensure cache matching and deletion works as expected.`); - } - } - if (!response) { - { - logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`); - } - throw new WorkboxError('cache-put-with-no-response', { - url: getFriendlyURL(effectiveRequest.url) - }); - } - const responseToCache = await this._ensureResponseSafeToCache(response); - if (!responseToCache) { - { - logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` + `will not be cached.`, responseToCache); - } - return false; - } - const { - cacheName, - matchOptions - } = this._strategy; - const cache = await self.caches.open(cacheName); - const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate'); - const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams( - // TODO(philipwalton): the `__WB_REVISION__` param is a precaching - // feature. Consider into ways to only add this behavior if using - // precaching. - cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions) : null; - { - logger.debug(`Updating the '${cacheName}' cache with a new Response ` + `for ${getFriendlyURL(effectiveRequest.url)}.`); - } - try { - await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache); - } catch (error) { - if (error instanceof Error) { - // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError - if (error.name === 'QuotaExceededError') { - await executeQuotaErrorCallbacks(); - } - throw error; - } - } - for (const callback of this.iterateCallbacks('cacheDidUpdate')) { - await callback({ - cacheName, - oldResponse, - newResponse: responseToCache.clone(), - request: effectiveRequest, - event: this.event - }); - } - return true; - } - /** - * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and - * executes any of those callbacks found in sequence. The final `Request` - * object returned by the last plugin is treated as the cache key for cache - * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have - * been registered, the passed request is returned unmodified - * - * @param {Request} request - * @param {string} mode - * @return {Promise<Request>} - */ - async getCacheKey(request, mode) { - const key = `${request.url} | ${mode}`; - if (!this._cacheKeys[key]) { - let effectiveRequest = request; - for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) { - effectiveRequest = toRequest(await callback({ - mode, - request: effectiveRequest, - event: this.event, - // params has a type any can't change right now. - params: this.params // eslint-disable-line - })); - } - this._cacheKeys[key] = effectiveRequest; - } - return this._cacheKeys[key]; - } - /** - * Returns true if the strategy has at least one plugin with the given - * callback. - * - * @param {string} name The name of the callback to check for. - * @return {boolean} - */ - hasCallback(name) { - for (const plugin of this._strategy.plugins) { - if (name in plugin) { - return true; - } - } - return false; - } - /** - * Runs all plugin callbacks matching the given name, in order, passing the - * given param object (merged ith the current plugin state) as the only - * argument. - * - * Note: since this method runs all plugins, it's not suitable for cases - * where the return value of a callback needs to be applied prior to calling - * the next callback. See - * {@link workbox-strategies.StrategyHandler#iterateCallbacks} - * below for how to handle that case. - * - * @param {string} name The name of the callback to run within each plugin. - * @param {Object} param The object to pass as the first (and only) param - * when executing each callback. This object will be merged with the - * current plugin state prior to callback execution. - */ - async runCallbacks(name, param) { - for (const callback of this.iterateCallbacks(name)) { - // TODO(philipwalton): not sure why `any` is needed. It seems like - // this should work with `as WorkboxPluginCallbackParam[C]`. - await callback(param); - } - } - /** - * Accepts a callback and returns an iterable of matching plugin callbacks, - * where each callback is wrapped with the current handler state (i.e. when - * you call each callback, whatever object parameter you pass it will - * be merged with the plugin's current state). - * - * @param {string} name The name fo the callback to run - * @return {Array<Function>} - */ - *iterateCallbacks(name) { - for (const plugin of this._strategy.plugins) { - if (typeof plugin[name] === 'function') { - const state = this._pluginStateMap.get(plugin); - const statefulCallback = param => { - const statefulParam = Object.assign(Object.assign({}, param), { - state - }); - // TODO(philipwalton): not sure why `any` is needed. It seems like - // this should work with `as WorkboxPluginCallbackParam[C]`. - return plugin[name](statefulParam); - }; - yield statefulCallback; - } - } - } - /** - * Adds a promise to the - * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises} - * of the event event associated with the request being handled (usually a - * `FetchEvent`). - * - * Note: you can await - * {@link workbox-strategies.StrategyHandler~doneWaiting} - * to know when all added promises have settled. - * - * @param {Promise} promise A promise to add to the extend lifetime promises - * of the event that triggered the request. - */ - waitUntil(promise) { - this._extendLifetimePromises.push(promise); - return promise; - } - /** - * Returns a promise that resolves once all promises passed to - * {@link workbox-strategies.StrategyHandler~waitUntil} - * have settled. - * - * Note: any work done after `doneWaiting()` settles should be manually - * passed to an event's `waitUntil()` method (not this handler's - * `waitUntil()` method), otherwise the service worker thread my be killed - * prior to your work completing. - */ - async doneWaiting() { - let promise; - while (promise = this._extendLifetimePromises.shift()) { - await promise; - } - } - /** - * Stops running the strategy and immediately resolves any pending - * `waitUntil()` promises. - */ - destroy() { - this._handlerDeferred.resolve(null); - } - /** - * This method will call cacheWillUpdate on the available plugins (or use - * status === 200) to determine if the Response is safe and valid to cache. - * - * @param {Request} options.request - * @param {Response} options.response - * @return {Promise<Response|undefined>} - * - * @private - */ - async _ensureResponseSafeToCache(response) { - let responseToCache = response; - let pluginsUsed = false; - for (const callback of this.iterateCallbacks('cacheWillUpdate')) { - responseToCache = (await callback({ - request: this.request, - response: responseToCache, - event: this.event - })) || undefined; - pluginsUsed = true; - if (!responseToCache) { - break; - } - } - if (!pluginsUsed) { - if (responseToCache && responseToCache.status !== 200) { - responseToCache = undefined; - } - { - if (responseToCache) { - if (responseToCache.status !== 200) { - if (responseToCache.status === 0) { - logger.warn(`The response for '${this.request.url}' ` + `is an opaque response. The caching strategy that you're ` + `using will not cache opaque responses by default.`); - } else { - logger.debug(`The response for '${this.request.url}' ` + `returned a status code of '${response.status}' and won't ` + `be cached as a result.`); - } - } - } - } - } - return responseToCache; - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An abstract base class that all other strategy classes must extend from: - * - * @memberof workbox-strategies - */ - class Strategy { - /** - * Creates a new instance of the strategy and sets all documented option - * properties as public instance properties. - * - * Note: if a custom strategy class extends the base Strategy class and does - * not need more than these properties, it does not need to define its own - * constructor. - * - * @param {Object} [options] - * @param {string} [options.cacheName] Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796) - * `fetch()` requests made by this strategy. - * @param {Object} [options.matchOptions] The - * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - */ - constructor(options = {}) { - /** - * Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * - * @type {string} - */ - this.cacheName = cacheNames.getRuntimeName(options.cacheName); - /** - * The list - * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * used by this strategy. - * - * @type {Array<Object>} - */ - this.plugins = options.plugins || []; - /** - * Values passed along to the - * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters} - * of all fetch() requests made by this strategy. - * - * @type {Object} - */ - this.fetchOptions = options.fetchOptions; - /** - * The - * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - * - * @type {Object} - */ - this.matchOptions = options.matchOptions; - } - /** - * Perform a request strategy and returns a `Promise` that will resolve with - * a `Response`, invoking all relevant plugin callbacks. - * - * When a strategy instance is registered with a Workbox - * {@link workbox-routing.Route}, this method is automatically - * called when the route matches. - * - * Alternatively, this method can be used in a standalone `FetchEvent` - * listener by passing it to `event.respondWith()`. - * - * @param {FetchEvent|Object} options A `FetchEvent` or an object with the - * properties listed below. - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] - */ - handle(options) { - const [responseDone] = this.handleAll(options); - return responseDone; - } - /** - * Similar to {@link workbox-strategies.Strategy~handle}, but - * instead of just returning a `Promise` that resolves to a `Response` it - * it will return an tuple of `[response, done]` promises, where the former - * (`response`) is equivalent to what `handle()` returns, and the latter is a - * Promise that will resolve once any promises that were added to - * `event.waitUntil()` as part of performing the strategy have completed. - * - * You can await the `done` promise to ensure any extra work performed by - * the strategy (usually caching responses) completes successfully. - * - * @param {FetchEvent|Object} options A `FetchEvent` or an object with the - * properties listed below. - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] - * @return {Array<Promise>} A tuple of [response, done] - * promises that can be used to determine when the response resolves as - * well as when the handler has completed all its work. - */ - handleAll(options) { - // Allow for flexible options to be passed. - if (options instanceof FetchEvent) { - options = { - event: options, - request: options.request - }; - } - const event = options.event; - const request = typeof options.request === 'string' ? new Request(options.request) : options.request; - const params = 'params' in options ? options.params : undefined; - const handler = new StrategyHandler(this, { - event, - request, - params - }); - const responseDone = this._getResponse(handler, request, event); - const handlerDone = this._awaitComplete(responseDone, handler, request, event); - // Return an array of promises, suitable for use with Promise.all(). - return [responseDone, handlerDone]; - } - async _getResponse(handler, request, event) { - await handler.runCallbacks('handlerWillStart', { - event, - request - }); - let response = undefined; - try { - response = await this._handle(request, handler); - // The "official" Strategy subclasses all throw this error automatically, - // but in case a third-party Strategy doesn't, ensure that we have a - // consistent failure when there's no response or an error response. - if (!response || response.type === 'error') { - throw new WorkboxError('no-response', { - url: request.url - }); - } - } catch (error) { - if (error instanceof Error) { - for (const callback of handler.iterateCallbacks('handlerDidError')) { - response = await callback({ - error, - event, - request - }); - if (response) { - break; - } - } - } - if (!response) { - throw error; - } else { - logger.log(`While responding to '${getFriendlyURL(request.url)}', ` + `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` + `a handlerDidError plugin.`); - } - } - for (const callback of handler.iterateCallbacks('handlerWillRespond')) { - response = await callback({ - event, - request, - response - }); - } - return response; - } - async _awaitComplete(responseDone, handler, request, event) { - let response; - let error; - try { - response = await responseDone; - } catch (error) { - // Ignore errors, as response errors should be caught via the `response` - // promise above. The `done` promise will only throw for errors in - // promises passed to `handler.waitUntil()`. - } - try { - await handler.runCallbacks('handlerDidRespond', { - event, - request, - response - }); - await handler.doneWaiting(); - } catch (waitUntilError) { - if (waitUntilError instanceof Error) { - error = waitUntilError; - } - } - await handler.runCallbacks('handlerDidComplete', { - event, - request, - response, - error: error - }); - handler.destroy(); - if (error) { - throw error; - } - } - } - /** - * Classes extending the `Strategy` based class should implement this method, - * and leverage the {@link workbox-strategies.StrategyHandler} - * arg to perform all fetching and cache logic, which will ensure all relevant - * cache, cache options, fetch options and plugins are used (per the current - * strategy instance). - * - * @name _handle - * @instance - * @abstract - * @function - * @param {Request} request - * @param {workbox-strategies.StrategyHandler} handler - * @return {Promise<Response>} - * - * @memberof workbox-strategies.Strategy - */ - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const messages = { - strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`, - printFinalResponse: response => { - if (response) { - logger.groupCollapsed(`View the final response here.`); - logger.log(response || '[No response returned]'); - logger.groupEnd(); - } - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network) - * request strategy. - * - * A cache first strategy is useful for assets that have been revisioned, - * such as URLs like `/styles/example.a8f5f1.css`, since they - * can be cached for long periods of time. - * - * If the network request fails, and there is no cache match, this will throw - * a `WorkboxError` exception. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-strategies - */ - class CacheFirst extends Strategy { - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - const logs = []; - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-strategies', - className: this.constructor.name, - funcName: 'makeRequest', - paramName: 'request' - }); - } - let response = await handler.cacheMatch(request); - let error = undefined; - if (!response) { - { - logs.push(`No response found in the '${this.cacheName}' cache. ` + `Will respond with a network request.`); - } - try { - response = await handler.fetchAndCachePut(request); - } catch (err) { - if (err instanceof Error) { - error = err; - } - } - { - if (response) { - logs.push(`Got response from network.`); - } else { - logs.push(`Unable to get a response from the network.`); - } - } - } else { - { - logs.push(`Found a cached response in the '${this.cacheName}' cache.`); - } - } - { - logger.groupCollapsed(messages.strategyStart(this.constructor.name, request)); - for (const log of logs) { - logger.log(log); - } - messages.printFinalResponse(response); - logger.groupEnd(); - } - if (!response) { - throw new WorkboxError('no-response', { - url: request.url, - error - }); - } - return response; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An implementation of a - * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only) - * request strategy. - * - * This class is useful if you want to take advantage of any - * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/). - * - * If the network request fails, this will throw a `WorkboxError` exception. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-strategies - */ - class NetworkOnly extends Strategy { - /** - * @param {Object} [options] - * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796) - * `fetch()` requests made by this strategy. - * @param {number} [options.networkTimeoutSeconds] If set, any network requests - * that fail to respond within the timeout will result in a network error. - */ - constructor(options = {}) { - super(options); - this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0; - } - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-strategies', - className: this.constructor.name, - funcName: '_handle', - paramName: 'request' - }); - } - let error = undefined; - let response; - try { - const promises = [handler.fetch(request)]; - if (this._networkTimeoutSeconds) { - const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000); - promises.push(timeoutPromise); - } - response = await Promise.race(promises); - if (!response) { - throw new Error(`Timed out the network response after ` + `${this._networkTimeoutSeconds} seconds.`); - } - } catch (err) { - if (err instanceof Error) { - error = err; - } - } - { - logger.groupCollapsed(messages.strategyStart(this.constructor.name, request)); - if (response) { - logger.log(`Got response from network.`); - } else { - logger.log(`Unable to get a response from the network.`); - } - messages.printFinalResponse(response); - logger.groupEnd(); - } - if (!response) { - throw new WorkboxError('no-response', { - url: request.url, - error - }); - } - return response; - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Claim any currently available clients once the service worker - * becomes active. This is normally used in conjunction with `skipWaiting()`. - * - * @memberof workbox-core - */ - function clientsClaim() { - self.addEventListener('activate', () => self.clients.claim()); - } - - /* - Copyright 2020 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A utility method that makes it easier to use `event.waitUntil` with - * async functions and return the result. - * - * @param {ExtendableEvent} event - * @param {Function} asyncFn - * @return {Function} - * @private - */ - function waitUntil(event, asyncFn) { - const returnPromise = asyncFn(); - event.waitUntil(returnPromise); - return returnPromise; - } - - // @ts-ignore - try { - self['workbox:precaching:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - // Name of the search parameter used to store revision info. - const REVISION_SEARCH_PARAM = '__WB_REVISION__'; - /** - * Converts a manifest entry into a versioned URL suitable for precaching. - * - * @param {Object|string} entry - * @return {string} A URL with versioning info. - * - * @private - * @memberof workbox-precaching - */ - function createCacheKey(entry) { - if (!entry) { - throw new WorkboxError('add-to-cache-list-unexpected-type', { - entry - }); - } - // If a precache manifest entry is a string, it's assumed to be a versioned - // URL, like '/app.abcd1234.js'. Return as-is. - if (typeof entry === 'string') { - const urlObject = new URL(entry, location.href); - return { - cacheKey: urlObject.href, - url: urlObject.href - }; - } - const { - revision, - url - } = entry; - if (!url) { - throw new WorkboxError('add-to-cache-list-unexpected-type', { - entry - }); - } - // If there's just a URL and no revision, then it's also assumed to be a - // versioned URL. - if (!revision) { - const urlObject = new URL(url, location.href); - return { - cacheKey: urlObject.href, - url: urlObject.href - }; - } - // Otherwise, construct a properly versioned URL using the custom Workbox - // search parameter along with the revision info. - const cacheKeyURL = new URL(url, location.href); - const originalURL = new URL(url, location.href); - cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision); - return { - cacheKey: cacheKeyURL.href, - url: originalURL.href - }; - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A plugin, designed to be used with PrecacheController, to determine the - * of assets that were updated (or not updated) during the install event. - * - * @private - */ - class PrecacheInstallReportPlugin { - constructor() { - this.updatedURLs = []; - this.notUpdatedURLs = []; - this.handlerWillStart = async ({ - request, - state - }) => { - // TODO: `state` should never be undefined... - if (state) { - state.originalRequest = request; - } - }; - this.cachedResponseWillBeUsed = async ({ - event, - state, - cachedResponse - }) => { - if (event.type === 'install') { - if (state && state.originalRequest && state.originalRequest instanceof Request) { - // TODO: `state` should never be undefined... - const url = state.originalRequest.url; - if (cachedResponse) { - this.notUpdatedURLs.push(url); - } else { - this.updatedURLs.push(url); - } - } - } - return cachedResponse; - }; - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A plugin, designed to be used with PrecacheController, to translate URLs into - * the corresponding cache key, based on the current revision info. - * - * @private - */ - class PrecacheCacheKeyPlugin { - constructor({ - precacheController - }) { - this.cacheKeyWillBeUsed = async ({ - request, - params - }) => { - // Params is type any, can't change right now. - /* eslint-disable */ - const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) || this._precacheController.getCacheKeyForURL(request.url); - /* eslint-enable */ - return cacheKey ? new Request(cacheKey, { - headers: request.headers - }) : request; - }; - this._precacheController = precacheController; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {string} groupTitle - * @param {Array<string>} deletedURLs - * - * @private - */ - const logGroup = (groupTitle, deletedURLs) => { - logger.groupCollapsed(groupTitle); - for (const url of deletedURLs) { - logger.log(url); - } - logger.groupEnd(); - }; - /** - * @param {Array<string>} deletedURLs - * - * @private - * @memberof workbox-precaching - */ - function printCleanupDetails(deletedURLs) { - const deletionCount = deletedURLs.length; - if (deletionCount > 0) { - logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`); - logGroup('Deleted Cache Requests', deletedURLs); - logger.groupEnd(); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {string} groupTitle - * @param {Array<string>} urls - * - * @private - */ - function _nestedGroup(groupTitle, urls) { - if (urls.length === 0) { - return; - } - logger.groupCollapsed(groupTitle); - for (const url of urls) { - logger.log(url); - } - logger.groupEnd(); - } - /** - * @param {Array<string>} urlsToPrecache - * @param {Array<string>} urlsAlreadyPrecached - * - * @private - * @memberof workbox-precaching - */ - function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) { - const precachedCount = urlsToPrecache.length; - const alreadyPrecachedCount = urlsAlreadyPrecached.length; - if (precachedCount || alreadyPrecachedCount) { - let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`; - if (alreadyPrecachedCount > 0) { - message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`; - } - logger.groupCollapsed(message); - _nestedGroup(`View newly precached URLs.`, urlsToPrecache); - _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached); - logger.groupEnd(); - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let supportStatus; - /** - * A utility function that determines whether the current browser supports - * constructing a new `Response` from a `response.body` stream. - * - * @return {boolean} `true`, if the current browser can successfully - * construct a `Response` from a `response.body` stream, `false` otherwise. - * - * @private - */ - function canConstructResponseFromBodyStream() { - if (supportStatus === undefined) { - const testResponse = new Response(''); - if ('body' in testResponse) { - try { - new Response(testResponse.body); - supportStatus = true; - } catch (error) { - supportStatus = false; - } - } - supportStatus = false; - } - return supportStatus; - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Allows developers to copy a response and modify its `headers`, `status`, - * or `statusText` values (the values settable via a - * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax} - * object in the constructor). - * To modify these values, pass a function as the second argument. That - * function will be invoked with a single object with the response properties - * `{headers, status, statusText}`. The return value of this function will - * be used as the `ResponseInit` for the new `Response`. To change the values - * either modify the passed parameter(s) and return it, or return a totally - * new object. - * - * This method is intentionally limited to same-origin responses, regardless of - * whether CORS was used or not. - * - * @param {Response} response - * @param {Function} modifier - * @memberof workbox-core - */ - async function copyResponse(response, modifier) { - let origin = null; - // If response.url isn't set, assume it's cross-origin and keep origin null. - if (response.url) { - const responseURL = new URL(response.url); - origin = responseURL.origin; - } - if (origin !== self.location.origin) { - throw new WorkboxError('cross-origin-copy-response', { - origin - }); - } - const clonedResponse = response.clone(); - // Create a fresh `ResponseInit` object by cloning the headers. - const responseInit = { - headers: new Headers(clonedResponse.headers), - status: clonedResponse.status, - statusText: clonedResponse.statusText - }; - // Apply any user modifications. - const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit; - // Create the new response from the body stream and `ResponseInit` - // modifications. Note: not all browsers support the Response.body stream, - // so fall back to reading the entire body into memory as a blob. - const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob(); - return new Response(body, modifiedResponseInit); - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A {@link workbox-strategies.Strategy} implementation - * specifically designed to work with - * {@link workbox-precaching.PrecacheController} - * to both cache and fetch precached assets. - * - * Note: an instance of this class is created automatically when creating a - * `PrecacheController`; it's generally not necessary to create this yourself. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-precaching - */ - class PrecacheStrategy extends Strategy { - /** - * - * @param {Object} [options] - * @param {string} [options.cacheName] Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init} - * of all fetch() requests made by this strategy. - * @param {Object} [options.matchOptions] The - * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to - * get the response from the network if there's a precache miss. - */ - constructor(options = {}) { - options.cacheName = cacheNames.getPrecacheName(options.cacheName); - super(options); - this._fallbackToNetwork = options.fallbackToNetwork === false ? false : true; - // Redirected responses cannot be used to satisfy a navigation request, so - // any redirected response must be "copied" rather than cloned, so the new - // response doesn't contain the `redirected` flag. See: - // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1 - this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin); - } - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - const response = await handler.cacheMatch(request); - if (response) { - return response; - } - // If this is an `install` event for an entry that isn't already cached, - // then populate the cache. - if (handler.event && handler.event.type === 'install') { - return await this._handleInstall(request, handler); - } - // Getting here means something went wrong. An entry that should have been - // precached wasn't found in the cache. - return await this._handleFetch(request, handler); - } - async _handleFetch(request, handler) { - let response; - const params = handler.params || {}; - // Fall back to the network if we're configured to do so. - if (this._fallbackToNetwork) { - { - logger.warn(`The precached response for ` + `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` + `found. Falling back to the network.`); - } - const integrityInManifest = params.integrity; - const integrityInRequest = request.integrity; - const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest; - // Do not add integrity if the original request is no-cors - // See https://github.com/GoogleChrome/workbox/issues/3096 - response = await handler.fetch(new Request(request, { - integrity: request.mode !== 'no-cors' ? integrityInRequest || integrityInManifest : undefined - })); - // It's only "safe" to repair the cache if we're using SRI to guarantee - // that the response matches the precache manifest's expectations, - // and there's either a) no integrity property in the incoming request - // or b) there is an integrity, and it matches the precache manifest. - // See https://github.com/GoogleChrome/workbox/issues/2858 - // Also if the original request users no-cors we don't use integrity. - // See https://github.com/GoogleChrome/workbox/issues/3096 - if (integrityInManifest && noIntegrityConflict && request.mode !== 'no-cors') { - this._useDefaultCacheabilityPluginIfNeeded(); - const wasCached = await handler.cachePut(request, response.clone()); - { - if (wasCached) { - logger.log(`A response for ${getFriendlyURL(request.url)} ` + `was used to "repair" the precache.`); - } - } - } - } else { - // This shouldn't normally happen, but there are edge cases: - // https://github.com/GoogleChrome/workbox/issues/1441 - throw new WorkboxError('missing-precache-entry', { - cacheName: this.cacheName, - url: request.url - }); - } - { - const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read')); - // Workbox is going to handle the route. - // print the routing details to the console. - logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL(request.url)); - logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`); - logger.groupCollapsed(`View request details here.`); - logger.log(request); - logger.groupEnd(); - logger.groupCollapsed(`View response details here.`); - logger.log(response); - logger.groupEnd(); - logger.groupEnd(); - } - return response; - } - async _handleInstall(request, handler) { - this._useDefaultCacheabilityPluginIfNeeded(); - const response = await handler.fetch(request); - // Make sure we defer cachePut() until after we know the response - // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737 - const wasCached = await handler.cachePut(request, response.clone()); - if (!wasCached) { - // Throwing here will lead to the `install` handler failing, which - // we want to do if *any* of the responses aren't safe to cache. - throw new WorkboxError('bad-precaching-response', { - url: request.url, - status: response.status - }); - } - return response; - } - /** - * This method is complex, as there a number of things to account for: - * - * The `plugins` array can be set at construction, and/or it might be added to - * to at any time before the strategy is used. - * - * At the time the strategy is used (i.e. during an `install` event), there - * needs to be at least one plugin that implements `cacheWillUpdate` in the - * array, other than `copyRedirectedCacheableResponsesPlugin`. - * - * - If this method is called and there are no suitable `cacheWillUpdate` - * plugins, we need to add `defaultPrecacheCacheabilityPlugin`. - * - * - If this method is called and there is exactly one `cacheWillUpdate`, then - * we don't have to do anything (this might be a previously added - * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin). - * - * - If this method is called and there is more than one `cacheWillUpdate`, - * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so, - * we need to remove it. (This situation is unlikely, but it could happen if - * the strategy is used multiple times, the first without a `cacheWillUpdate`, - * and then later on after manually adding a custom `cacheWillUpdate`.) - * - * See https://github.com/GoogleChrome/workbox/issues/2737 for more context. - * - * @private - */ - _useDefaultCacheabilityPluginIfNeeded() { - let defaultPluginIndex = null; - let cacheWillUpdatePluginCount = 0; - for (const [index, plugin] of this.plugins.entries()) { - // Ignore the copy redirected plugin when determining what to do. - if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) { - continue; - } - // Save the default plugin's index, in case it needs to be removed. - if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) { - defaultPluginIndex = index; - } - if (plugin.cacheWillUpdate) { - cacheWillUpdatePluginCount++; - } - } - if (cacheWillUpdatePluginCount === 0) { - this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin); - } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) { - // Only remove the default plugin; multiple custom plugins are allowed. - this.plugins.splice(defaultPluginIndex, 1); - } - // Nothing needs to be done if cacheWillUpdatePluginCount is 1 - } - } - PrecacheStrategy.defaultPrecacheCacheabilityPlugin = { - async cacheWillUpdate({ - response - }) { - if (!response || response.status >= 400) { - return null; - } - return response; - } - }; - PrecacheStrategy.copyRedirectedCacheableResponsesPlugin = { - async cacheWillUpdate({ - response - }) { - return response.redirected ? await copyResponse(response) : response; - } - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Performs efficient precaching of assets. - * - * @memberof workbox-precaching - */ - class PrecacheController { - /** - * Create a new PrecacheController. - * - * @param {Object} [options] - * @param {string} [options.cacheName] The cache to use for precaching. - * @param {string} [options.plugins] Plugins to use when precaching as well - * as responding to fetch events for precached assets. - * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to - * get the response from the network if there's a precache miss. - */ - constructor({ - cacheName, - plugins = [], - fallbackToNetwork = true - } = {}) { - this._urlsToCacheKeys = new Map(); - this._urlsToCacheModes = new Map(); - this._cacheKeysToIntegrities = new Map(); - this._strategy = new PrecacheStrategy({ - cacheName: cacheNames.getPrecacheName(cacheName), - plugins: [...plugins, new PrecacheCacheKeyPlugin({ - precacheController: this - })], - fallbackToNetwork - }); - // Bind the install and activate methods to the instance. - this.install = this.install.bind(this); - this.activate = this.activate.bind(this); - } - /** - * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and - * used to cache assets and respond to fetch events. - */ - get strategy() { - return this._strategy; - } - /** - * Adds items to the precache list, removing any duplicates and - * stores the files in the - * {@link workbox-core.cacheNames|"precache cache"} when the service - * worker installs. - * - * This method can be called multiple times. - * - * @param {Array<Object|string>} [entries=[]] Array of entries to precache. - */ - precache(entries) { - this.addToCacheList(entries); - if (!this._installAndActiveListenersAdded) { - self.addEventListener('install', this.install); - self.addEventListener('activate', this.activate); - this._installAndActiveListenersAdded = true; - } - } - /** - * This method will add items to the precache list, removing duplicates - * and ensuring the information is valid. - * - * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries - * Array of entries to precache. - */ - addToCacheList(entries) { - { - finalAssertExports.isArray(entries, { - moduleName: 'workbox-precaching', - className: 'PrecacheController', - funcName: 'addToCacheList', - paramName: 'entries' - }); - } - const urlsToWarnAbout = []; - for (const entry of entries) { - // See https://github.com/GoogleChrome/workbox/issues/2259 - if (typeof entry === 'string') { - urlsToWarnAbout.push(entry); - } else if (entry && entry.revision === undefined) { - urlsToWarnAbout.push(entry.url); - } - const { - cacheKey, - url - } = createCacheKey(entry); - const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default'; - if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) { - throw new WorkboxError('add-to-cache-list-conflicting-entries', { - firstEntry: this._urlsToCacheKeys.get(url), - secondEntry: cacheKey - }); - } - if (typeof entry !== 'string' && entry.integrity) { - if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) { - throw new WorkboxError('add-to-cache-list-conflicting-integrities', { - url - }); - } - this._cacheKeysToIntegrities.set(cacheKey, entry.integrity); - } - this._urlsToCacheKeys.set(url, cacheKey); - this._urlsToCacheModes.set(url, cacheMode); - if (urlsToWarnAbout.length > 0) { - const warningMessage = `Workbox is precaching URLs without revision ` + `info: ${urlsToWarnAbout.join(', ')}\nThis is generally NOT safe. ` + `Learn more at https://bit.ly/wb-precache`; - { - logger.warn(warningMessage); - } - } - } - } - /** - * Precaches new and updated assets. Call this method from the service worker - * install event. - * - * Note: this method calls `event.waitUntil()` for you, so you do not need - * to call it yourself in your event handlers. - * - * @param {ExtendableEvent} event - * @return {Promise<workbox-precaching.InstallResult>} - */ - install(event) { - // waitUntil returns Promise<any> - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return waitUntil(event, async () => { - const installReportPlugin = new PrecacheInstallReportPlugin(); - this.strategy.plugins.push(installReportPlugin); - // Cache entries one at a time. - // See https://github.com/GoogleChrome/workbox/issues/2528 - for (const [url, cacheKey] of this._urlsToCacheKeys) { - const integrity = this._cacheKeysToIntegrities.get(cacheKey); - const cacheMode = this._urlsToCacheModes.get(url); - const request = new Request(url, { - integrity, - cache: cacheMode, - credentials: 'same-origin' - }); - await Promise.all(this.strategy.handleAll({ - params: { - cacheKey - }, - request, - event - })); - } - const { - updatedURLs, - notUpdatedURLs - } = installReportPlugin; - { - printInstallDetails(updatedURLs, notUpdatedURLs); - } - return { - updatedURLs, - notUpdatedURLs - }; - }); - } - /** - * Deletes assets that are no longer present in the current precache manifest. - * Call this method from the service worker activate event. - * - * Note: this method calls `event.waitUntil()` for you, so you do not need - * to call it yourself in your event handlers. - * - * @param {ExtendableEvent} event - * @return {Promise<workbox-precaching.CleanupResult>} - */ - activate(event) { - // waitUntil returns Promise<any> - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return waitUntil(event, async () => { - const cache = await self.caches.open(this.strategy.cacheName); - const currentlyCachedRequests = await cache.keys(); - const expectedCacheKeys = new Set(this._urlsToCacheKeys.values()); - const deletedURLs = []; - for (const request of currentlyCachedRequests) { - if (!expectedCacheKeys.has(request.url)) { - await cache.delete(request); - deletedURLs.push(request.url); - } - } - { - printCleanupDetails(deletedURLs); - } - return { - deletedURLs - }; - }); - } - /** - * Returns a mapping of a precached URL to the corresponding cache key, taking - * into account the revision information for the URL. - * - * @return {Map<string, string>} A URL to cache key mapping. - */ - getURLsToCacheKeys() { - return this._urlsToCacheKeys; - } - /** - * Returns a list of all the URLs that have been precached by the current - * service worker. - * - * @return {Array<string>} The precached URLs. - */ - getCachedURLs() { - return [...this._urlsToCacheKeys.keys()]; - } - /** - * Returns the cache key used for storing a given URL. If that URL is - * unversioned, like `/index.html', then the cache key will be the original - * URL with a search parameter appended to it. - * - * @param {string} url A URL whose cache key you want to look up. - * @return {string} The versioned URL that corresponds to a cache key - * for the original URL, or undefined if that URL isn't precached. - */ - getCacheKeyForURL(url) { - const urlObject = new URL(url, location.href); - return this._urlsToCacheKeys.get(urlObject.href); - } - /** - * @param {string} url A cache key whose SRI you want to look up. - * @return {string} The subresource integrity associated with the cache key, - * or undefined if it's not set. - */ - getIntegrityForCacheKey(cacheKey) { - return this._cacheKeysToIntegrities.get(cacheKey); - } - /** - * This acts as a drop-in replacement for - * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) - * with the following differences: - * - * - It knows what the name of the precache is, and only checks in that cache. - * - It allows you to pass in an "original" URL without versioning parameters, - * and it will automatically look up the correct cache key for the currently - * active revision of that URL. - * - * E.g., `matchPrecache('index.html')` will find the correct precached - * response for the currently active service worker, even if the actual cache - * key is `'/index.html?__WB_REVISION__=1234abcd'`. - * - * @param {string|Request} request The key (without revisioning parameters) - * to look up in the precache. - * @return {Promise<Response|undefined>} - */ - async matchPrecache(request) { - const url = request instanceof Request ? request.url : request; - const cacheKey = this.getCacheKeyForURL(url); - if (cacheKey) { - const cache = await self.caches.open(this.strategy.cacheName); - return cache.match(cacheKey); - } - return undefined; - } - /** - * Returns a function that looks up `url` in the precache (taking into - * account revision information), and returns the corresponding `Response`. - * - * @param {string} url The precached URL which will be used to lookup the - * `Response`. - * @return {workbox-routing~handlerCallback} - */ - createHandlerBoundToURL(url) { - const cacheKey = this.getCacheKeyForURL(url); - if (!cacheKey) { - throw new WorkboxError('non-precached-url', { - url - }); - } - return options => { - options.request = new Request(url); - options.params = Object.assign({ - cacheKey - }, options.params); - return this.strategy.handle(options); - }; - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let precacheController; - /** - * @return {PrecacheController} - * @private - */ - const getOrCreatePrecacheController = () => { - if (!precacheController) { - precacheController = new PrecacheController(); - } - return precacheController; - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Removes any URL search parameters that should be ignored. - * - * @param {URL} urlObject The original URL. - * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against - * each search parameter name. Matches mean that the search parameter should be - * ignored. - * @return {URL} The URL with any ignored search parameters removed. - * - * @private - * @memberof workbox-precaching - */ - function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) { - // Convert the iterable into an array at the start of the loop to make sure - // deletion doesn't mess up iteration. - for (const paramName of [...urlObject.searchParams.keys()]) { - if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) { - urlObject.searchParams.delete(paramName); - } - } - return urlObject; - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Generator function that yields possible variations on the original URL to - * check, one at a time. - * - * @param {string} url - * @param {Object} options - * - * @private - * @memberof workbox-precaching - */ - function* generateURLVariations(url, { - ignoreURLParametersMatching = [/^utm_/, /^fbclid$/], - directoryIndex = 'index.html', - cleanURLs = true, - urlManipulation - } = {}) { - const urlObject = new URL(url, location.href); - urlObject.hash = ''; - yield urlObject.href; - const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching); - yield urlWithoutIgnoredParams.href; - if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) { - const directoryURL = new URL(urlWithoutIgnoredParams.href); - directoryURL.pathname += directoryIndex; - yield directoryURL.href; - } - if (cleanURLs) { - const cleanURL = new URL(urlWithoutIgnoredParams.href); - cleanURL.pathname += '.html'; - yield cleanURL.href; - } - if (urlManipulation) { - const additionalURLs = urlManipulation({ - url: urlObject - }); - for (const urlToAttempt of additionalURLs) { - yield urlToAttempt.href; - } - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A subclass of {@link workbox-routing.Route} that takes a - * {@link workbox-precaching.PrecacheController} - * instance and uses it to match incoming requests and handle fetching - * responses from the precache. - * - * @memberof workbox-precaching - * @extends workbox-routing.Route - */ - class PrecacheRoute extends Route { - /** - * @param {PrecacheController} precacheController A `PrecacheController` - * instance used to both match requests and respond to fetch events. - * @param {Object} [options] Options to control how requests are matched - * against the list of precached URLs. - * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will - * check cache entries for a URLs ending with '/' to see if there is a hit when - * appending the `directoryIndex` value. - * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An - * array of regex's to remove search params when looking for a cache match. - * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will - * check the cache for the URL with a `.html` added to the end of the end. - * @param {workbox-precaching~urlManipulation} [options.urlManipulation] - * This is a function that should take a URL and return an array of - * alternative URLs that should be checked for precache matches. - */ - constructor(precacheController, options) { - const match = ({ - request - }) => { - const urlsToCacheKeys = precacheController.getURLsToCacheKeys(); - for (const possibleURL of generateURLVariations(request.url, options)) { - const cacheKey = urlsToCacheKeys.get(possibleURL); - if (cacheKey) { - const integrity = precacheController.getIntegrityForCacheKey(cacheKey); - return { - cacheKey, - integrity - }; - } - } - { - logger.debug(`Precaching did not find a match for ` + getFriendlyURL(request.url)); - } - return; - }; - super(match, precacheController.strategy); - } - } - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Add a `fetch` listener to the service worker that will - * respond to - * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests} - * with precached assets. - * - * Requests for assets that aren't precached, the `FetchEvent` will not be - * responded to, allowing the event to fall through to other `fetch` event - * listeners. - * - * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute} - * options. - * - * @memberof workbox-precaching - */ - function addRoute(options) { - const precacheController = getOrCreatePrecacheController(); - const precacheRoute = new PrecacheRoute(precacheController, options); - registerRoute(precacheRoute); - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Adds items to the precache list, removing any duplicates and - * stores the files in the - * {@link workbox-core.cacheNames|"precache cache"} when the service - * worker installs. - * - * This method can be called multiple times. - * - * Please note: This method **will not** serve any of the cached files for you. - * It only precaches files. To respond to a network request you call - * {@link workbox-precaching.addRoute}. - * - * If you have a single array of files to precache, you can just call - * {@link workbox-precaching.precacheAndRoute}. - * - * @param {Array<Object|string>} [entries=[]] Array of entries to precache. - * - * @memberof workbox-precaching - */ - function precache(entries) { - const precacheController = getOrCreatePrecacheController(); - precacheController.precache(entries); - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * This method will add entries to the precache list and add a route to - * respond to fetch events. - * - * This is a convenience method that will call - * {@link workbox-precaching.precache} and - * {@link workbox-precaching.addRoute} in a single call. - * - * @param {Array<Object|string>} entries Array of entries to precache. - * @param {Object} [options] See the - * {@link workbox-precaching.PrecacheRoute} options. - * - * @memberof workbox-precaching - */ - function precacheAndRoute(entries, options) { - precache(entries); - addRoute(options); - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const SUBSTRING_TO_FIND = '-precache-'; - /** - * Cleans up incompatible precaches that were created by older versions of - * Workbox, by a service worker registered under the current scope. - * - * This is meant to be called as part of the `activate` event. - * - * This should be safe to use as long as you don't include `substringToFind` - * (defaulting to `-precache-`) in your non-precache cache names. - * - * @param {string} currentPrecacheName The cache name currently in use for - * precaching. This cache won't be deleted. - * @param {string} [substringToFind='-precache-'] Cache names which include this - * substring will be deleted (excluding `currentPrecacheName`). - * @return {Array<string>} A list of all the cache names that were deleted. - * - * @private - * @memberof workbox-precaching - */ - const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => { - const cacheNames = await self.caches.keys(); - const cacheNamesToDelete = cacheNames.filter(cacheName => { - return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName; - }); - await Promise.all(cacheNamesToDelete.map(cacheName => self.caches.delete(cacheName))); - return cacheNamesToDelete; - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Adds an `activate` event listener which will clean up incompatible - * precaches that were created by older versions of Workbox. - * - * @memberof workbox-precaching - */ - function cleanupOutdatedCaches() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('activate', event => { - const cacheName = cacheNames.getPrecacheName(); - event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => { - { - if (cachesDeleted.length > 0) { - logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted); - } - } - })); - }); - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * NavigationRoute makes it easy to create a - * {@link workbox-routing.Route} that matches for browser - * [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}. - * - * It will only match incoming Requests whose - * {@link https://fetch.spec.whatwg.org/#concept-request-mode|mode} - * is set to `navigate`. - * - * You can optionally only apply this route to a subset of navigation requests - * by using one or both of the `denylist` and `allowlist` parameters. - * - * @memberof workbox-routing - * @extends workbox-routing.Route - */ - class NavigationRoute extends Route { - /** - * If both `denylist` and `allowlist` are provided, the `denylist` will - * take precedence and the request will not match this route. - * - * The regular expressions in `allowlist` and `denylist` - * are matched against the concatenated - * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname} - * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search} - * portions of the requested URL. - * - * *Note*: These RegExps may be evaluated against every destination URL during - * a navigation. Avoid using - * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077), - * or else your users may see delays when navigating your site. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {Object} options - * @param {Array<RegExp>} [options.denylist] If any of these patterns match, - * the route will not handle the request (even if a allowlist RegExp matches). - * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns - * match the URL's pathname and search parameter, the route will handle the - * request (assuming the denylist doesn't match). - */ - constructor(handler, { - allowlist = [/./], - denylist = [] - } = {}) { - { - finalAssertExports.isArrayOfClass(allowlist, RegExp, { - moduleName: 'workbox-routing', - className: 'NavigationRoute', - funcName: 'constructor', - paramName: 'options.allowlist' - }); - finalAssertExports.isArrayOfClass(denylist, RegExp, { - moduleName: 'workbox-routing', - className: 'NavigationRoute', - funcName: 'constructor', - paramName: 'options.denylist' - }); - } - super(options => this._match(options), handler); - this._allowlist = allowlist; - this._denylist = denylist; - } - /** - * Routes match handler. - * - * @param {Object} options - * @param {URL} options.url - * @param {Request} options.request - * @return {boolean} - * - * @private - */ - _match({ - url, - request - }) { - if (request && request.mode !== 'navigate') { - return false; - } - const pathnameAndSearch = url.pathname + url.search; - for (const regExp of this._denylist) { - if (regExp.test(pathnameAndSearch)) { - { - logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL matches this denylist pattern: ` + `${regExp.toString()}`); - } - return false; - } - } - if (this._allowlist.some(regExp => regExp.test(pathnameAndSearch))) { - { - logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`); - } - return true; - } - { - logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL being navigated to doesn't ` + `match the allowlist.`); - } - return false; - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Helper function that calls - * {@link PrecacheController#createHandlerBoundToURL} on the default - * {@link PrecacheController} instance. - * - * If you are creating your own {@link PrecacheController}, then call the - * {@link PrecacheController#createHandlerBoundToURL} on that instance, - * instead of using this function. - * - * @param {string} url The precached URL which will be used to lookup the - * `Response`. - * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the - * response from the network if there's a precache miss. - * @return {workbox-routing~handlerCallback} - * - * @memberof workbox-precaching - */ - function createHandlerBoundToURL(url) { - const precacheController = getOrCreatePrecacheController(); - return precacheController.createHandlerBoundToURL(url); - } - - exports.CacheFirst = CacheFirst; - exports.CacheableResponsePlugin = CacheableResponsePlugin; - exports.NavigationRoute = NavigationRoute; - exports.NetworkOnly = NetworkOnly; - exports.cleanupOutdatedCaches = cleanupOutdatedCaches; - exports.clientsClaim = clientsClaim; - exports.createHandlerBoundToURL = createHandlerBoundToURL; - exports.precacheAndRoute = precacheAndRoute; - exports.registerRoute = registerRoute; - -})); -//# sourceMappingURL=workbox-4a2e5f00.js.map diff --git a/dev-dist/workbox-4a2e5f00.js.map b/dev-dist/workbox-4a2e5f00.js.map deleted file mode 100644 index b05d970..0000000 --- a/dev-dist/workbox-4a2e5f00.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"workbox-4a2e5f00.js","sources":["node_modules/workbox-core/_version.js","node_modules/workbox-core/_private/logger.js","node_modules/workbox-core/models/messages/messages.js","node_modules/workbox-core/models/messages/messageGenerator.js","node_modules/workbox-core/_private/WorkboxError.js","node_modules/workbox-core/_private/assert.js","node_modules/workbox-routing/_version.js","node_modules/workbox-routing/utils/constants.js","node_modules/workbox-routing/utils/normalizeHandler.js","node_modules/workbox-routing/Route.js","node_modules/workbox-routing/RegExpRoute.js","node_modules/workbox-core/_private/getFriendlyURL.js","node_modules/workbox-routing/Router.js","node_modules/workbox-routing/utils/getOrCreateDefaultRouter.js","node_modules/workbox-routing/registerRoute.js","node_modules/workbox-cacheable-response/_version.js","node_modules/workbox-cacheable-response/CacheableResponse.js","node_modules/workbox-cacheable-response/CacheableResponsePlugin.js","node_modules/workbox-core/_private/cacheNames.js","node_modules/workbox-core/_private/cacheMatchIgnoreParams.js","node_modules/workbox-core/_private/Deferred.js","node_modules/workbox-core/models/quotaErrorCallbacks.js","node_modules/workbox-core/_private/executeQuotaErrorCallbacks.js","node_modules/workbox-core/_private/timeout.js","node_modules/workbox-strategies/_version.js","node_modules/workbox-strategies/StrategyHandler.js","node_modules/workbox-strategies/Strategy.js","node_modules/workbox-strategies/utils/messages.js","node_modules/workbox-strategies/CacheFirst.js","node_modules/workbox-strategies/NetworkOnly.js","node_modules/workbox-core/clientsClaim.js","node_modules/workbox-core/_private/waitUntil.js","node_modules/workbox-precaching/_version.js","node_modules/workbox-precaching/utils/createCacheKey.js","node_modules/workbox-precaching/utils/PrecacheInstallReportPlugin.js","node_modules/workbox-precaching/utils/PrecacheCacheKeyPlugin.js","node_modules/workbox-precaching/utils/printCleanupDetails.js","node_modules/workbox-precaching/utils/printInstallDetails.js","node_modules/workbox-core/_private/canConstructResponseFromBodyStream.js","node_modules/workbox-core/copyResponse.js","node_modules/workbox-precaching/PrecacheStrategy.js","node_modules/workbox-precaching/PrecacheController.js","node_modules/workbox-precaching/utils/getOrCreatePrecacheController.js","node_modules/workbox-precaching/utils/removeIgnoredSearchParams.js","node_modules/workbox-precaching/utils/generateURLVariations.js","node_modules/workbox-precaching/PrecacheRoute.js","node_modules/workbox-precaching/addRoute.js","node_modules/workbox-precaching/precache.js","node_modules/workbox-precaching/precacheAndRoute.js","node_modules/workbox-precaching/utils/deleteOutdatedCaches.js","node_modules/workbox-precaching/cleanupOutdatedCaches.js","node_modules/workbox-routing/NavigationRoute.js","node_modules/workbox-precaching/createHandlerBoundToURL.js"],"sourcesContent":["\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:core:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst logger = (process.env.NODE_ENV === 'production'\n ? null\n : (() => {\n // Don't overwrite this value if it's already set.\n // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923\n if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) {\n self.__WB_DISABLE_DEV_LOGS = false;\n }\n let inGroup = false;\n const methodToColorMap = {\n debug: `#7f8c8d`,\n log: `#2ecc71`,\n warn: `#f39c12`,\n error: `#c0392b`,\n groupCollapsed: `#3498db`,\n groupEnd: null, // No colored prefix on groupEnd\n };\n const print = function (method, args) {\n if (self.__WB_DISABLE_DEV_LOGS) {\n return;\n }\n if (method === 'groupCollapsed') {\n // Safari doesn't print all console.groupCollapsed() arguments:\n // https://bugs.webkit.org/show_bug.cgi?id=182754\n if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {\n console[method](...args);\n return;\n }\n }\n const styles = [\n `background: ${methodToColorMap[method]}`,\n `border-radius: 0.5em`,\n `color: white`,\n `font-weight: bold`,\n `padding: 2px 0.5em`,\n ];\n // When in a group, the workbox prefix is not displayed.\n const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];\n console[method](...logPrefix, ...args);\n if (method === 'groupCollapsed') {\n inGroup = true;\n }\n if (method === 'groupEnd') {\n inGroup = false;\n }\n };\n // eslint-disable-next-line @typescript-eslint/ban-types\n const api = {};\n const loggerMethods = Object.keys(methodToColorMap);\n for (const key of loggerMethods) {\n const method = key;\n api[method] = (...args) => {\n print(method, args);\n };\n }\n return api;\n })());\nexport { logger };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../../_version.js';\nexport const messages = {\n 'invalid-value': ({ paramName, validValueDescription, value }) => {\n if (!paramName || !validValueDescription) {\n throw new Error(`Unexpected input to 'invalid-value' error.`);\n }\n return (`The '${paramName}' parameter was given a value with an ` +\n `unexpected value. ${validValueDescription} Received a value of ` +\n `${JSON.stringify(value)}.`);\n },\n 'not-an-array': ({ moduleName, className, funcName, paramName }) => {\n if (!moduleName || !className || !funcName || !paramName) {\n throw new Error(`Unexpected input to 'not-an-array' error.`);\n }\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${className}.${funcName}()' must be an array.`);\n },\n 'incorrect-type': ({ expectedType, paramName, moduleName, className, funcName, }) => {\n if (!expectedType || !paramName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'incorrect-type' error.`);\n }\n const classNameStr = className ? `${className}.` : '';\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${classNameStr}` +\n `${funcName}()' must be of type ${expectedType}.`);\n },\n 'incorrect-class': ({ expectedClassName, paramName, moduleName, className, funcName, isReturnValueProblem, }) => {\n if (!expectedClassName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'incorrect-class' error.`);\n }\n const classNameStr = className ? `${className}.` : '';\n if (isReturnValueProblem) {\n return (`The return value from ` +\n `'${moduleName}.${classNameStr}${funcName}()' ` +\n `must be an instance of class ${expectedClassName}.`);\n }\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${classNameStr}${funcName}()' ` +\n `must be an instance of class ${expectedClassName}.`);\n },\n 'missing-a-method': ({ expectedMethod, paramName, moduleName, className, funcName, }) => {\n if (!expectedMethod ||\n !paramName ||\n !moduleName ||\n !className ||\n !funcName) {\n throw new Error(`Unexpected input to 'missing-a-method' error.`);\n }\n return (`${moduleName}.${className}.${funcName}() expected the ` +\n `'${paramName}' parameter to expose a '${expectedMethod}' method.`);\n },\n 'add-to-cache-list-unexpected-type': ({ entry }) => {\n return (`An unexpected entry was passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` +\n `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` +\n `strings with one or more characters, objects with a url property or ` +\n `Request objects.`);\n },\n 'add-to-cache-list-conflicting-entries': ({ firstEntry, secondEntry }) => {\n if (!firstEntry || !secondEntry) {\n throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`);\n }\n return (`Two of the entries passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` +\n `${firstEntry} but different revision details. Workbox is ` +\n `unable to cache and version the asset correctly. Please remove one ` +\n `of the entries.`);\n },\n 'plugin-error-request-will-fetch': ({ thrownErrorMessage }) => {\n if (!thrownErrorMessage) {\n throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`);\n }\n return (`An error was thrown by a plugins 'requestWillFetch()' method. ` +\n `The thrown error message was: '${thrownErrorMessage}'.`);\n },\n 'invalid-cache-name': ({ cacheNameId, value }) => {\n if (!cacheNameId) {\n throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`);\n }\n return (`You must provide a name containing at least one character for ` +\n `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` +\n `'${JSON.stringify(value)}'`);\n },\n 'unregister-route-but-not-found-with-method': ({ method }) => {\n if (!method) {\n throw new Error(`Unexpected input to ` +\n `'unregister-route-but-not-found-with-method' error.`);\n }\n return (`The route you're trying to unregister was not previously ` +\n `registered for the method type '${method}'.`);\n },\n 'unregister-route-route-not-registered': () => {\n return (`The route you're trying to unregister was not previously ` +\n `registered.`);\n },\n 'queue-replay-failed': ({ name }) => {\n return `Replaying the background sync queue '${name}' failed.`;\n },\n 'duplicate-queue-name': ({ name }) => {\n return (`The Queue name '${name}' is already being used. ` +\n `All instances of backgroundSync.Queue must be given unique names.`);\n },\n 'expired-test-without-max-age': ({ methodName, paramName }) => {\n return (`The '${methodName}()' method can only be used when the ` +\n `'${paramName}' is used in the constructor.`);\n },\n 'unsupported-route-type': ({ moduleName, className, funcName, paramName }) => {\n return (`The supplied '${paramName}' parameter was an unsupported type. ` +\n `Please check the docs for ${moduleName}.${className}.${funcName} for ` +\n `valid input types.`);\n },\n 'not-array-of-class': ({ value, expectedClass, moduleName, className, funcName, paramName, }) => {\n return (`The supplied '${paramName}' parameter must be an array of ` +\n `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` +\n `Please check the call to ${moduleName}.${className}.${funcName}() ` +\n `to fix the issue.`);\n },\n 'max-entries-or-age-required': ({ moduleName, className, funcName }) => {\n return (`You must define either config.maxEntries or config.maxAgeSeconds` +\n `in ${moduleName}.${className}.${funcName}`);\n },\n 'statuses-or-headers-required': ({ moduleName, className, funcName }) => {\n return (`You must define either config.statuses or config.headers` +\n `in ${moduleName}.${className}.${funcName}`);\n },\n 'invalid-string': ({ moduleName, funcName, paramName }) => {\n if (!paramName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'invalid-string' error.`);\n }\n return (`When using strings, the '${paramName}' parameter must start with ` +\n `'http' (for cross-origin matches) or '/' (for same-origin matches). ` +\n `Please see the docs for ${moduleName}.${funcName}() for ` +\n `more info.`);\n },\n 'channel-name-required': () => {\n return (`You must provide a channelName to construct a ` +\n `BroadcastCacheUpdate instance.`);\n },\n 'invalid-responses-are-same-args': () => {\n return (`The arguments passed into responsesAreSame() appear to be ` +\n `invalid. Please ensure valid Responses are used.`);\n },\n 'expire-custom-caches-only': () => {\n return (`You must provide a 'cacheName' property when using the ` +\n `expiration plugin with a runtime caching strategy.`);\n },\n 'unit-must-be-bytes': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`);\n }\n return (`The 'unit' portion of the Range header must be set to 'bytes'. ` +\n `The Range header provided was \"${normalizedRangeHeader}\"`);\n },\n 'single-range-only': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'single-range-only' error.`);\n }\n return (`Multiple ranges are not supported. Please use a single start ` +\n `value, and optional end value. The Range header provided was ` +\n `\"${normalizedRangeHeader}\"`);\n },\n 'invalid-range-values': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'invalid-range-values' error.`);\n }\n return (`The Range header is missing both start and end values. At least ` +\n `one of those values is needed. The Range header provided was ` +\n `\"${normalizedRangeHeader}\"`);\n },\n 'no-range-header': () => {\n return `No Range header was found in the Request provided.`;\n },\n 'range-not-satisfiable': ({ size, start, end }) => {\n return (`The start (${start}) and end (${end}) values in the Range are ` +\n `not satisfiable by the cached response, which is ${size} bytes.`);\n },\n 'attempt-to-cache-non-get-request': ({ url, method }) => {\n return (`Unable to cache '${url}' because it is a '${method}' request and ` +\n `only 'GET' requests can be cached.`);\n },\n 'cache-put-with-no-response': ({ url }) => {\n return (`There was an attempt to cache '${url}' but the response was not ` +\n `defined.`);\n },\n 'no-response': ({ url, error }) => {\n let message = `The strategy could not generate a response for '${url}'.`;\n if (error) {\n message += ` The underlying error is ${error}.`;\n }\n return message;\n },\n 'bad-precaching-response': ({ url, status }) => {\n return (`The precaching request for '${url}' failed` +\n (status ? ` with an HTTP status of ${status}.` : `.`));\n },\n 'non-precached-url': ({ url }) => {\n return (`createHandlerBoundToURL('${url}') was called, but that URL is ` +\n `not precached. Please pass in a URL that is precached instead.`);\n },\n 'add-to-cache-list-conflicting-integrities': ({ url }) => {\n return (`Two of the entries passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` +\n `${url} with different integrity values. Please remove one of them.`);\n },\n 'missing-precache-entry': ({ cacheName, url }) => {\n return `Unable to find a precached response in ${cacheName} for ${url}.`;\n },\n 'cross-origin-copy-response': ({ origin }) => {\n return (`workbox-core.copyResponse() can only be used with same-origin ` +\n `responses. It was passed a response with origin ${origin}.`);\n },\n 'opaque-streams-source': ({ type }) => {\n const message = `One of the workbox-streams sources resulted in an ` +\n `'${type}' response.`;\n if (type === 'opaqueredirect') {\n return (`${message} Please do not use a navigation request that results ` +\n `in a redirect as a source.`);\n }\n return `${message} Please ensure your sources are CORS-enabled.`;\n },\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { messages } from './messages.js';\nimport '../../_version.js';\nconst fallback = (code, ...args) => {\n let msg = code;\n if (args.length > 0) {\n msg += ` :: ${JSON.stringify(args)}`;\n }\n return msg;\n};\nconst generatorFunction = (code, details = {}) => {\n const message = messages[code];\n if (!message) {\n throw new Error(`Unable to find message for code '${code}'.`);\n }\n return message(details);\n};\nexport const messageGenerator = process.env.NODE_ENV === 'production' ? fallback : generatorFunction;\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { messageGenerator } from '../models/messages/messageGenerator.js';\nimport '../_version.js';\n/**\n * Workbox errors should be thrown with this class.\n * This allows use to ensure the type easily in tests,\n * helps developers identify errors from workbox\n * easily and allows use to optimise error\n * messages correctly.\n *\n * @private\n */\nclass WorkboxError extends Error {\n /**\n *\n * @param {string} errorCode The error code that\n * identifies this particular error.\n * @param {Object=} details Any relevant arguments\n * that will help developers identify issues should\n * be added as a key on the context object.\n */\n constructor(errorCode, details) {\n const message = messageGenerator(errorCode, details);\n super(message);\n this.name = errorCode;\n this.details = details;\n }\n}\nexport { WorkboxError };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { WorkboxError } from '../_private/WorkboxError.js';\nimport '../_version.js';\n/*\n * This method throws if the supplied value is not an array.\n * The destructed values are required to produce a meaningful error for users.\n * The destructed and restructured object is so it's clear what is\n * needed.\n */\nconst isArray = (value, details) => {\n if (!Array.isArray(value)) {\n throw new WorkboxError('not-an-array', details);\n }\n};\nconst hasMethod = (object, expectedMethod, details) => {\n const type = typeof object[expectedMethod];\n if (type !== 'function') {\n details['expectedMethod'] = expectedMethod;\n throw new WorkboxError('missing-a-method', details);\n }\n};\nconst isType = (object, expectedType, details) => {\n if (typeof object !== expectedType) {\n details['expectedType'] = expectedType;\n throw new WorkboxError('incorrect-type', details);\n }\n};\nconst isInstance = (object, \n// Need the general type to do the check later.\n// eslint-disable-next-line @typescript-eslint/ban-types\nexpectedClass, details) => {\n if (!(object instanceof expectedClass)) {\n details['expectedClassName'] = expectedClass.name;\n throw new WorkboxError('incorrect-class', details);\n }\n};\nconst isOneOf = (value, validValues, details) => {\n if (!validValues.includes(value)) {\n details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`;\n throw new WorkboxError('invalid-value', details);\n }\n};\nconst isArrayOfClass = (value, \n// Need general type to do check later.\nexpectedClass, // eslint-disable-line\ndetails) => {\n const error = new WorkboxError('not-array-of-class', details);\n if (!Array.isArray(value)) {\n throw error;\n }\n for (const item of value) {\n if (!(item instanceof expectedClass)) {\n throw error;\n }\n }\n};\nconst finalAssertExports = process.env.NODE_ENV === 'production'\n ? null\n : {\n hasMethod,\n isArray,\n isInstance,\n isOneOf,\n isType,\n isArrayOfClass,\n };\nexport { finalAssertExports as assert };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:routing:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * The default HTTP method, 'GET', used when there's no specific method\n * configured for a route.\n *\n * @type {string}\n *\n * @private\n */\nexport const defaultMethod = 'GET';\n/**\n * The list of valid HTTP methods associated with requests that could be routed.\n *\n * @type {Array<string>}\n *\n * @private\n */\nexport const validMethods = [\n 'DELETE',\n 'GET',\n 'HEAD',\n 'PATCH',\n 'POST',\n 'PUT',\n];\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport '../_version.js';\n/**\n * @param {function()|Object} handler Either a function, or an object with a\n * 'handle' method.\n * @return {Object} An object with a handle method.\n *\n * @private\n */\nexport const normalizeHandler = (handler) => {\n if (handler && typeof handler === 'object') {\n if (process.env.NODE_ENV !== 'production') {\n assert.hasMethod(handler, 'handle', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'handler',\n });\n }\n return handler;\n }\n else {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(handler, 'function', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'handler',\n });\n }\n return { handle: handler };\n }\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { defaultMethod, validMethods } from './utils/constants.js';\nimport { normalizeHandler } from './utils/normalizeHandler.js';\nimport './_version.js';\n/**\n * A `Route` consists of a pair of callback functions, \"match\" and \"handler\".\n * The \"match\" callback determine if a route should be used to \"handle\" a\n * request by returning a non-falsy value if it can. The \"handler\" callback\n * is called when there is a match and should return a Promise that resolves\n * to a `Response`.\n *\n * @memberof workbox-routing\n */\nclass Route {\n /**\n * Constructor for Route class.\n *\n * @param {workbox-routing~matchCallback} match\n * A callback function that determines whether the route matches a given\n * `fetch` event by returning a non-falsy value.\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resolving to a Response.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n */\n constructor(match, handler, method = defaultMethod) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(match, 'function', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'match',\n });\n if (method) {\n assert.isOneOf(method, validMethods, { paramName: 'method' });\n }\n }\n // These values are referenced directly by Router so cannot be\n // altered by minificaton.\n this.handler = normalizeHandler(handler);\n this.match = match;\n this.method = method;\n }\n /**\n *\n * @param {workbox-routing-handlerCallback} handler A callback\n * function that returns a Promise resolving to a Response\n */\n setCatchHandler(handler) {\n this.catchHandler = normalizeHandler(handler);\n }\n}\nexport { Route };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { Route } from './Route.js';\nimport './_version.js';\n/**\n * RegExpRoute makes it easy to create a regular expression based\n * {@link workbox-routing.Route}.\n *\n * For same-origin requests the RegExp only needs to match part of the URL. For\n * requests against third-party servers, you must define a RegExp that matches\n * the start of the URL.\n *\n * @memberof workbox-routing\n * @extends workbox-routing.Route\n */\nclass RegExpRoute extends Route {\n /**\n * If the regular expression contains\n * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},\n * the captured values will be passed to the\n * {@link workbox-routing~handlerCallback} `params`\n * argument.\n *\n * @param {RegExp} regExp The regular expression to match against URLs.\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n */\n constructor(regExp, handler, method) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(regExp, RegExp, {\n moduleName: 'workbox-routing',\n className: 'RegExpRoute',\n funcName: 'constructor',\n paramName: 'pattern',\n });\n }\n const match = ({ url }) => {\n const result = regExp.exec(url.href);\n // Return immediately if there's no match.\n if (!result) {\n return;\n }\n // Require that the match start at the first character in the URL string\n // if it's a cross-origin request.\n // See https://github.com/GoogleChrome/workbox/issues/281 for the context\n // behind this behavior.\n if (url.origin !== location.origin && result.index !== 0) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`The regular expression '${regExp.toString()}' only partially matched ` +\n `against the cross-origin URL '${url.toString()}'. RegExpRoute's will only ` +\n `handle cross-origin requests if they match the entire URL.`);\n }\n return;\n }\n // If the route matches, but there aren't any capture groups defined, then\n // this will return [], which is truthy and therefore sufficient to\n // indicate a match.\n // If there are capture groups, then it will return their values.\n return result.slice(1);\n };\n super(match, handler, method);\n }\n}\nexport { RegExpRoute };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst getFriendlyURL = (url) => {\n const urlObj = new URL(String(url), location.href);\n // See https://github.com/GoogleChrome/workbox/issues/2323\n // We want to include everything, except for the origin if it's same-origin.\n return urlObj.href.replace(new RegExp(`^${location.origin}`), '');\n};\nexport { getFriendlyURL };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { defaultMethod } from './utils/constants.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { normalizeHandler } from './utils/normalizeHandler.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport './_version.js';\n/**\n * The Router can be used to process a `FetchEvent` using one or more\n * {@link workbox-routing.Route}, responding with a `Response` if\n * a matching route exists.\n *\n * If no route matches a given a request, the Router will use a \"default\"\n * handler if one is defined.\n *\n * Should the matching Route throw an error, the Router will use a \"catch\"\n * handler if one is defined to gracefully deal with issues and respond with a\n * Request.\n *\n * If a request matches multiple routes, the **earliest** registered route will\n * be used to respond to the request.\n *\n * @memberof workbox-routing\n */\nclass Router {\n /**\n * Initializes a new Router.\n */\n constructor() {\n this._routes = new Map();\n this._defaultHandlerMap = new Map();\n }\n /**\n * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP\n * method name ('GET', etc.) to an array of all the corresponding `Route`\n * instances that are registered.\n */\n get routes() {\n return this._routes;\n }\n /**\n * Adds a fetch event listener to respond to events when a route matches\n * the event's request.\n */\n addFetchListener() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('fetch', ((event) => {\n const { request } = event;\n const responsePromise = this.handleRequest({ request, event });\n if (responsePromise) {\n event.respondWith(responsePromise);\n }\n }));\n }\n /**\n * Adds a message event listener for URLs to cache from the window.\n * This is useful to cache resources loaded on the page prior to when the\n * service worker started controlling it.\n *\n * The format of the message data sent from the window should be as follows.\n * Where the `urlsToCache` array may consist of URL strings or an array of\n * URL string + `requestInit` object (the same as you'd pass to `fetch()`).\n *\n * ```\n * {\n * type: 'CACHE_URLS',\n * payload: {\n * urlsToCache: [\n * './script1.js',\n * './script2.js',\n * ['./script3.js', {mode: 'no-cors'}],\n * ],\n * },\n * }\n * ```\n */\n addCacheListener() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('message', ((event) => {\n // event.data is type 'any'\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (event.data && event.data.type === 'CACHE_URLS') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { payload } = event.data;\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Caching URLs from the window`, payload.urlsToCache);\n }\n const requestPromises = Promise.all(payload.urlsToCache.map((entry) => {\n if (typeof entry === 'string') {\n entry = [entry];\n }\n const request = new Request(...entry);\n return this.handleRequest({ request, event });\n // TODO(philipwalton): TypeScript errors without this typecast for\n // some reason (probably a bug). The real type here should work but\n // doesn't: `Array<Promise<Response> | undefined>`.\n })); // TypeScript\n event.waitUntil(requestPromises);\n // If a MessageChannel was used, reply to the message on success.\n if (event.ports && event.ports[0]) {\n void requestPromises.then(() => event.ports[0].postMessage(true));\n }\n }\n }));\n }\n /**\n * Apply the routing rules to a FetchEvent object to get a Response from an\n * appropriate Route's handler.\n *\n * @param {Object} options\n * @param {Request} options.request The request to handle.\n * @param {ExtendableEvent} options.event The event that triggered the\n * request.\n * @return {Promise<Response>|undefined} A promise is returned if a\n * registered route can handle the request. If there is no matching\n * route and there's no `defaultHandler`, `undefined` is returned.\n */\n handleRequest({ request, event, }) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'handleRequest',\n paramName: 'options.request',\n });\n }\n const url = new URL(request.url, location.href);\n if (!url.protocol.startsWith('http')) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Workbox Router only supports URLs that start with 'http'.`);\n }\n return;\n }\n const sameOrigin = url.origin === location.origin;\n const { params, route } = this.findMatchingRoute({\n event,\n request,\n sameOrigin,\n url,\n });\n let handler = route && route.handler;\n const debugMessages = [];\n if (process.env.NODE_ENV !== 'production') {\n if (handler) {\n debugMessages.push([`Found a route to handle this request:`, route]);\n if (params) {\n debugMessages.push([\n `Passing the following params to the route's handler:`,\n params,\n ]);\n }\n }\n }\n // If we don't have a handler because there was no matching route, then\n // fall back to defaultHandler if that's defined.\n const method = request.method;\n if (!handler && this._defaultHandlerMap.has(method)) {\n if (process.env.NODE_ENV !== 'production') {\n debugMessages.push(`Failed to find a matching route. Falling ` +\n `back to the default handler for ${method}.`);\n }\n handler = this._defaultHandlerMap.get(method);\n }\n if (!handler) {\n if (process.env.NODE_ENV !== 'production') {\n // No handler so Workbox will do nothing. If logs is set of debug\n // i.e. verbose, we should print out this information.\n logger.debug(`No route found for: ${getFriendlyURL(url)}`);\n }\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // We have a handler, meaning Workbox is going to handle the route.\n // print the routing details to the console.\n logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);\n debugMessages.forEach((msg) => {\n if (Array.isArray(msg)) {\n logger.log(...msg);\n }\n else {\n logger.log(msg);\n }\n });\n logger.groupEnd();\n }\n // Wrap in try and catch in case the handle method throws a synchronous\n // error. It should still callback to the catch handler.\n let responsePromise;\n try {\n responsePromise = handler.handle({ url, request, event, params });\n }\n catch (err) {\n responsePromise = Promise.reject(err);\n }\n // Get route's catch handler, if it exists\n const catchHandler = route && route.catchHandler;\n if (responsePromise instanceof Promise &&\n (this._catchHandler || catchHandler)) {\n responsePromise = responsePromise.catch(async (err) => {\n // If there's a route catch handler, process that first\n if (catchHandler) {\n if (process.env.NODE_ENV !== 'production') {\n // Still include URL here as it will be async from the console group\n // and may not make sense without the URL\n logger.groupCollapsed(`Error thrown when responding to: ` +\n ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);\n logger.error(`Error thrown by:`, route);\n logger.error(err);\n logger.groupEnd();\n }\n try {\n return await catchHandler.handle({ url, request, event, params });\n }\n catch (catchErr) {\n if (catchErr instanceof Error) {\n err = catchErr;\n }\n }\n }\n if (this._catchHandler) {\n if (process.env.NODE_ENV !== 'production') {\n // Still include URL here as it will be async from the console group\n // and may not make sense without the URL\n logger.groupCollapsed(`Error thrown when responding to: ` +\n ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);\n logger.error(`Error thrown by:`, route);\n logger.error(err);\n logger.groupEnd();\n }\n return this._catchHandler.handle({ url, request, event });\n }\n throw err;\n });\n }\n return responsePromise;\n }\n /**\n * Checks a request and URL (and optionally an event) against the list of\n * registered routes, and if there's a match, returns the corresponding\n * route along with any params generated by the match.\n *\n * @param {Object} options\n * @param {URL} options.url\n * @param {boolean} options.sameOrigin The result of comparing `url.origin`\n * against the current origin.\n * @param {Request} options.request The request to match.\n * @param {Event} options.event The corresponding event.\n * @return {Object} An object with `route` and `params` properties.\n * They are populated if a matching route was found or `undefined`\n * otherwise.\n */\n findMatchingRoute({ url, sameOrigin, request, event, }) {\n const routes = this._routes.get(request.method) || [];\n for (const route of routes) {\n let params;\n // route.match returns type any, not possible to change right now.\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const matchResult = route.match({ url, sameOrigin, request, event });\n if (matchResult) {\n if (process.env.NODE_ENV !== 'production') {\n // Warn developers that using an async matchCallback is almost always\n // not the right thing to do.\n if (matchResult instanceof Promise) {\n logger.warn(`While routing ${getFriendlyURL(url)}, an async ` +\n `matchCallback function was used. Please convert the ` +\n `following route to use a synchronous matchCallback function:`, route);\n }\n }\n // See https://github.com/GoogleChrome/workbox/issues/2079\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n params = matchResult;\n if (Array.isArray(params) && params.length === 0) {\n // Instead of passing an empty array in as params, use undefined.\n params = undefined;\n }\n else if (matchResult.constructor === Object && // eslint-disable-line\n Object.keys(matchResult).length === 0) {\n // Instead of passing an empty object in as params, use undefined.\n params = undefined;\n }\n else if (typeof matchResult === 'boolean') {\n // For the boolean value true (rather than just something truth-y),\n // don't set params.\n // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353\n params = undefined;\n }\n // Return early if have a match.\n return { route, params };\n }\n }\n // If no match was found above, return and empty object.\n return {};\n }\n /**\n * Define a default `handler` that's called when no routes explicitly\n * match the incoming request.\n *\n * Each HTTP method ('GET', 'POST', etc.) gets its own default handler.\n *\n * Without a default handler, unmatched requests will go against the\n * network as if there were no service worker present.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {string} [method='GET'] The HTTP method to associate with this\n * default handler. Each method has its own default.\n */\n setDefaultHandler(handler, method = defaultMethod) {\n this._defaultHandlerMap.set(method, normalizeHandler(handler));\n }\n /**\n * If a Route throws an error while handling a request, this `handler`\n * will be called and given a chance to provide a response.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n */\n setCatchHandler(handler) {\n this._catchHandler = normalizeHandler(handler);\n }\n /**\n * Registers a route with the router.\n *\n * @param {workbox-routing.Route} route The route to register.\n */\n registerRoute(route) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(route, 'object', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.hasMethod(route, 'match', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.isType(route.handler, 'object', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.hasMethod(route.handler, 'handle', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route.handler',\n });\n assert.isType(route.method, 'string', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route.method',\n });\n }\n if (!this._routes.has(route.method)) {\n this._routes.set(route.method, []);\n }\n // Give precedence to all of the earlier routes by adding this additional\n // route to the end of the array.\n this._routes.get(route.method).push(route);\n }\n /**\n * Unregisters a route with the router.\n *\n * @param {workbox-routing.Route} route The route to unregister.\n */\n unregisterRoute(route) {\n if (!this._routes.has(route.method)) {\n throw new WorkboxError('unregister-route-but-not-found-with-method', {\n method: route.method,\n });\n }\n const routeIndex = this._routes.get(route.method).indexOf(route);\n if (routeIndex > -1) {\n this._routes.get(route.method).splice(routeIndex, 1);\n }\n else {\n throw new WorkboxError('unregister-route-route-not-registered');\n }\n }\n}\nexport { Router };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { Router } from '../Router.js';\nimport '../_version.js';\nlet defaultRouter;\n/**\n * Creates a new, singleton Router instance if one does not exist. If one\n * does already exist, that instance is returned.\n *\n * @private\n * @return {Router}\n */\nexport const getOrCreateDefaultRouter = () => {\n if (!defaultRouter) {\n defaultRouter = new Router();\n // The helpers that use the default Router assume these listeners exist.\n defaultRouter.addFetchListener();\n defaultRouter.addCacheListener();\n }\n return defaultRouter;\n};\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Route } from './Route.js';\nimport { RegExpRoute } from './RegExpRoute.js';\nimport { getOrCreateDefaultRouter } from './utils/getOrCreateDefaultRouter.js';\nimport './_version.js';\n/**\n * Easily register a RegExp, string, or function with a caching\n * strategy to a singleton Router instance.\n *\n * This method will generate a Route for you if needed and\n * call {@link workbox-routing.Router#registerRoute}.\n *\n * @param {RegExp|string|workbox-routing.Route~matchCallback|workbox-routing.Route} capture\n * If the capture param is a `Route`, all other arguments will be ignored.\n * @param {workbox-routing~handlerCallback} [handler] A callback\n * function that returns a Promise resulting in a Response. This parameter\n * is required if `capture` is not a `Route` object.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n * @return {workbox-routing.Route} The generated `Route`.\n *\n * @memberof workbox-routing\n */\nfunction registerRoute(capture, handler, method) {\n let route;\n if (typeof capture === 'string') {\n const captureUrl = new URL(capture, location.href);\n if (process.env.NODE_ENV !== 'production') {\n if (!(capture.startsWith('/') || capture.startsWith('http'))) {\n throw new WorkboxError('invalid-string', {\n moduleName: 'workbox-routing',\n funcName: 'registerRoute',\n paramName: 'capture',\n });\n }\n // We want to check if Express-style wildcards are in the pathname only.\n // TODO: Remove this log message in v4.\n const valueToCheck = capture.startsWith('http')\n ? captureUrl.pathname\n : capture;\n // See https://github.com/pillarjs/path-to-regexp#parameters\n const wildcards = '[*:?+]';\n if (new RegExp(`${wildcards}`).exec(valueToCheck)) {\n logger.debug(`The '$capture' parameter contains an Express-style wildcard ` +\n `character (${wildcards}). Strings are now always interpreted as ` +\n `exact matches; use a RegExp for partial or wildcard matches.`);\n }\n }\n const matchCallback = ({ url }) => {\n if (process.env.NODE_ENV !== 'production') {\n if (url.pathname === captureUrl.pathname &&\n url.origin !== captureUrl.origin) {\n logger.debug(`${capture} only partially matches the cross-origin URL ` +\n `${url.toString()}. This route will only handle cross-origin requests ` +\n `if they match the entire URL.`);\n }\n }\n return url.href === captureUrl.href;\n };\n // If `capture` is a string then `handler` and `method` must be present.\n route = new Route(matchCallback, handler, method);\n }\n else if (capture instanceof RegExp) {\n // If `capture` is a `RegExp` then `handler` and `method` must be present.\n route = new RegExpRoute(capture, handler, method);\n }\n else if (typeof capture === 'function') {\n // If `capture` is a function then `handler` and `method` must be present.\n route = new Route(capture, handler, method);\n }\n else if (capture instanceof Route) {\n route = capture;\n }\n else {\n throw new WorkboxError('unsupported-route-type', {\n moduleName: 'workbox-routing',\n funcName: 'registerRoute',\n paramName: 'capture',\n });\n }\n const defaultRouter = getOrCreateDefaultRouter();\n defaultRouter.registerRoute(route);\n return route;\n}\nexport { registerRoute };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:cacheable-response:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport './_version.js';\n/**\n * This class allows you to set up rules determining what\n * status codes and/or headers need to be present in order for a\n * [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)\n * to be considered cacheable.\n *\n * @memberof workbox-cacheable-response\n */\nclass CacheableResponse {\n /**\n * To construct a new CacheableResponse instance you must provide at least\n * one of the `config` properties.\n *\n * If both `statuses` and `headers` are specified, then both conditions must\n * be met for the `Response` to be considered cacheable.\n *\n * @param {Object} config\n * @param {Array<number>} [config.statuses] One or more status codes that a\n * `Response` can have and be considered cacheable.\n * @param {Object<string,string>} [config.headers] A mapping of header names\n * and expected values that a `Response` can have and be considered cacheable.\n * If multiple headers are provided, only one needs to be present.\n */\n constructor(config = {}) {\n if (process.env.NODE_ENV !== 'production') {\n if (!(config.statuses || config.headers)) {\n throw new WorkboxError('statuses-or-headers-required', {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n });\n }\n if (config.statuses) {\n assert.isArray(config.statuses, {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n paramName: 'config.statuses',\n });\n }\n if (config.headers) {\n assert.isType(config.headers, 'object', {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n paramName: 'config.headers',\n });\n }\n }\n this._statuses = config.statuses;\n this._headers = config.headers;\n }\n /**\n * Checks a response to see whether it's cacheable or not, based on this\n * object's configuration.\n *\n * @param {Response} response The response whose cacheability is being\n * checked.\n * @return {boolean} `true` if the `Response` is cacheable, and `false`\n * otherwise.\n */\n isResponseCacheable(response) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(response, Response, {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'isResponseCacheable',\n paramName: 'response',\n });\n }\n let cacheable = true;\n if (this._statuses) {\n cacheable = this._statuses.includes(response.status);\n }\n if (this._headers && cacheable) {\n cacheable = Object.keys(this._headers).some((headerName) => {\n return response.headers.get(headerName) === this._headers[headerName];\n });\n }\n if (process.env.NODE_ENV !== 'production') {\n if (!cacheable) {\n logger.groupCollapsed(`The request for ` +\n `'${getFriendlyURL(response.url)}' returned a response that does ` +\n `not meet the criteria for being cached.`);\n logger.groupCollapsed(`View cacheability criteria here.`);\n logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses));\n logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2));\n logger.groupEnd();\n const logFriendlyHeaders = {};\n response.headers.forEach((value, key) => {\n logFriendlyHeaders[key] = value;\n });\n logger.groupCollapsed(`View response status and headers here.`);\n logger.log(`Response status: ${response.status}`);\n logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2));\n logger.groupEnd();\n logger.groupCollapsed(`View full response details here.`);\n logger.log(response.headers);\n logger.log(response);\n logger.groupEnd();\n logger.groupEnd();\n }\n }\n return cacheable;\n }\n}\nexport { CacheableResponse };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { CacheableResponse, } from './CacheableResponse.js';\nimport './_version.js';\n/**\n * A class implementing the `cacheWillUpdate` lifecycle callback. This makes it\n * easier to add in cacheability checks to requests made via Workbox's built-in\n * strategies.\n *\n * @memberof workbox-cacheable-response\n */\nclass CacheableResponsePlugin {\n /**\n * To construct a new CacheableResponsePlugin instance you must provide at\n * least one of the `config` properties.\n *\n * If both `statuses` and `headers` are specified, then both conditions must\n * be met for the `Response` to be considered cacheable.\n *\n * @param {Object} config\n * @param {Array<number>} [config.statuses] One or more status codes that a\n * `Response` can have and be considered cacheable.\n * @param {Object<string,string>} [config.headers] A mapping of header names\n * and expected values that a `Response` can have and be considered cacheable.\n * If multiple headers are provided, only one needs to be present.\n */\n constructor(config) {\n /**\n * @param {Object} options\n * @param {Response} options.response\n * @return {Response|null}\n * @private\n */\n this.cacheWillUpdate = async ({ response }) => {\n if (this._cacheableResponse.isResponseCacheable(response)) {\n return response;\n }\n return null;\n };\n this._cacheableResponse = new CacheableResponse(config);\n }\n}\nexport { CacheableResponsePlugin };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst _cacheNameDetails = {\n googleAnalytics: 'googleAnalytics',\n precache: 'precache-v2',\n prefix: 'workbox',\n runtime: 'runtime',\n suffix: typeof registration !== 'undefined' ? registration.scope : '',\n};\nconst _createCacheName = (cacheName) => {\n return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix]\n .filter((value) => value && value.length > 0)\n .join('-');\n};\nconst eachCacheNameDetail = (fn) => {\n for (const key of Object.keys(_cacheNameDetails)) {\n fn(key);\n }\n};\nexport const cacheNames = {\n updateDetails: (details) => {\n eachCacheNameDetail((key) => {\n if (typeof details[key] === 'string') {\n _cacheNameDetails[key] = details[key];\n }\n });\n },\n getGoogleAnalyticsName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);\n },\n getPrecacheName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.precache);\n },\n getPrefix: () => {\n return _cacheNameDetails.prefix;\n },\n getRuntimeName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.runtime);\n },\n getSuffix: () => {\n return _cacheNameDetails.suffix;\n },\n};\n","/*\n Copyright 2020 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nfunction stripParams(fullURL, ignoreParams) {\n const strippedURL = new URL(fullURL);\n for (const param of ignoreParams) {\n strippedURL.searchParams.delete(param);\n }\n return strippedURL.href;\n}\n/**\n * Matches an item in the cache, ignoring specific URL params. This is similar\n * to the `ignoreSearch` option, but it allows you to ignore just specific\n * params (while continuing to match on the others).\n *\n * @private\n * @param {Cache} cache\n * @param {Request} request\n * @param {Object} matchOptions\n * @param {Array<string>} ignoreParams\n * @return {Promise<Response|undefined>}\n */\nasync function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) {\n const strippedRequestURL = stripParams(request.url, ignoreParams);\n // If the request doesn't include any ignored params, match as normal.\n if (request.url === strippedRequestURL) {\n return cache.match(request, matchOptions);\n }\n // Otherwise, match by comparing keys\n const keysOptions = Object.assign(Object.assign({}, matchOptions), { ignoreSearch: true });\n const cacheKeys = await cache.keys(request, keysOptions);\n for (const cacheKey of cacheKeys) {\n const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams);\n if (strippedRequestURL === strippedCacheKeyURL) {\n return cache.match(cacheKey, matchOptions);\n }\n }\n return;\n}\nexport { cacheMatchIgnoreParams };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * The Deferred class composes Promises in a way that allows for them to be\n * resolved or rejected from outside the constructor. In most cases promises\n * should be used directly, but Deferreds can be necessary when the logic to\n * resolve a promise must be separate.\n *\n * @private\n */\nclass Deferred {\n /**\n * Creates a promise and exposes its resolve and reject functions as methods.\n */\n constructor() {\n this.promise = new Promise((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\nexport { Deferred };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n// Callbacks to be executed whenever there's a quota error.\n// Can't change Function type right now.\n// eslint-disable-next-line @typescript-eslint/ban-types\nconst quotaErrorCallbacks = new Set();\nexport { quotaErrorCallbacks };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from '../_private/logger.js';\nimport { quotaErrorCallbacks } from '../models/quotaErrorCallbacks.js';\nimport '../_version.js';\n/**\n * Runs all of the callback functions, one at a time sequentially, in the order\n * in which they were registered.\n *\n * @memberof workbox-core\n * @private\n */\nasync function executeQuotaErrorCallbacks() {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`About to run ${quotaErrorCallbacks.size} ` +\n `callbacks to clean up caches.`);\n }\n for (const callback of quotaErrorCallbacks) {\n await callback();\n if (process.env.NODE_ENV !== 'production') {\n logger.log(callback, 'is complete.');\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.log('Finished running callbacks.');\n }\n}\nexport { executeQuotaErrorCallbacks };\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * Returns a promise that resolves and the passed number of milliseconds.\n * This utility is an async/await-friendly version of `setTimeout`.\n *\n * @param {number} ms\n * @return {Promise}\n * @private\n */\nexport function timeout(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:strategies:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { cacheMatchIgnoreParams } from 'workbox-core/_private/cacheMatchIgnoreParams.js';\nimport { Deferred } from 'workbox-core/_private/Deferred.js';\nimport { executeQuotaErrorCallbacks } from 'workbox-core/_private/executeQuotaErrorCallbacks.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { timeout } from 'workbox-core/_private/timeout.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport './_version.js';\nfunction toRequest(input) {\n return typeof input === 'string' ? new Request(input) : input;\n}\n/**\n * A class created every time a Strategy instance instance calls\n * {@link workbox-strategies.Strategy~handle} or\n * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and\n * cache actions around plugin callbacks and keeps track of when the strategy\n * is \"done\" (i.e. all added `event.waitUntil()` promises have resolved).\n *\n * @memberof workbox-strategies\n */\nclass StrategyHandler {\n /**\n * Creates a new instance associated with the passed strategy and event\n * that's handling the request.\n *\n * The constructor also initializes the state that will be passed to each of\n * the plugins handling this request.\n *\n * @param {workbox-strategies.Strategy} strategy\n * @param {Object} options\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params] The return value from the\n * {@link workbox-routing~matchCallback} (if applicable).\n */\n constructor(strategy, options) {\n this._cacheKeys = {};\n /**\n * The request the strategy is performing (passed to the strategy's\n * `handle()` or `handleAll()` method).\n * @name request\n * @instance\n * @type {Request}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * The event associated with this request.\n * @name event\n * @instance\n * @type {ExtendableEvent}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * A `URL` instance of `request.url` (if passed to the strategy's\n * `handle()` or `handleAll()` method).\n * Note: the `url` param will be present if the strategy was invoked\n * from a workbox `Route` object.\n * @name url\n * @instance\n * @type {URL|undefined}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * A `param` value (if passed to the strategy's\n * `handle()` or `handleAll()` method).\n * Note: the `param` param will be present if the strategy was invoked\n * from a workbox `Route` object and the\n * {@link workbox-routing~matchCallback} returned\n * a truthy value (it will be that value).\n * @name params\n * @instance\n * @type {*|undefined}\n * @memberof workbox-strategies.StrategyHandler\n */\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(options.event, ExtendableEvent, {\n moduleName: 'workbox-strategies',\n className: 'StrategyHandler',\n funcName: 'constructor',\n paramName: 'options.event',\n });\n }\n Object.assign(this, options);\n this.event = options.event;\n this._strategy = strategy;\n this._handlerDeferred = new Deferred();\n this._extendLifetimePromises = [];\n // Copy the plugins list (since it's mutable on the strategy),\n // so any mutations don't affect this handler instance.\n this._plugins = [...strategy.plugins];\n this._pluginStateMap = new Map();\n for (const plugin of this._plugins) {\n this._pluginStateMap.set(plugin, {});\n }\n this.event.waitUntil(this._handlerDeferred.promise);\n }\n /**\n * Fetches a given request (and invokes any applicable plugin callback\n * methods) using the `fetchOptions` (for non-navigation requests) and\n * `plugins` defined on the `Strategy` object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - `requestWillFetch()`\n * - `fetchDidSucceed()`\n * - `fetchDidFail()`\n *\n * @param {Request|string} input The URL or request to fetch.\n * @return {Promise<Response>}\n */\n async fetch(input) {\n const { event } = this;\n let request = toRequest(input);\n if (request.mode === 'navigate' &&\n event instanceof FetchEvent &&\n event.preloadResponse) {\n const possiblePreloadResponse = (await event.preloadResponse);\n if (possiblePreloadResponse) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Using a preloaded navigation response for ` +\n `'${getFriendlyURL(request.url)}'`);\n }\n return possiblePreloadResponse;\n }\n }\n // If there is a fetchDidFail plugin, we need to save a clone of the\n // original request before it's either modified by a requestWillFetch\n // plugin or before the original request's body is consumed via fetch().\n const originalRequest = this.hasCallback('fetchDidFail')\n ? request.clone()\n : null;\n try {\n for (const cb of this.iterateCallbacks('requestWillFetch')) {\n request = await cb({ request: request.clone(), event });\n }\n }\n catch (err) {\n if (err instanceof Error) {\n throw new WorkboxError('plugin-error-request-will-fetch', {\n thrownErrorMessage: err.message,\n });\n }\n }\n // The request can be altered by plugins with `requestWillFetch` making\n // the original request (most likely from a `fetch` event) different\n // from the Request we make. Pass both to `fetchDidFail` to aid debugging.\n const pluginFilteredRequest = request.clone();\n try {\n let fetchResponse;\n // See https://github.com/GoogleChrome/workbox/issues/1796\n fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions);\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Network request for ` +\n `'${getFriendlyURL(request.url)}' returned a response with ` +\n `status '${fetchResponse.status}'.`);\n }\n for (const callback of this.iterateCallbacks('fetchDidSucceed')) {\n fetchResponse = await callback({\n event,\n request: pluginFilteredRequest,\n response: fetchResponse,\n });\n }\n return fetchResponse;\n }\n catch (error) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Network request for ` +\n `'${getFriendlyURL(request.url)}' threw an error.`, error);\n }\n // `originalRequest` will only exist if a `fetchDidFail` callback\n // is being used (see above).\n if (originalRequest) {\n await this.runCallbacks('fetchDidFail', {\n error: error,\n event,\n originalRequest: originalRequest.clone(),\n request: pluginFilteredRequest.clone(),\n });\n }\n throw error;\n }\n }\n /**\n * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on\n * the response generated by `this.fetch()`.\n *\n * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,\n * so you do not have to manually call `waitUntil()` on the event.\n *\n * @param {Request|string} input The request or URL to fetch and cache.\n * @return {Promise<Response>}\n */\n async fetchAndCachePut(input) {\n const response = await this.fetch(input);\n const responseClone = response.clone();\n void this.waitUntil(this.cachePut(input, responseClone));\n return response;\n }\n /**\n * Matches a request from the cache (and invokes any applicable plugin\n * callback methods) using the `cacheName`, `matchOptions`, and `plugins`\n * defined on the strategy object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - cacheKeyWillBeUsed()\n * - cachedResponseWillBeUsed()\n *\n * @param {Request|string} key The Request or URL to use as the cache key.\n * @return {Promise<Response|undefined>} A matching response, if found.\n */\n async cacheMatch(key) {\n const request = toRequest(key);\n let cachedResponse;\n const { cacheName, matchOptions } = this._strategy;\n const effectiveRequest = await this.getCacheKey(request, 'read');\n const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), { cacheName });\n cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);\n if (process.env.NODE_ENV !== 'production') {\n if (cachedResponse) {\n logger.debug(`Found a cached response in '${cacheName}'.`);\n }\n else {\n logger.debug(`No cached response found in '${cacheName}'.`);\n }\n }\n for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {\n cachedResponse =\n (await callback({\n cacheName,\n matchOptions,\n cachedResponse,\n request: effectiveRequest,\n event: this.event,\n })) || undefined;\n }\n return cachedResponse;\n }\n /**\n * Puts a request/response pair in the cache (and invokes any applicable\n * plugin callback methods) using the `cacheName` and `plugins` defined on\n * the strategy object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - cacheKeyWillBeUsed()\n * - cacheWillUpdate()\n * - cacheDidUpdate()\n *\n * @param {Request|string} key The request or URL to use as the cache key.\n * @param {Response} response The response to cache.\n * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response\n * not be cached, and `true` otherwise.\n */\n async cachePut(key, response) {\n const request = toRequest(key);\n // Run in the next task to avoid blocking other cache reads.\n // https://github.com/w3c/ServiceWorker/issues/1397\n await timeout(0);\n const effectiveRequest = await this.getCacheKey(request, 'write');\n if (process.env.NODE_ENV !== 'production') {\n if (effectiveRequest.method && effectiveRequest.method !== 'GET') {\n throw new WorkboxError('attempt-to-cache-non-get-request', {\n url: getFriendlyURL(effectiveRequest.url),\n method: effectiveRequest.method,\n });\n }\n // See https://github.com/GoogleChrome/workbox/issues/2818\n const vary = response.headers.get('Vary');\n if (vary) {\n logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` +\n `has a 'Vary: ${vary}' header. ` +\n `Consider setting the {ignoreVary: true} option on your strategy ` +\n `to ensure cache matching and deletion works as expected.`);\n }\n }\n if (!response) {\n if (process.env.NODE_ENV !== 'production') {\n logger.error(`Cannot cache non-existent response for ` +\n `'${getFriendlyURL(effectiveRequest.url)}'.`);\n }\n throw new WorkboxError('cache-put-with-no-response', {\n url: getFriendlyURL(effectiveRequest.url),\n });\n }\n const responseToCache = await this._ensureResponseSafeToCache(response);\n if (!responseToCache) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` +\n `will not be cached.`, responseToCache);\n }\n return false;\n }\n const { cacheName, matchOptions } = this._strategy;\n const cache = await self.caches.open(cacheName);\n const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');\n const oldResponse = hasCacheUpdateCallback\n ? await cacheMatchIgnoreParams(\n // TODO(philipwalton): the `__WB_REVISION__` param is a precaching\n // feature. Consider into ways to only add this behavior if using\n // precaching.\n cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions)\n : null;\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Updating the '${cacheName}' cache with a new Response ` +\n `for ${getFriendlyURL(effectiveRequest.url)}.`);\n }\n try {\n await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);\n }\n catch (error) {\n if (error instanceof Error) {\n // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError\n if (error.name === 'QuotaExceededError') {\n await executeQuotaErrorCallbacks();\n }\n throw error;\n }\n }\n for (const callback of this.iterateCallbacks('cacheDidUpdate')) {\n await callback({\n cacheName,\n oldResponse,\n newResponse: responseToCache.clone(),\n request: effectiveRequest,\n event: this.event,\n });\n }\n return true;\n }\n /**\n * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and\n * executes any of those callbacks found in sequence. The final `Request`\n * object returned by the last plugin is treated as the cache key for cache\n * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have\n * been registered, the passed request is returned unmodified\n *\n * @param {Request} request\n * @param {string} mode\n * @return {Promise<Request>}\n */\n async getCacheKey(request, mode) {\n const key = `${request.url} | ${mode}`;\n if (!this._cacheKeys[key]) {\n let effectiveRequest = request;\n for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {\n effectiveRequest = toRequest(await callback({\n mode,\n request: effectiveRequest,\n event: this.event,\n // params has a type any can't change right now.\n params: this.params, // eslint-disable-line\n }));\n }\n this._cacheKeys[key] = effectiveRequest;\n }\n return this._cacheKeys[key];\n }\n /**\n * Returns true if the strategy has at least one plugin with the given\n * callback.\n *\n * @param {string} name The name of the callback to check for.\n * @return {boolean}\n */\n hasCallback(name) {\n for (const plugin of this._strategy.plugins) {\n if (name in plugin) {\n return true;\n }\n }\n return false;\n }\n /**\n * Runs all plugin callbacks matching the given name, in order, passing the\n * given param object (merged ith the current plugin state) as the only\n * argument.\n *\n * Note: since this method runs all plugins, it's not suitable for cases\n * where the return value of a callback needs to be applied prior to calling\n * the next callback. See\n * {@link workbox-strategies.StrategyHandler#iterateCallbacks}\n * below for how to handle that case.\n *\n * @param {string} name The name of the callback to run within each plugin.\n * @param {Object} param The object to pass as the first (and only) param\n * when executing each callback. This object will be merged with the\n * current plugin state prior to callback execution.\n */\n async runCallbacks(name, param) {\n for (const callback of this.iterateCallbacks(name)) {\n // TODO(philipwalton): not sure why `any` is needed. It seems like\n // this should work with `as WorkboxPluginCallbackParam[C]`.\n await callback(param);\n }\n }\n /**\n * Accepts a callback and returns an iterable of matching plugin callbacks,\n * where each callback is wrapped with the current handler state (i.e. when\n * you call each callback, whatever object parameter you pass it will\n * be merged with the plugin's current state).\n *\n * @param {string} name The name fo the callback to run\n * @return {Array<Function>}\n */\n *iterateCallbacks(name) {\n for (const plugin of this._strategy.plugins) {\n if (typeof plugin[name] === 'function') {\n const state = this._pluginStateMap.get(plugin);\n const statefulCallback = (param) => {\n const statefulParam = Object.assign(Object.assign({}, param), { state });\n // TODO(philipwalton): not sure why `any` is needed. It seems like\n // this should work with `as WorkboxPluginCallbackParam[C]`.\n return plugin[name](statefulParam);\n };\n yield statefulCallback;\n }\n }\n }\n /**\n * Adds a promise to the\n * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}\n * of the event event associated with the request being handled (usually a\n * `FetchEvent`).\n *\n * Note: you can await\n * {@link workbox-strategies.StrategyHandler~doneWaiting}\n * to know when all added promises have settled.\n *\n * @param {Promise} promise A promise to add to the extend lifetime promises\n * of the event that triggered the request.\n */\n waitUntil(promise) {\n this._extendLifetimePromises.push(promise);\n return promise;\n }\n /**\n * Returns a promise that resolves once all promises passed to\n * {@link workbox-strategies.StrategyHandler~waitUntil}\n * have settled.\n *\n * Note: any work done after `doneWaiting()` settles should be manually\n * passed to an event's `waitUntil()` method (not this handler's\n * `waitUntil()` method), otherwise the service worker thread my be killed\n * prior to your work completing.\n */\n async doneWaiting() {\n let promise;\n while ((promise = this._extendLifetimePromises.shift())) {\n await promise;\n }\n }\n /**\n * Stops running the strategy and immediately resolves any pending\n * `waitUntil()` promises.\n */\n destroy() {\n this._handlerDeferred.resolve(null);\n }\n /**\n * This method will call cacheWillUpdate on the available plugins (or use\n * status === 200) to determine if the Response is safe and valid to cache.\n *\n * @param {Request} options.request\n * @param {Response} options.response\n * @return {Promise<Response|undefined>}\n *\n * @private\n */\n async _ensureResponseSafeToCache(response) {\n let responseToCache = response;\n let pluginsUsed = false;\n for (const callback of this.iterateCallbacks('cacheWillUpdate')) {\n responseToCache =\n (await callback({\n request: this.request,\n response: responseToCache,\n event: this.event,\n })) || undefined;\n pluginsUsed = true;\n if (!responseToCache) {\n break;\n }\n }\n if (!pluginsUsed) {\n if (responseToCache && responseToCache.status !== 200) {\n responseToCache = undefined;\n }\n if (process.env.NODE_ENV !== 'production') {\n if (responseToCache) {\n if (responseToCache.status !== 200) {\n if (responseToCache.status === 0) {\n logger.warn(`The response for '${this.request.url}' ` +\n `is an opaque response. The caching strategy that you're ` +\n `using will not cache opaque responses by default.`);\n }\n else {\n logger.debug(`The response for '${this.request.url}' ` +\n `returned a status code of '${response.status}' and won't ` +\n `be cached as a result.`);\n }\n }\n }\n }\n }\n return responseToCache;\n }\n}\nexport { StrategyHandler };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { StrategyHandler } from './StrategyHandler.js';\nimport './_version.js';\n/**\n * An abstract base class that all other strategy classes must extend from:\n *\n * @memberof workbox-strategies\n */\nclass Strategy {\n /**\n * Creates a new instance of the strategy and sets all documented option\n * properties as public instance properties.\n *\n * Note: if a custom strategy class extends the base Strategy class and does\n * not need more than these properties, it does not need to define its own\n * constructor.\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {Object} [options.matchOptions] The\n * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n */\n constructor(options = {}) {\n /**\n * Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n *\n * @type {string}\n */\n this.cacheName = cacheNames.getRuntimeName(options.cacheName);\n /**\n * The list\n * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * used by this strategy.\n *\n * @type {Array<Object>}\n */\n this.plugins = options.plugins || [];\n /**\n * Values passed along to the\n * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}\n * of all fetch() requests made by this strategy.\n *\n * @type {Object}\n */\n this.fetchOptions = options.fetchOptions;\n /**\n * The\n * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n *\n * @type {Object}\n */\n this.matchOptions = options.matchOptions;\n }\n /**\n * Perform a request strategy and returns a `Promise` that will resolve with\n * a `Response`, invoking all relevant plugin callbacks.\n *\n * When a strategy instance is registered with a Workbox\n * {@link workbox-routing.Route}, this method is automatically\n * called when the route matches.\n *\n * Alternatively, this method can be used in a standalone `FetchEvent`\n * listener by passing it to `event.respondWith()`.\n *\n * @param {FetchEvent|Object} options A `FetchEvent` or an object with the\n * properties listed below.\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n */\n handle(options) {\n const [responseDone] = this.handleAll(options);\n return responseDone;\n }\n /**\n * Similar to {@link workbox-strategies.Strategy~handle}, but\n * instead of just returning a `Promise` that resolves to a `Response` it\n * it will return an tuple of `[response, done]` promises, where the former\n * (`response`) is equivalent to what `handle()` returns, and the latter is a\n * Promise that will resolve once any promises that were added to\n * `event.waitUntil()` as part of performing the strategy have completed.\n *\n * You can await the `done` promise to ensure any extra work performed by\n * the strategy (usually caching responses) completes successfully.\n *\n * @param {FetchEvent|Object} options A `FetchEvent` or an object with the\n * properties listed below.\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n * @return {Array<Promise>} A tuple of [response, done]\n * promises that can be used to determine when the response resolves as\n * well as when the handler has completed all its work.\n */\n handleAll(options) {\n // Allow for flexible options to be passed.\n if (options instanceof FetchEvent) {\n options = {\n event: options,\n request: options.request,\n };\n }\n const event = options.event;\n const request = typeof options.request === 'string'\n ? new Request(options.request)\n : options.request;\n const params = 'params' in options ? options.params : undefined;\n const handler = new StrategyHandler(this, { event, request, params });\n const responseDone = this._getResponse(handler, request, event);\n const handlerDone = this._awaitComplete(responseDone, handler, request, event);\n // Return an array of promises, suitable for use with Promise.all().\n return [responseDone, handlerDone];\n }\n async _getResponse(handler, request, event) {\n await handler.runCallbacks('handlerWillStart', { event, request });\n let response = undefined;\n try {\n response = await this._handle(request, handler);\n // The \"official\" Strategy subclasses all throw this error automatically,\n // but in case a third-party Strategy doesn't, ensure that we have a\n // consistent failure when there's no response or an error response.\n if (!response || response.type === 'error') {\n throw new WorkboxError('no-response', { url: request.url });\n }\n }\n catch (error) {\n if (error instanceof Error) {\n for (const callback of handler.iterateCallbacks('handlerDidError')) {\n response = await callback({ error, event, request });\n if (response) {\n break;\n }\n }\n }\n if (!response) {\n throw error;\n }\n else if (process.env.NODE_ENV !== 'production') {\n logger.log(`While responding to '${getFriendlyURL(request.url)}', ` +\n `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` +\n `a handlerDidError plugin.`);\n }\n }\n for (const callback of handler.iterateCallbacks('handlerWillRespond')) {\n response = await callback({ event, request, response });\n }\n return response;\n }\n async _awaitComplete(responseDone, handler, request, event) {\n let response;\n let error;\n try {\n response = await responseDone;\n }\n catch (error) {\n // Ignore errors, as response errors should be caught via the `response`\n // promise above. The `done` promise will only throw for errors in\n // promises passed to `handler.waitUntil()`.\n }\n try {\n await handler.runCallbacks('handlerDidRespond', {\n event,\n request,\n response,\n });\n await handler.doneWaiting();\n }\n catch (waitUntilError) {\n if (waitUntilError instanceof Error) {\n error = waitUntilError;\n }\n }\n await handler.runCallbacks('handlerDidComplete', {\n event,\n request,\n response,\n error: error,\n });\n handler.destroy();\n if (error) {\n throw error;\n }\n }\n}\nexport { Strategy };\n/**\n * Classes extending the `Strategy` based class should implement this method,\n * and leverage the {@link workbox-strategies.StrategyHandler}\n * arg to perform all fetching and cache logic, which will ensure all relevant\n * cache, cache options, fetch options and plugins are used (per the current\n * strategy instance).\n *\n * @name _handle\n * @instance\n * @abstract\n * @function\n * @param {Request} request\n * @param {workbox-strategies.StrategyHandler} handler\n * @return {Promise<Response>}\n *\n * @memberof workbox-strategies.Strategy\n */\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport '../_version.js';\nexport const messages = {\n strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,\n printFinalResponse: (response) => {\n if (response) {\n logger.groupCollapsed(`View the final response here.`);\n logger.log(response || '[No response returned]');\n logger.groupEnd();\n }\n },\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from './Strategy.js';\nimport { messages } from './utils/messages.js';\nimport './_version.js';\n/**\n * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network)\n * request strategy.\n *\n * A cache first strategy is useful for assets that have been revisioned,\n * such as URLs like `/styles/example.a8f5f1.css`, since they\n * can be cached for long periods of time.\n *\n * If the network request fails, and there is no cache match, this will throw\n * a `WorkboxError` exception.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-strategies\n */\nclass CacheFirst extends Strategy {\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n const logs = [];\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: 'makeRequest',\n paramName: 'request',\n });\n }\n let response = await handler.cacheMatch(request);\n let error = undefined;\n if (!response) {\n if (process.env.NODE_ENV !== 'production') {\n logs.push(`No response found in the '${this.cacheName}' cache. ` +\n `Will respond with a network request.`);\n }\n try {\n response = await handler.fetchAndCachePut(request);\n }\n catch (err) {\n if (err instanceof Error) {\n error = err;\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n if (response) {\n logs.push(`Got response from network.`);\n }\n else {\n logs.push(`Unable to get a response from the network.`);\n }\n }\n }\n else {\n if (process.env.NODE_ENV !== 'production') {\n logs.push(`Found a cached response in the '${this.cacheName}' cache.`);\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));\n for (const log of logs) {\n logger.log(log);\n }\n messages.printFinalResponse(response);\n logger.groupEnd();\n }\n if (!response) {\n throw new WorkboxError('no-response', { url: request.url, error });\n }\n return response;\n }\n}\nexport { CacheFirst };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { timeout } from 'workbox-core/_private/timeout.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from './Strategy.js';\nimport { messages } from './utils/messages.js';\nimport './_version.js';\n/**\n * An implementation of a\n * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)\n * request strategy.\n *\n * This class is useful if you want to take advantage of any\n * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).\n *\n * If the network request fails, this will throw a `WorkboxError` exception.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-strategies\n */\nclass NetworkOnly extends Strategy {\n /**\n * @param {Object} [options]\n * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {number} [options.networkTimeoutSeconds] If set, any network requests\n * that fail to respond within the timeout will result in a network error.\n */\n constructor(options = {}) {\n super(options);\n this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;\n }\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: '_handle',\n paramName: 'request',\n });\n }\n let error = undefined;\n let response;\n try {\n const promises = [\n handler.fetch(request),\n ];\n if (this._networkTimeoutSeconds) {\n const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);\n promises.push(timeoutPromise);\n }\n response = await Promise.race(promises);\n if (!response) {\n throw new Error(`Timed out the network response after ` +\n `${this._networkTimeoutSeconds} seconds.`);\n }\n }\n catch (err) {\n if (err instanceof Error) {\n error = err;\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));\n if (response) {\n logger.log(`Got response from network.`);\n }\n else {\n logger.log(`Unable to get a response from the network.`);\n }\n messages.printFinalResponse(response);\n logger.groupEnd();\n }\n if (!response) {\n throw new WorkboxError('no-response', { url: request.url, error });\n }\n return response;\n }\n}\nexport { NetworkOnly };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport './_version.js';\n/**\n * Claim any currently available clients once the service worker\n * becomes active. This is normally used in conjunction with `skipWaiting()`.\n *\n * @memberof workbox-core\n */\nfunction clientsClaim() {\n self.addEventListener('activate', () => self.clients.claim());\n}\nexport { clientsClaim };\n","/*\n Copyright 2020 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A utility method that makes it easier to use `event.waitUntil` with\n * async functions and return the result.\n *\n * @param {ExtendableEvent} event\n * @param {Function} asyncFn\n * @return {Function}\n * @private\n */\nfunction waitUntil(event, asyncFn) {\n const returnPromise = asyncFn();\n event.waitUntil(returnPromise);\n return returnPromise;\n}\nexport { waitUntil };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:precaching:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport '../_version.js';\n// Name of the search parameter used to store revision info.\nconst REVISION_SEARCH_PARAM = '__WB_REVISION__';\n/**\n * Converts a manifest entry into a versioned URL suitable for precaching.\n *\n * @param {Object|string} entry\n * @return {string} A URL with versioning info.\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function createCacheKey(entry) {\n if (!entry) {\n throw new WorkboxError('add-to-cache-list-unexpected-type', { entry });\n }\n // If a precache manifest entry is a string, it's assumed to be a versioned\n // URL, like '/app.abcd1234.js'. Return as-is.\n if (typeof entry === 'string') {\n const urlObject = new URL(entry, location.href);\n return {\n cacheKey: urlObject.href,\n url: urlObject.href,\n };\n }\n const { revision, url } = entry;\n if (!url) {\n throw new WorkboxError('add-to-cache-list-unexpected-type', { entry });\n }\n // If there's just a URL and no revision, then it's also assumed to be a\n // versioned URL.\n if (!revision) {\n const urlObject = new URL(url, location.href);\n return {\n cacheKey: urlObject.href,\n url: urlObject.href,\n };\n }\n // Otherwise, construct a properly versioned URL using the custom Workbox\n // search parameter along with the revision info.\n const cacheKeyURL = new URL(url, location.href);\n const originalURL = new URL(url, location.href);\n cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);\n return {\n cacheKey: cacheKeyURL.href,\n url: originalURL.href,\n };\n}\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A plugin, designed to be used with PrecacheController, to determine the\n * of assets that were updated (or not updated) during the install event.\n *\n * @private\n */\nclass PrecacheInstallReportPlugin {\n constructor() {\n this.updatedURLs = [];\n this.notUpdatedURLs = [];\n this.handlerWillStart = async ({ request, state, }) => {\n // TODO: `state` should never be undefined...\n if (state) {\n state.originalRequest = request;\n }\n };\n this.cachedResponseWillBeUsed = async ({ event, state, cachedResponse, }) => {\n if (event.type === 'install') {\n if (state &&\n state.originalRequest &&\n state.originalRequest instanceof Request) {\n // TODO: `state` should never be undefined...\n const url = state.originalRequest.url;\n if (cachedResponse) {\n this.notUpdatedURLs.push(url);\n }\n else {\n this.updatedURLs.push(url);\n }\n }\n }\n return cachedResponse;\n };\n }\n}\nexport { PrecacheInstallReportPlugin };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A plugin, designed to be used with PrecacheController, to translate URLs into\n * the corresponding cache key, based on the current revision info.\n *\n * @private\n */\nclass PrecacheCacheKeyPlugin {\n constructor({ precacheController }) {\n this.cacheKeyWillBeUsed = async ({ request, params, }) => {\n // Params is type any, can't change right now.\n /* eslint-disable */\n const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) ||\n this._precacheController.getCacheKeyForURL(request.url);\n /* eslint-enable */\n return cacheKey\n ? new Request(cacheKey, { headers: request.headers })\n : request;\n };\n this._precacheController = precacheController;\n }\n}\nexport { PrecacheCacheKeyPlugin };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport '../_version.js';\n/**\n * @param {string} groupTitle\n * @param {Array<string>} deletedURLs\n *\n * @private\n */\nconst logGroup = (groupTitle, deletedURLs) => {\n logger.groupCollapsed(groupTitle);\n for (const url of deletedURLs) {\n logger.log(url);\n }\n logger.groupEnd();\n};\n/**\n * @param {Array<string>} deletedURLs\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function printCleanupDetails(deletedURLs) {\n const deletionCount = deletedURLs.length;\n if (deletionCount > 0) {\n logger.groupCollapsed(`During precaching cleanup, ` +\n `${deletionCount} cached ` +\n `request${deletionCount === 1 ? ' was' : 's were'} deleted.`);\n logGroup('Deleted Cache Requests', deletedURLs);\n logger.groupEnd();\n }\n}\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport '../_version.js';\n/**\n * @param {string} groupTitle\n * @param {Array<string>} urls\n *\n * @private\n */\nfunction _nestedGroup(groupTitle, urls) {\n if (urls.length === 0) {\n return;\n }\n logger.groupCollapsed(groupTitle);\n for (const url of urls) {\n logger.log(url);\n }\n logger.groupEnd();\n}\n/**\n * @param {Array<string>} urlsToPrecache\n * @param {Array<string>} urlsAlreadyPrecached\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) {\n const precachedCount = urlsToPrecache.length;\n const alreadyPrecachedCount = urlsAlreadyPrecached.length;\n if (precachedCount || alreadyPrecachedCount) {\n let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;\n if (alreadyPrecachedCount > 0) {\n message +=\n ` ${alreadyPrecachedCount} ` +\n `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;\n }\n logger.groupCollapsed(message);\n _nestedGroup(`View newly precached URLs.`, urlsToPrecache);\n _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached);\n logger.groupEnd();\n }\n}\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nlet supportStatus;\n/**\n * A utility function that determines whether the current browser supports\n * constructing a new `Response` from a `response.body` stream.\n *\n * @return {boolean} `true`, if the current browser can successfully\n * construct a `Response` from a `response.body` stream, `false` otherwise.\n *\n * @private\n */\nfunction canConstructResponseFromBodyStream() {\n if (supportStatus === undefined) {\n const testResponse = new Response('');\n if ('body' in testResponse) {\n try {\n new Response(testResponse.body);\n supportStatus = true;\n }\n catch (error) {\n supportStatus = false;\n }\n }\n supportStatus = false;\n }\n return supportStatus;\n}\nexport { canConstructResponseFromBodyStream };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { canConstructResponseFromBodyStream } from './_private/canConstructResponseFromBodyStream.js';\nimport { WorkboxError } from './_private/WorkboxError.js';\nimport './_version.js';\n/**\n * Allows developers to copy a response and modify its `headers`, `status`,\n * or `statusText` values (the values settable via a\n * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax}\n * object in the constructor).\n * To modify these values, pass a function as the second argument. That\n * function will be invoked with a single object with the response properties\n * `{headers, status, statusText}`. The return value of this function will\n * be used as the `ResponseInit` for the new `Response`. To change the values\n * either modify the passed parameter(s) and return it, or return a totally\n * new object.\n *\n * This method is intentionally limited to same-origin responses, regardless of\n * whether CORS was used or not.\n *\n * @param {Response} response\n * @param {Function} modifier\n * @memberof workbox-core\n */\nasync function copyResponse(response, modifier) {\n let origin = null;\n // If response.url isn't set, assume it's cross-origin and keep origin null.\n if (response.url) {\n const responseURL = new URL(response.url);\n origin = responseURL.origin;\n }\n if (origin !== self.location.origin) {\n throw new WorkboxError('cross-origin-copy-response', { origin });\n }\n const clonedResponse = response.clone();\n // Create a fresh `ResponseInit` object by cloning the headers.\n const responseInit = {\n headers: new Headers(clonedResponse.headers),\n status: clonedResponse.status,\n statusText: clonedResponse.statusText,\n };\n // Apply any user modifications.\n const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;\n // Create the new response from the body stream and `ResponseInit`\n // modifications. Note: not all browsers support the Response.body stream,\n // so fall back to reading the entire body into memory as a blob.\n const body = canConstructResponseFromBodyStream()\n ? clonedResponse.body\n : await clonedResponse.blob();\n return new Response(body, modifiedResponseInit);\n}\nexport { copyResponse };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { copyResponse } from 'workbox-core/copyResponse.js';\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from 'workbox-strategies/Strategy.js';\nimport './_version.js';\n/**\n * A {@link workbox-strategies.Strategy} implementation\n * specifically designed to work with\n * {@link workbox-precaching.PrecacheController}\n * to both cache and fetch precached assets.\n *\n * Note: an instance of this class is created automatically when creating a\n * `PrecacheController`; it's generally not necessary to create this yourself.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-precaching\n */\nclass PrecacheStrategy extends Strategy {\n /**\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init}\n * of all fetch() requests made by this strategy.\n * @param {Object} [options.matchOptions] The\n * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to\n * get the response from the network if there's a precache miss.\n */\n constructor(options = {}) {\n options.cacheName = cacheNames.getPrecacheName(options.cacheName);\n super(options);\n this._fallbackToNetwork =\n options.fallbackToNetwork === false ? false : true;\n // Redirected responses cannot be used to satisfy a navigation request, so\n // any redirected response must be \"copied\" rather than cloned, so the new\n // response doesn't contain the `redirected` flag. See:\n // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1\n this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);\n }\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n const response = await handler.cacheMatch(request);\n if (response) {\n return response;\n }\n // If this is an `install` event for an entry that isn't already cached,\n // then populate the cache.\n if (handler.event && handler.event.type === 'install') {\n return await this._handleInstall(request, handler);\n }\n // Getting here means something went wrong. An entry that should have been\n // precached wasn't found in the cache.\n return await this._handleFetch(request, handler);\n }\n async _handleFetch(request, handler) {\n let response;\n const params = (handler.params || {});\n // Fall back to the network if we're configured to do so.\n if (this._fallbackToNetwork) {\n if (process.env.NODE_ENV !== 'production') {\n logger.warn(`The precached response for ` +\n `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` +\n `found. Falling back to the network.`);\n }\n const integrityInManifest = params.integrity;\n const integrityInRequest = request.integrity;\n const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;\n // Do not add integrity if the original request is no-cors\n // See https://github.com/GoogleChrome/workbox/issues/3096\n response = await handler.fetch(new Request(request, {\n integrity: request.mode !== 'no-cors'\n ? integrityInRequest || integrityInManifest\n : undefined,\n }));\n // It's only \"safe\" to repair the cache if we're using SRI to guarantee\n // that the response matches the precache manifest's expectations,\n // and there's either a) no integrity property in the incoming request\n // or b) there is an integrity, and it matches the precache manifest.\n // See https://github.com/GoogleChrome/workbox/issues/2858\n // Also if the original request users no-cors we don't use integrity.\n // See https://github.com/GoogleChrome/workbox/issues/3096\n if (integrityInManifest &&\n noIntegrityConflict &&\n request.mode !== 'no-cors') {\n this._useDefaultCacheabilityPluginIfNeeded();\n const wasCached = await handler.cachePut(request, response.clone());\n if (process.env.NODE_ENV !== 'production') {\n if (wasCached) {\n logger.log(`A response for ${getFriendlyURL(request.url)} ` +\n `was used to \"repair\" the precache.`);\n }\n }\n }\n }\n else {\n // This shouldn't normally happen, but there are edge cases:\n // https://github.com/GoogleChrome/workbox/issues/1441\n throw new WorkboxError('missing-precache-entry', {\n cacheName: this.cacheName,\n url: request.url,\n });\n }\n if (process.env.NODE_ENV !== 'production') {\n const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read'));\n // Workbox is going to handle the route.\n // print the routing details to the console.\n logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL(request.url));\n logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);\n logger.groupCollapsed(`View request details here.`);\n logger.log(request);\n logger.groupEnd();\n logger.groupCollapsed(`View response details here.`);\n logger.log(response);\n logger.groupEnd();\n logger.groupEnd();\n }\n return response;\n }\n async _handleInstall(request, handler) {\n this._useDefaultCacheabilityPluginIfNeeded();\n const response = await handler.fetch(request);\n // Make sure we defer cachePut() until after we know the response\n // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737\n const wasCached = await handler.cachePut(request, response.clone());\n if (!wasCached) {\n // Throwing here will lead to the `install` handler failing, which\n // we want to do if *any* of the responses aren't safe to cache.\n throw new WorkboxError('bad-precaching-response', {\n url: request.url,\n status: response.status,\n });\n }\n return response;\n }\n /**\n * This method is complex, as there a number of things to account for:\n *\n * The `plugins` array can be set at construction, and/or it might be added to\n * to at any time before the strategy is used.\n *\n * At the time the strategy is used (i.e. during an `install` event), there\n * needs to be at least one plugin that implements `cacheWillUpdate` in the\n * array, other than `copyRedirectedCacheableResponsesPlugin`.\n *\n * - If this method is called and there are no suitable `cacheWillUpdate`\n * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.\n *\n * - If this method is called and there is exactly one `cacheWillUpdate`, then\n * we don't have to do anything (this might be a previously added\n * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).\n *\n * - If this method is called and there is more than one `cacheWillUpdate`,\n * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,\n * we need to remove it. (This situation is unlikely, but it could happen if\n * the strategy is used multiple times, the first without a `cacheWillUpdate`,\n * and then later on after manually adding a custom `cacheWillUpdate`.)\n *\n * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.\n *\n * @private\n */\n _useDefaultCacheabilityPluginIfNeeded() {\n let defaultPluginIndex = null;\n let cacheWillUpdatePluginCount = 0;\n for (const [index, plugin] of this.plugins.entries()) {\n // Ignore the copy redirected plugin when determining what to do.\n if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {\n continue;\n }\n // Save the default plugin's index, in case it needs to be removed.\n if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {\n defaultPluginIndex = index;\n }\n if (plugin.cacheWillUpdate) {\n cacheWillUpdatePluginCount++;\n }\n }\n if (cacheWillUpdatePluginCount === 0) {\n this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);\n }\n else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {\n // Only remove the default plugin; multiple custom plugins are allowed.\n this.plugins.splice(defaultPluginIndex, 1);\n }\n // Nothing needs to be done if cacheWillUpdatePluginCount is 1\n }\n}\nPrecacheStrategy.defaultPrecacheCacheabilityPlugin = {\n async cacheWillUpdate({ response }) {\n if (!response || response.status >= 400) {\n return null;\n }\n return response;\n },\n};\nPrecacheStrategy.copyRedirectedCacheableResponsesPlugin = {\n async cacheWillUpdate({ response }) {\n return response.redirected ? await copyResponse(response) : response;\n },\n};\nexport { PrecacheStrategy };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { waitUntil } from 'workbox-core/_private/waitUntil.js';\nimport { createCacheKey } from './utils/createCacheKey.js';\nimport { PrecacheInstallReportPlugin } from './utils/PrecacheInstallReportPlugin.js';\nimport { PrecacheCacheKeyPlugin } from './utils/PrecacheCacheKeyPlugin.js';\nimport { printCleanupDetails } from './utils/printCleanupDetails.js';\nimport { printInstallDetails } from './utils/printInstallDetails.js';\nimport { PrecacheStrategy } from './PrecacheStrategy.js';\nimport './_version.js';\n/**\n * Performs efficient precaching of assets.\n *\n * @memberof workbox-precaching\n */\nclass PrecacheController {\n /**\n * Create a new PrecacheController.\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] The cache to use for precaching.\n * @param {string} [options.plugins] Plugins to use when precaching as well\n * as responding to fetch events for precached assets.\n * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to\n * get the response from the network if there's a precache miss.\n */\n constructor({ cacheName, plugins = [], fallbackToNetwork = true, } = {}) {\n this._urlsToCacheKeys = new Map();\n this._urlsToCacheModes = new Map();\n this._cacheKeysToIntegrities = new Map();\n this._strategy = new PrecacheStrategy({\n cacheName: cacheNames.getPrecacheName(cacheName),\n plugins: [\n ...plugins,\n new PrecacheCacheKeyPlugin({ precacheController: this }),\n ],\n fallbackToNetwork,\n });\n // Bind the install and activate methods to the instance.\n this.install = this.install.bind(this);\n this.activate = this.activate.bind(this);\n }\n /**\n * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and\n * used to cache assets and respond to fetch events.\n */\n get strategy() {\n return this._strategy;\n }\n /**\n * Adds items to the precache list, removing any duplicates and\n * stores the files in the\n * {@link workbox-core.cacheNames|\"precache cache\"} when the service\n * worker installs.\n *\n * This method can be called multiple times.\n *\n * @param {Array<Object|string>} [entries=[]] Array of entries to precache.\n */\n precache(entries) {\n this.addToCacheList(entries);\n if (!this._installAndActiveListenersAdded) {\n self.addEventListener('install', this.install);\n self.addEventListener('activate', this.activate);\n this._installAndActiveListenersAdded = true;\n }\n }\n /**\n * This method will add items to the precache list, removing duplicates\n * and ensuring the information is valid.\n *\n * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries\n * Array of entries to precache.\n */\n addToCacheList(entries) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isArray(entries, {\n moduleName: 'workbox-precaching',\n className: 'PrecacheController',\n funcName: 'addToCacheList',\n paramName: 'entries',\n });\n }\n const urlsToWarnAbout = [];\n for (const entry of entries) {\n // See https://github.com/GoogleChrome/workbox/issues/2259\n if (typeof entry === 'string') {\n urlsToWarnAbout.push(entry);\n }\n else if (entry && entry.revision === undefined) {\n urlsToWarnAbout.push(entry.url);\n }\n const { cacheKey, url } = createCacheKey(entry);\n const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default';\n if (this._urlsToCacheKeys.has(url) &&\n this._urlsToCacheKeys.get(url) !== cacheKey) {\n throw new WorkboxError('add-to-cache-list-conflicting-entries', {\n firstEntry: this._urlsToCacheKeys.get(url),\n secondEntry: cacheKey,\n });\n }\n if (typeof entry !== 'string' && entry.integrity) {\n if (this._cacheKeysToIntegrities.has(cacheKey) &&\n this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {\n throw new WorkboxError('add-to-cache-list-conflicting-integrities', {\n url,\n });\n }\n this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);\n }\n this._urlsToCacheKeys.set(url, cacheKey);\n this._urlsToCacheModes.set(url, cacheMode);\n if (urlsToWarnAbout.length > 0) {\n const warningMessage = `Workbox is precaching URLs without revision ` +\n `info: ${urlsToWarnAbout.join(', ')}\\nThis is generally NOT safe. ` +\n `Learn more at https://bit.ly/wb-precache`;\n if (process.env.NODE_ENV === 'production') {\n // Use console directly to display this warning without bloating\n // bundle sizes by pulling in all of the logger codebase in prod.\n console.warn(warningMessage);\n }\n else {\n logger.warn(warningMessage);\n }\n }\n }\n }\n /**\n * Precaches new and updated assets. Call this method from the service worker\n * install event.\n *\n * Note: this method calls `event.waitUntil()` for you, so you do not need\n * to call it yourself in your event handlers.\n *\n * @param {ExtendableEvent} event\n * @return {Promise<workbox-precaching.InstallResult>}\n */\n install(event) {\n // waitUntil returns Promise<any>\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return waitUntil(event, async () => {\n const installReportPlugin = new PrecacheInstallReportPlugin();\n this.strategy.plugins.push(installReportPlugin);\n // Cache entries one at a time.\n // See https://github.com/GoogleChrome/workbox/issues/2528\n for (const [url, cacheKey] of this._urlsToCacheKeys) {\n const integrity = this._cacheKeysToIntegrities.get(cacheKey);\n const cacheMode = this._urlsToCacheModes.get(url);\n const request = new Request(url, {\n integrity,\n cache: cacheMode,\n credentials: 'same-origin',\n });\n await Promise.all(this.strategy.handleAll({\n params: { cacheKey },\n request,\n event,\n }));\n }\n const { updatedURLs, notUpdatedURLs } = installReportPlugin;\n if (process.env.NODE_ENV !== 'production') {\n printInstallDetails(updatedURLs, notUpdatedURLs);\n }\n return { updatedURLs, notUpdatedURLs };\n });\n }\n /**\n * Deletes assets that are no longer present in the current precache manifest.\n * Call this method from the service worker activate event.\n *\n * Note: this method calls `event.waitUntil()` for you, so you do not need\n * to call it yourself in your event handlers.\n *\n * @param {ExtendableEvent} event\n * @return {Promise<workbox-precaching.CleanupResult>}\n */\n activate(event) {\n // waitUntil returns Promise<any>\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return waitUntil(event, async () => {\n const cache = await self.caches.open(this.strategy.cacheName);\n const currentlyCachedRequests = await cache.keys();\n const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());\n const deletedURLs = [];\n for (const request of currentlyCachedRequests) {\n if (!expectedCacheKeys.has(request.url)) {\n await cache.delete(request);\n deletedURLs.push(request.url);\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n printCleanupDetails(deletedURLs);\n }\n return { deletedURLs };\n });\n }\n /**\n * Returns a mapping of a precached URL to the corresponding cache key, taking\n * into account the revision information for the URL.\n *\n * @return {Map<string, string>} A URL to cache key mapping.\n */\n getURLsToCacheKeys() {\n return this._urlsToCacheKeys;\n }\n /**\n * Returns a list of all the URLs that have been precached by the current\n * service worker.\n *\n * @return {Array<string>} The precached URLs.\n */\n getCachedURLs() {\n return [...this._urlsToCacheKeys.keys()];\n }\n /**\n * Returns the cache key used for storing a given URL. If that URL is\n * unversioned, like `/index.html', then the cache key will be the original\n * URL with a search parameter appended to it.\n *\n * @param {string} url A URL whose cache key you want to look up.\n * @return {string} The versioned URL that corresponds to a cache key\n * for the original URL, or undefined if that URL isn't precached.\n */\n getCacheKeyForURL(url) {\n const urlObject = new URL(url, location.href);\n return this._urlsToCacheKeys.get(urlObject.href);\n }\n /**\n * @param {string} url A cache key whose SRI you want to look up.\n * @return {string} The subresource integrity associated with the cache key,\n * or undefined if it's not set.\n */\n getIntegrityForCacheKey(cacheKey) {\n return this._cacheKeysToIntegrities.get(cacheKey);\n }\n /**\n * This acts as a drop-in replacement for\n * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)\n * with the following differences:\n *\n * - It knows what the name of the precache is, and only checks in that cache.\n * - It allows you to pass in an \"original\" URL without versioning parameters,\n * and it will automatically look up the correct cache key for the currently\n * active revision of that URL.\n *\n * E.g., `matchPrecache('index.html')` will find the correct precached\n * response for the currently active service worker, even if the actual cache\n * key is `'/index.html?__WB_REVISION__=1234abcd'`.\n *\n * @param {string|Request} request The key (without revisioning parameters)\n * to look up in the precache.\n * @return {Promise<Response|undefined>}\n */\n async matchPrecache(request) {\n const url = request instanceof Request ? request.url : request;\n const cacheKey = this.getCacheKeyForURL(url);\n if (cacheKey) {\n const cache = await self.caches.open(this.strategy.cacheName);\n return cache.match(cacheKey);\n }\n return undefined;\n }\n /**\n * Returns a function that looks up `url` in the precache (taking into\n * account revision information), and returns the corresponding `Response`.\n *\n * @param {string} url The precached URL which will be used to lookup the\n * `Response`.\n * @return {workbox-routing~handlerCallback}\n */\n createHandlerBoundToURL(url) {\n const cacheKey = this.getCacheKeyForURL(url);\n if (!cacheKey) {\n throw new WorkboxError('non-precached-url', { url });\n }\n return (options) => {\n options.request = new Request(url);\n options.params = Object.assign({ cacheKey }, options.params);\n return this.strategy.handle(options);\n };\n }\n}\nexport { PrecacheController };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { PrecacheController } from '../PrecacheController.js';\nimport '../_version.js';\nlet precacheController;\n/**\n * @return {PrecacheController}\n * @private\n */\nexport const getOrCreatePrecacheController = () => {\n if (!precacheController) {\n precacheController = new PrecacheController();\n }\n return precacheController;\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * Removes any URL search parameters that should be ignored.\n *\n * @param {URL} urlObject The original URL.\n * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against\n * each search parameter name. Matches mean that the search parameter should be\n * ignored.\n * @return {URL} The URL with any ignored search parameters removed.\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) {\n // Convert the iterable into an array at the start of the loop to make sure\n // deletion doesn't mess up iteration.\n for (const paramName of [...urlObject.searchParams.keys()]) {\n if (ignoreURLParametersMatching.some((regExp) => regExp.test(paramName))) {\n urlObject.searchParams.delete(paramName);\n }\n }\n return urlObject;\n}\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { removeIgnoredSearchParams } from './removeIgnoredSearchParams.js';\nimport '../_version.js';\n/**\n * Generator function that yields possible variations on the original URL to\n * check, one at a time.\n *\n * @param {string} url\n * @param {Object} options\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function* generateURLVariations(url, { ignoreURLParametersMatching = [/^utm_/, /^fbclid$/], directoryIndex = 'index.html', cleanURLs = true, urlManipulation, } = {}) {\n const urlObject = new URL(url, location.href);\n urlObject.hash = '';\n yield urlObject.href;\n const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);\n yield urlWithoutIgnoredParams.href;\n if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {\n const directoryURL = new URL(urlWithoutIgnoredParams.href);\n directoryURL.pathname += directoryIndex;\n yield directoryURL.href;\n }\n if (cleanURLs) {\n const cleanURL = new URL(urlWithoutIgnoredParams.href);\n cleanURL.pathname += '.html';\n yield cleanURL.href;\n }\n if (urlManipulation) {\n const additionalURLs = urlManipulation({ url: urlObject });\n for (const urlToAttempt of additionalURLs) {\n yield urlToAttempt.href;\n }\n }\n}\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { Route } from 'workbox-routing/Route.js';\nimport { generateURLVariations } from './utils/generateURLVariations.js';\nimport './_version.js';\n/**\n * A subclass of {@link workbox-routing.Route} that takes a\n * {@link workbox-precaching.PrecacheController}\n * instance and uses it to match incoming requests and handle fetching\n * responses from the precache.\n *\n * @memberof workbox-precaching\n * @extends workbox-routing.Route\n */\nclass PrecacheRoute extends Route {\n /**\n * @param {PrecacheController} precacheController A `PrecacheController`\n * instance used to both match requests and respond to fetch events.\n * @param {Object} [options] Options to control how requests are matched\n * against the list of precached URLs.\n * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will\n * check cache entries for a URLs ending with '/' to see if there is a hit when\n * appending the `directoryIndex` value.\n * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An\n * array of regex's to remove search params when looking for a cache match.\n * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will\n * check the cache for the URL with a `.html` added to the end of the end.\n * @param {workbox-precaching~urlManipulation} [options.urlManipulation]\n * This is a function that should take a URL and return an array of\n * alternative URLs that should be checked for precache matches.\n */\n constructor(precacheController, options) {\n const match = ({ request, }) => {\n const urlsToCacheKeys = precacheController.getURLsToCacheKeys();\n for (const possibleURL of generateURLVariations(request.url, options)) {\n const cacheKey = urlsToCacheKeys.get(possibleURL);\n if (cacheKey) {\n const integrity = precacheController.getIntegrityForCacheKey(cacheKey);\n return { cacheKey, integrity };\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Precaching did not find a match for ` + getFriendlyURL(request.url));\n }\n return;\n };\n super(match, precacheController.strategy);\n }\n}\nexport { PrecacheRoute };\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { registerRoute } from 'workbox-routing/registerRoute.js';\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport { PrecacheRoute } from './PrecacheRoute.js';\nimport './_version.js';\n/**\n * Add a `fetch` listener to the service worker that will\n * respond to\n * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}\n * with precached assets.\n *\n * Requests for assets that aren't precached, the `FetchEvent` will not be\n * responded to, allowing the event to fall through to other `fetch` event\n * listeners.\n *\n * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute}\n * options.\n *\n * @memberof workbox-precaching\n */\nfunction addRoute(options) {\n const precacheController = getOrCreatePrecacheController();\n const precacheRoute = new PrecacheRoute(precacheController, options);\n registerRoute(precacheRoute);\n}\nexport { addRoute };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport './_version.js';\n/**\n * Adds items to the precache list, removing any duplicates and\n * stores the files in the\n * {@link workbox-core.cacheNames|\"precache cache\"} when the service\n * worker installs.\n *\n * This method can be called multiple times.\n *\n * Please note: This method **will not** serve any of the cached files for you.\n * It only precaches files. To respond to a network request you call\n * {@link workbox-precaching.addRoute}.\n *\n * If you have a single array of files to precache, you can just call\n * {@link workbox-precaching.precacheAndRoute}.\n *\n * @param {Array<Object|string>} [entries=[]] Array of entries to precache.\n *\n * @memberof workbox-precaching\n */\nfunction precache(entries) {\n const precacheController = getOrCreatePrecacheController();\n precacheController.precache(entries);\n}\nexport { precache };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { addRoute } from './addRoute.js';\nimport { precache } from './precache.js';\nimport './_version.js';\n/**\n * This method will add entries to the precache list and add a route to\n * respond to fetch events.\n *\n * This is a convenience method that will call\n * {@link workbox-precaching.precache} and\n * {@link workbox-precaching.addRoute} in a single call.\n *\n * @param {Array<Object|string>} entries Array of entries to precache.\n * @param {Object} [options] See the\n * {@link workbox-precaching.PrecacheRoute} options.\n *\n * @memberof workbox-precaching\n */\nfunction precacheAndRoute(entries, options) {\n precache(entries);\n addRoute(options);\n}\nexport { precacheAndRoute };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst SUBSTRING_TO_FIND = '-precache-';\n/**\n * Cleans up incompatible precaches that were created by older versions of\n * Workbox, by a service worker registered under the current scope.\n *\n * This is meant to be called as part of the `activate` event.\n *\n * This should be safe to use as long as you don't include `substringToFind`\n * (defaulting to `-precache-`) in your non-precache cache names.\n *\n * @param {string} currentPrecacheName The cache name currently in use for\n * precaching. This cache won't be deleted.\n * @param {string} [substringToFind='-precache-'] Cache names which include this\n * substring will be deleted (excluding `currentPrecacheName`).\n * @return {Array<string>} A list of all the cache names that were deleted.\n *\n * @private\n * @memberof workbox-precaching\n */\nconst deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => {\n const cacheNames = await self.caches.keys();\n const cacheNamesToDelete = cacheNames.filter((cacheName) => {\n return (cacheName.includes(substringToFind) &&\n cacheName.includes(self.registration.scope) &&\n cacheName !== currentPrecacheName);\n });\n await Promise.all(cacheNamesToDelete.map((cacheName) => self.caches.delete(cacheName)));\n return cacheNamesToDelete;\n};\nexport { deleteOutdatedCaches };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { deleteOutdatedCaches } from './utils/deleteOutdatedCaches.js';\nimport './_version.js';\n/**\n * Adds an `activate` event listener which will clean up incompatible\n * precaches that were created by older versions of Workbox.\n *\n * @memberof workbox-precaching\n */\nfunction cleanupOutdatedCaches() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('activate', ((event) => {\n const cacheName = cacheNames.getPrecacheName();\n event.waitUntil(deleteOutdatedCaches(cacheName).then((cachesDeleted) => {\n if (process.env.NODE_ENV !== 'production') {\n if (cachesDeleted.length > 0) {\n logger.log(`The following out-of-date precaches were cleaned up ` +\n `automatically:`, cachesDeleted);\n }\n }\n }));\n }));\n}\nexport { cleanupOutdatedCaches };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { Route } from './Route.js';\nimport './_version.js';\n/**\n * NavigationRoute makes it easy to create a\n * {@link workbox-routing.Route} that matches for browser\n * [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}.\n *\n * It will only match incoming Requests whose\n * {@link https://fetch.spec.whatwg.org/#concept-request-mode|mode}\n * is set to `navigate`.\n *\n * You can optionally only apply this route to a subset of navigation requests\n * by using one or both of the `denylist` and `allowlist` parameters.\n *\n * @memberof workbox-routing\n * @extends workbox-routing.Route\n */\nclass NavigationRoute extends Route {\n /**\n * If both `denylist` and `allowlist` are provided, the `denylist` will\n * take precedence and the request will not match this route.\n *\n * The regular expressions in `allowlist` and `denylist`\n * are matched against the concatenated\n * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname}\n * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search}\n * portions of the requested URL.\n *\n * *Note*: These RegExps may be evaluated against every destination URL during\n * a navigation. Avoid using\n * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077),\n * or else your users may see delays when navigating your site.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {Object} options\n * @param {Array<RegExp>} [options.denylist] If any of these patterns match,\n * the route will not handle the request (even if a allowlist RegExp matches).\n * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns\n * match the URL's pathname and search parameter, the route will handle the\n * request (assuming the denylist doesn't match).\n */\n constructor(handler, { allowlist = [/./], denylist = [] } = {}) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isArrayOfClass(allowlist, RegExp, {\n moduleName: 'workbox-routing',\n className: 'NavigationRoute',\n funcName: 'constructor',\n paramName: 'options.allowlist',\n });\n assert.isArrayOfClass(denylist, RegExp, {\n moduleName: 'workbox-routing',\n className: 'NavigationRoute',\n funcName: 'constructor',\n paramName: 'options.denylist',\n });\n }\n super((options) => this._match(options), handler);\n this._allowlist = allowlist;\n this._denylist = denylist;\n }\n /**\n * Routes match handler.\n *\n * @param {Object} options\n * @param {URL} options.url\n * @param {Request} options.request\n * @return {boolean}\n *\n * @private\n */\n _match({ url, request }) {\n if (request && request.mode !== 'navigate') {\n return false;\n }\n const pathnameAndSearch = url.pathname + url.search;\n for (const regExp of this._denylist) {\n if (regExp.test(pathnameAndSearch)) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`The navigation route ${pathnameAndSearch} is not ` +\n `being used, since the URL matches this denylist pattern: ` +\n `${regExp.toString()}`);\n }\n return false;\n }\n }\n if (this._allowlist.some((regExp) => regExp.test(pathnameAndSearch))) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`);\n }\n return true;\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`The navigation route ${pathnameAndSearch} is not ` +\n `being used, since the URL being navigated to doesn't ` +\n `match the allowlist.`);\n }\n return false;\n }\n}\nexport { NavigationRoute };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport './_version.js';\n/**\n * Helper function that calls\n * {@link PrecacheController#createHandlerBoundToURL} on the default\n * {@link PrecacheController} instance.\n *\n * If you are creating your own {@link PrecacheController}, then call the\n * {@link PrecacheController#createHandlerBoundToURL} on that instance,\n * instead of using this function.\n *\n * @param {string} url The precached URL which will be used to lookup the\n * `Response`.\n * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the\n * response from the network if there's a precache miss.\n * @return {workbox-routing~handlerCallback}\n *\n * @memberof workbox-precaching\n */\nfunction createHandlerBoundToURL(url) {\n const precacheController = getOrCreatePrecacheController();\n return precacheController.createHandlerBoundToURL(url);\n}\nexport { createHandlerBoundToURL };\n"],"names":["self","_","e","logger","globalThis","__WB_DISABLE_DEV_LOGS","inGroup","methodToColorMap","debug","log","warn","error","groupCollapsed","groupEnd","print","method","args","test","navigator","userAgent","console","styles","logPrefix","join","api","loggerMethods","Object","keys","key","messages","invalid-value","paramName","validValueDescription","value","Error","JSON","stringify","not-an-array","moduleName","className","funcName","incorrect-type","expectedType","classNameStr","incorrect-class","expectedClassName","isReturnValueProblem","missing-a-method","expectedMethod","add-to-cache-list-unexpected-type","entry","add-to-cache-list-conflicting-entries","firstEntry","secondEntry","plugin-error-request-will-fetch","thrownErrorMessage","invalid-cache-name","cacheNameId","unregister-route-but-not-found-with-method","unregister-route-route-not-registered","queue-replay-failed","name","duplicate-queue-name","expired-test-without-max-age","methodName","unsupported-route-type","not-array-of-class","expectedClass","max-entries-or-age-required","statuses-or-headers-required","invalid-string","channel-name-required","invalid-responses-are-same-args","expire-custom-caches-only","unit-must-be-bytes","normalizedRangeHeader","single-range-only","invalid-range-values","no-range-header","range-not-satisfiable","size","start","end","attempt-to-cache-non-get-request","url","cache-put-with-no-response","no-response","message","bad-precaching-response","status","non-precached-url","add-to-cache-list-conflicting-integrities","missing-precache-entry","cacheName","cross-origin-copy-response","origin","opaque-streams-source","type","generatorFunction","code","details","messageGenerator","WorkboxError","constructor","errorCode","isArray","Array","hasMethod","object","isType","isInstance","isOneOf","validValues","includes","isArrayOfClass","item","finalAssertExports","defaultMethod","validMethods","normalizeHandler","handler","assert","handle","Route","match","setCatchHandler","catchHandler","RegExpRoute","regExp","RegExp","result","exec","href","location","index","toString","slice","getFriendlyURL","urlObj","URL","String","replace","Router","_routes","Map","_defaultHandlerMap","routes","addFetchListener","addEventListener","event","request","responsePromise","handleRequest","respondWith","addCacheListener","data","payload","urlsToCache","requestPromises","Promise","all","map","Request","waitUntil","ports","then","postMessage","protocol","startsWith","sameOrigin","params","route","findMatchingRoute","debugMessages","push","has","get","forEach","msg","err","reject","_catchHandler","catch","catchErr","matchResult","length","undefined","setDefaultHandler","set","registerRoute","unregisterRoute","routeIndex","indexOf","splice","defaultRouter","getOrCreateDefaultRouter","capture","captureUrl","valueToCheck","pathname","wildcards","matchCallback","CacheableResponse","config","statuses","headers","_statuses","_headers","isResponseCacheable","response","Response","cacheable","some","headerName","logFriendlyHeaders","CacheableResponsePlugin","cacheWillUpdate","_cacheableResponse","_cacheNameDetails","googleAnalytics","precache","prefix","runtime","suffix","registration","scope","_createCacheName","filter","eachCacheNameDetail","fn","cacheNames","updateDetails","getGoogleAnalyticsName","userCacheName","getPrecacheName","getPrefix","getRuntimeName","getSuffix","stripParams","fullURL","ignoreParams","strippedURL","param","searchParams","delete","cacheMatchIgnoreParams","cache","matchOptions","strippedRequestURL","keysOptions","assign","ignoreSearch","cacheKeys","cacheKey","strippedCacheKeyURL","Deferred","promise","resolve","quotaErrorCallbacks","Set","executeQuotaErrorCallbacks","callback","timeout","ms","setTimeout","toRequest","input","StrategyHandler","strategy","options","_cacheKeys","ExtendableEvent","_strategy","_handlerDeferred","_extendLifetimePromises","_plugins","plugins","_pluginStateMap","plugin","fetch","mode","FetchEvent","preloadResponse","possiblePreloadResponse","originalRequest","hasCallback","clone","cb","iterateCallbacks","pluginFilteredRequest","fetchResponse","fetchOptions","runCallbacks","fetchAndCachePut","responseClone","cachePut","cacheMatch","cachedResponse","effectiveRequest","getCacheKey","multiMatchOptions","caches","vary","responseToCache","_ensureResponseSafeToCache","open","hasCacheUpdateCallback","oldResponse","put","newResponse","state","statefulCallback","statefulParam","doneWaiting","shift","destroy","pluginsUsed","Strategy","responseDone","handleAll","_getResponse","handlerDone","_awaitComplete","_handle","waitUntilError","strategyStart","strategyName","printFinalResponse","CacheFirst","logs","NetworkOnly","_networkTimeoutSeconds","networkTimeoutSeconds","promises","timeoutPromise","race","clientsClaim","clients","claim","asyncFn","returnPromise","REVISION_SEARCH_PARAM","createCacheKey","urlObject","revision","cacheKeyURL","originalURL","PrecacheInstallReportPlugin","updatedURLs","notUpdatedURLs","handlerWillStart","cachedResponseWillBeUsed","PrecacheCacheKeyPlugin","precacheController","cacheKeyWillBeUsed","_precacheController","getCacheKeyForURL","logGroup","groupTitle","deletedURLs","printCleanupDetails","deletionCount","_nestedGroup","urls","printInstallDetails","urlsToPrecache","urlsAlreadyPrecached","precachedCount","alreadyPrecachedCount","supportStatus","canConstructResponseFromBodyStream","testResponse","body","copyResponse","modifier","responseURL","clonedResponse","responseInit","Headers","statusText","modifiedResponseInit","blob","PrecacheStrategy","_fallbackToNetwork","fallbackToNetwork","copyRedirectedCacheableResponsesPlugin","_handleInstall","_handleFetch","integrityInManifest","integrity","integrityInRequest","noIntegrityConflict","_useDefaultCacheabilityPluginIfNeeded","wasCached","defaultPluginIndex","cacheWillUpdatePluginCount","entries","defaultPrecacheCacheabilityPlugin","redirected","PrecacheController","_urlsToCacheKeys","_urlsToCacheModes","_cacheKeysToIntegrities","install","bind","activate","addToCacheList","_installAndActiveListenersAdded","urlsToWarnAbout","cacheMode","warningMessage","installReportPlugin","credentials","currentlyCachedRequests","expectedCacheKeys","values","getURLsToCacheKeys","getCachedURLs","getIntegrityForCacheKey","matchPrecache","createHandlerBoundToURL","getOrCreatePrecacheController","removeIgnoredSearchParams","ignoreURLParametersMatching","generateURLVariations","directoryIndex","cleanURLs","urlManipulation","hash","urlWithoutIgnoredParams","endsWith","directoryURL","cleanURL","additionalURLs","urlToAttempt","PrecacheRoute","urlsToCacheKeys","possibleURL","addRoute","precacheRoute","precacheAndRoute","SUBSTRING_TO_FIND","deleteOutdatedCaches","currentPrecacheName","substringToFind","cacheNamesToDelete","cleanupOutdatedCaches","cachesDeleted","NavigationRoute","allowlist","denylist","_match","_allowlist","_denylist","pathnameAndSearch","search"],"mappings":"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xH,MAAM,CAAc,YAAA,CAAA,GAC3D,wBAAwB,CAAC,CAAA;IACjC,aAAA;IACJ,WAAA;IACJ,SAAA;IACJ,OAAA;IACJ,KAAA;IACA,IAAA,OAAOsN,eAAe,CAAA;IAC1B,GAAA;IACJ;;ICngBA;IACA;AACA;IACA;IACA;IACA;IACA;IAOA;IACA;IACA;IACA;IACA;IACA,MAAMc,QAAQ,CAAC;IACX;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACItN,EAAAA,WAAWA,CAACsK,OAAO,GAAG,EAAE,EAAE;IACtB;IACR;IACA;IACA;IACA;IACA;IACA;QACQ,IAAI,CAAChL,SAAS,GAAGyI,UAAU,CAACM,cAAc,CAACiC,OAAO,CAAChL,SAAS,CAAC,CAAA;IAC7D;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAACuL,OAAO,GAAGP,OAAO,CAACO,OAAO,IAAI,EAAE,CAAA;IACpC;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAACe,YAAY,GAAGtB,OAAO,CAACsB,YAAY,CAAA;IACxC;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAAC5C,YAAY,GAAGsB,OAAO,CAACtB,YAAY,CAAA;IAC5C,GAAA;IACA;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;MACI7H,MAAMA,CAACmJ,OAAO,EAAE;QACZ,MAAM,CAACiD,YAAY,CAAC,GAAG,IAAI,CAACC,SAAS,CAAClD,OAAO,CAAC,CAAA;IAC9C,IAAA,OAAOiD,YAAY,CAAA;IACvB,GAAA;IACA;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;MACIC,SAASA,CAAClD,OAAO,EAAE;IACf;QACA,IAAIA,OAAO,YAAYY,UAAU,EAAE;I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"} \ No newline at end of file diff --git a/dev-dist/workbox-6f774a69.js b/dev-dist/workbox-6f774a69.js deleted file mode 100644 index f400667..0000000 --- a/dev-dist/workbox-6f774a69.js +++ /dev/null @@ -1,3731 +0,0 @@ -define(['exports'], (function (exports) { 'use strict'; - - // @ts-ignore - try { - self['workbox:core:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const logger = (() => { - // Don't overwrite this value if it's already set. - // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923 - if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) { - self.__WB_DISABLE_DEV_LOGS = false; - } - let inGroup = false; - const methodToColorMap = { - debug: `#7f8c8d`, - log: `#2ecc71`, - warn: `#f39c12`, - error: `#c0392b`, - groupCollapsed: `#3498db`, - groupEnd: null // No colored prefix on groupEnd - }; - const print = function (method, args) { - if (self.__WB_DISABLE_DEV_LOGS) { - return; - } - if (method === 'groupCollapsed') { - // Safari doesn't print all console.groupCollapsed() arguments: - // https://bugs.webkit.org/show_bug.cgi?id=182754 - if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - console[method](...args); - return; - } - } - const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`]; - // When in a group, the workbox prefix is not displayed. - const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')]; - console[method](...logPrefix, ...args); - if (method === 'groupCollapsed') { - inGroup = true; - } - if (method === 'groupEnd') { - inGroup = false; - } - }; - // eslint-disable-next-line @typescript-eslint/ban-types - const api = {}; - const loggerMethods = Object.keys(methodToColorMap); - for (const key of loggerMethods) { - const method = key; - api[method] = (...args) => { - print(method, args); - }; - } - return api; - })(); - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const messages$1 = { - 'invalid-value': ({ - paramName, - validValueDescription, - value - }) => { - if (!paramName || !validValueDescription) { - throw new Error(`Unexpected input to 'invalid-value' error.`); - } - return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`; - }, - 'not-an-array': ({ - moduleName, - className, - funcName, - paramName - }) => { - if (!moduleName || !className || !funcName || !paramName) { - throw new Error(`Unexpected input to 'not-an-array' error.`); - } - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`; - }, - 'incorrect-type': ({ - expectedType, - paramName, - moduleName, - className, - funcName - }) => { - if (!expectedType || !paramName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'incorrect-type' error.`); - } - const classNameStr = className ? `${className}.` : ''; - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}` + `${funcName}()' must be of type ${expectedType}.`; - }, - 'incorrect-class': ({ - expectedClassName, - paramName, - moduleName, - className, - funcName, - isReturnValueProblem - }) => { - if (!expectedClassName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'incorrect-class' error.`); - } - const classNameStr = className ? `${className}.` : ''; - if (isReturnValueProblem) { - return `The return value from ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`; - } - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`; - }, - 'missing-a-method': ({ - expectedMethod, - paramName, - moduleName, - className, - funcName - }) => { - if (!expectedMethod || !paramName || !moduleName || !className || !funcName) { - throw new Error(`Unexpected input to 'missing-a-method' error.`); - } - return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`; - }, - 'add-to-cache-list-unexpected-type': ({ - entry - }) => { - return `An unexpected entry was passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` + `strings with one or more characters, objects with a url property or ` + `Request objects.`; - }, - 'add-to-cache-list-conflicting-entries': ({ - firstEntry, - secondEntry - }) => { - if (!firstEntry || !secondEntry) { - throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`); - } - return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${firstEntry} but different revision details. Workbox is ` + `unable to cache and version the asset correctly. Please remove one ` + `of the entries.`; - }, - 'plugin-error-request-will-fetch': ({ - thrownErrorMessage - }) => { - if (!thrownErrorMessage) { - throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`); - } - return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownErrorMessage}'.`; - }, - 'invalid-cache-name': ({ - cacheNameId, - value - }) => { - if (!cacheNameId) { - throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`); - } - return `You must provide a name containing at least one character for ` + `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`; - }, - 'unregister-route-but-not-found-with-method': ({ - method - }) => { - if (!method) { - throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`); - } - return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`; - }, - 'unregister-route-route-not-registered': () => { - return `The route you're trying to unregister was not previously ` + `registered.`; - }, - 'queue-replay-failed': ({ - name - }) => { - return `Replaying the background sync queue '${name}' failed.`; - }, - 'duplicate-queue-name': ({ - name - }) => { - return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`; - }, - 'expired-test-without-max-age': ({ - methodName, - paramName - }) => { - return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`; - }, - 'unsupported-route-type': ({ - moduleName, - className, - funcName, - paramName - }) => { - return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`; - }, - 'not-array-of-class': ({ - value, - expectedClass, - moduleName, - className, - funcName, - paramName - }) => { - return `The supplied '${paramName}' parameter must be an array of ` + `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` + `Please check the call to ${moduleName}.${className}.${funcName}() ` + `to fix the issue.`; - }, - 'max-entries-or-age-required': ({ - moduleName, - className, - funcName - }) => { - return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`; - }, - 'statuses-or-headers-required': ({ - moduleName, - className, - funcName - }) => { - return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`; - }, - 'invalid-string': ({ - moduleName, - funcName, - paramName - }) => { - if (!paramName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'invalid-string' error.`); - } - return `When using strings, the '${paramName}' parameter must start with ` + `'http' (for cross-origin matches) or '/' (for same-origin matches). ` + `Please see the docs for ${moduleName}.${funcName}() for ` + `more info.`; - }, - 'channel-name-required': () => { - return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`; - }, - 'invalid-responses-are-same-args': () => { - return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`; - }, - 'expire-custom-caches-only': () => { - return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`; - }, - 'unit-must-be-bytes': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`); - } - return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`; - }, - 'single-range-only': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'single-range-only' error.`); - } - return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`; - }, - 'invalid-range-values': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'invalid-range-values' error.`); - } - return `The Range header is missing both start and end values. At least ` + `one of those values is needed. The Range header provided was ` + `"${normalizedRangeHeader}"`; - }, - 'no-range-header': () => { - return `No Range header was found in the Request provided.`; - }, - 'range-not-satisfiable': ({ - size, - start, - end - }) => { - return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`; - }, - 'attempt-to-cache-non-get-request': ({ - url, - method - }) => { - return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`; - }, - 'cache-put-with-no-response': ({ - url - }) => { - return `There was an attempt to cache '${url}' but the response was not ` + `defined.`; - }, - 'no-response': ({ - url, - error - }) => { - let message = `The strategy could not generate a response for '${url}'.`; - if (error) { - message += ` The underlying error is ${error}.`; - } - return message; - }, - 'bad-precaching-response': ({ - url, - status - }) => { - return `The precaching request for '${url}' failed` + (status ? ` with an HTTP status of ${status}.` : `.`); - }, - 'non-precached-url': ({ - url - }) => { - return `createHandlerBoundToURL('${url}') was called, but that URL is ` + `not precached. Please pass in a URL that is precached instead.`; - }, - 'add-to-cache-list-conflicting-integrities': ({ - url - }) => { - return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${url} with different integrity values. Please remove one of them.`; - }, - 'missing-precache-entry': ({ - cacheName, - url - }) => { - return `Unable to find a precached response in ${cacheName} for ${url}.`; - }, - 'cross-origin-copy-response': ({ - origin - }) => { - return `workbox-core.copyResponse() can only be used with same-origin ` + `responses. It was passed a response with origin ${origin}.`; - }, - 'opaque-streams-source': ({ - type - }) => { - const message = `One of the workbox-streams sources resulted in an ` + `'${type}' response.`; - if (type === 'opaqueredirect') { - return `${message} Please do not use a navigation request that results ` + `in a redirect as a source.`; - } - return `${message} Please ensure your sources are CORS-enabled.`; - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const generatorFunction = (code, details = {}) => { - const message = messages$1[code]; - if (!message) { - throw new Error(`Unable to find message for code '${code}'.`); - } - return message(details); - }; - const messageGenerator = generatorFunction; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Workbox errors should be thrown with this class. - * This allows use to ensure the type easily in tests, - * helps developers identify errors from workbox - * easily and allows use to optimise error - * messages correctly. - * - * @private - */ - class WorkboxError extends Error { - /** - * - * @param {string} errorCode The error code that - * identifies this particular error. - * @param {Object=} details Any relevant arguments - * that will help developers identify issues should - * be added as a key on the context object. - */ - constructor(errorCode, details) { - const message = messageGenerator(errorCode, details); - super(message); - this.name = errorCode; - this.details = details; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /* - * This method throws if the supplied value is not an array. - * The destructed values are required to produce a meaningful error for users. - * The destructed and restructured object is so it's clear what is - * needed. - */ - const isArray = (value, details) => { - if (!Array.isArray(value)) { - throw new WorkboxError('not-an-array', details); - } - }; - const hasMethod = (object, expectedMethod, details) => { - const type = typeof object[expectedMethod]; - if (type !== 'function') { - details['expectedMethod'] = expectedMethod; - throw new WorkboxError('missing-a-method', details); - } - }; - const isType = (object, expectedType, details) => { - if (typeof object !== expectedType) { - details['expectedType'] = expectedType; - throw new WorkboxError('incorrect-type', details); - } - }; - const isInstance = (object, - // Need the general type to do the check later. - // eslint-disable-next-line @typescript-eslint/ban-types - expectedClass, details) => { - if (!(object instanceof expectedClass)) { - details['expectedClassName'] = expectedClass.name; - throw new WorkboxError('incorrect-class', details); - } - }; - const isOneOf = (value, validValues, details) => { - if (!validValues.includes(value)) { - details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`; - throw new WorkboxError('invalid-value', details); - } - }; - const isArrayOfClass = (value, - // Need general type to do check later. - expectedClass, - // eslint-disable-line - details) => { - const error = new WorkboxError('not-array-of-class', details); - if (!Array.isArray(value)) { - throw error; - } - for (const item of value) { - if (!(item instanceof expectedClass)) { - throw error; - } - } - }; - const finalAssertExports = { - hasMethod, - isArray, - isInstance, - isOneOf, - isType, - isArrayOfClass - }; - - // @ts-ignore - try { - self['workbox:routing:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The default HTTP method, 'GET', used when there's no specific method - * configured for a route. - * - * @type {string} - * - * @private - */ - const defaultMethod = 'GET'; - /** - * The list of valid HTTP methods associated with requests that could be routed. - * - * @type {Array<string>} - * - * @private - */ - const validMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {function()|Object} handler Either a function, or an object with a - * 'handle' method. - * @return {Object} An object with a handle method. - * - * @private - */ - const normalizeHandler = handler => { - if (handler && typeof handler === 'object') { - { - finalAssertExports.hasMethod(handler, 'handle', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'handler' - }); - } - return handler; - } else { - { - finalAssertExports.isType(handler, 'function', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'handler' - }); - } - return { - handle: handler - }; - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A `Route` consists of a pair of callback functions, "match" and "handler". - * The "match" callback determine if a route should be used to "handle" a - * request by returning a non-falsy value if it can. The "handler" callback - * is called when there is a match and should return a Promise that resolves - * to a `Response`. - * - * @memberof workbox-routing - */ - class Route { - /** - * Constructor for Route class. - * - * @param {workbox-routing~matchCallback} match - * A callback function that determines whether the route matches a given - * `fetch` event by returning a non-falsy value. - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resolving to a Response. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - */ - constructor(match, handler, method = defaultMethod) { - { - finalAssertExports.isType(match, 'function', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'match' - }); - if (method) { - finalAssertExports.isOneOf(method, validMethods, { - paramName: 'method' - }); - } - } - // These values are referenced directly by Router so cannot be - // altered by minificaton. - this.handler = normalizeHandler(handler); - this.match = match; - this.method = method; - } - /** - * - * @param {workbox-routing-handlerCallback} handler A callback - * function that returns a Promise resolving to a Response - */ - setCatchHandler(handler) { - this.catchHandler = normalizeHandler(handler); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * RegExpRoute makes it easy to create a regular expression based - * {@link workbox-routing.Route}. - * - * For same-origin requests the RegExp only needs to match part of the URL. For - * requests against third-party servers, you must define a RegExp that matches - * the start of the URL. - * - * @memberof workbox-routing - * @extends workbox-routing.Route - */ - class RegExpRoute extends Route { - /** - * If the regular expression contains - * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references}, - * the captured values will be passed to the - * {@link workbox-routing~handlerCallback} `params` - * argument. - * - * @param {RegExp} regExp The regular expression to match against URLs. - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - */ - constructor(regExp, handler, method) { - { - finalAssertExports.isInstance(regExp, RegExp, { - moduleName: 'workbox-routing', - className: 'RegExpRoute', - funcName: 'constructor', - paramName: 'pattern' - }); - } - const match = ({ - url - }) => { - const result = regExp.exec(url.href); - // Return immediately if there's no match. - if (!result) { - return; - } - // Require that the match start at the first character in the URL string - // if it's a cross-origin request. - // See https://github.com/GoogleChrome/workbox/issues/281 for the context - // behind this behavior. - if (url.origin !== location.origin && result.index !== 0) { - { - logger.debug(`The regular expression '${regExp.toString()}' only partially matched ` + `against the cross-origin URL '${url.toString()}'. RegExpRoute's will only ` + `handle cross-origin requests if they match the entire URL.`); - } - return; - } - // If the route matches, but there aren't any capture groups defined, then - // this will return [], which is truthy and therefore sufficient to - // indicate a match. - // If there are capture groups, then it will return their values. - return result.slice(1); - }; - super(match, handler, method); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const getFriendlyURL = url => { - const urlObj = new URL(String(url), location.href); - // See https://github.com/GoogleChrome/workbox/issues/2323 - // We want to include everything, except for the origin if it's same-origin. - return urlObj.href.replace(new RegExp(`^${location.origin}`), ''); - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The Router can be used to process a `FetchEvent` using one or more - * {@link workbox-routing.Route}, responding with a `Response` if - * a matching route exists. - * - * If no route matches a given a request, the Router will use a "default" - * handler if one is defined. - * - * Should the matching Route throw an error, the Router will use a "catch" - * handler if one is defined to gracefully deal with issues and respond with a - * Request. - * - * If a request matches multiple routes, the **earliest** registered route will - * be used to respond to the request. - * - * @memberof workbox-routing - */ - class Router { - /** - * Initializes a new Router. - */ - constructor() { - this._routes = new Map(); - this._defaultHandlerMap = new Map(); - } - /** - * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP - * method name ('GET', etc.) to an array of all the corresponding `Route` - * instances that are registered. - */ - get routes() { - return this._routes; - } - /** - * Adds a fetch event listener to respond to events when a route matches - * the event's request. - */ - addFetchListener() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('fetch', event => { - const { - request - } = event; - const responsePromise = this.handleRequest({ - request, - event - }); - if (responsePromise) { - event.respondWith(responsePromise); - } - }); - } - /** - * Adds a message event listener for URLs to cache from the window. - * This is useful to cache resources loaded on the page prior to when the - * service worker started controlling it. - * - * The format of the message data sent from the window should be as follows. - * Where the `urlsToCache` array may consist of URL strings or an array of - * URL string + `requestInit` object (the same as you'd pass to `fetch()`). - * - * ``` - * { - * type: 'CACHE_URLS', - * payload: { - * urlsToCache: [ - * './script1.js', - * './script2.js', - * ['./script3.js', {mode: 'no-cors'}], - * ], - * }, - * } - * ``` - */ - addCacheListener() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('message', event => { - // event.data is type 'any' - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (event.data && event.data.type === 'CACHE_URLS') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { - payload - } = event.data; - { - logger.debug(`Caching URLs from the window`, payload.urlsToCache); - } - const requestPromises = Promise.all(payload.urlsToCache.map(entry => { - if (typeof entry === 'string') { - entry = [entry]; - } - const request = new Request(...entry); - return this.handleRequest({ - request, - event - }); - // TODO(philipwalton): TypeScript errors without this typecast for - // some reason (probably a bug). The real type here should work but - // doesn't: `Array<Promise<Response> | undefined>`. - })); // TypeScript - event.waitUntil(requestPromises); - // If a MessageChannel was used, reply to the message on success. - if (event.ports && event.ports[0]) { - void requestPromises.then(() => event.ports[0].postMessage(true)); - } - } - }); - } - /** - * Apply the routing rules to a FetchEvent object to get a Response from an - * appropriate Route's handler. - * - * @param {Object} options - * @param {Request} options.request The request to handle. - * @param {ExtendableEvent} options.event The event that triggered the - * request. - * @return {Promise<Response>|undefined} A promise is returned if a - * registered route can handle the request. If there is no matching - * route and there's no `defaultHandler`, `undefined` is returned. - */ - handleRequest({ - request, - event - }) { - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'handleRequest', - paramName: 'options.request' - }); - } - const url = new URL(request.url, location.href); - if (!url.protocol.startsWith('http')) { - { - logger.debug(`Workbox Router only supports URLs that start with 'http'.`); - } - return; - } - const sameOrigin = url.origin === location.origin; - const { - params, - route - } = this.findMatchingRoute({ - event, - request, - sameOrigin, - url - }); - let handler = route && route.handler; - const debugMessages = []; - { - if (handler) { - debugMessages.push([`Found a route to handle this request:`, route]); - if (params) { - debugMessages.push([`Passing the following params to the route's handler:`, params]); - } - } - } - // If we don't have a handler because there was no matching route, then - // fall back to defaultHandler if that's defined. - const method = request.method; - if (!handler && this._defaultHandlerMap.has(method)) { - { - debugMessages.push(`Failed to find a matching route. Falling ` + `back to the default handler for ${method}.`); - } - handler = this._defaultHandlerMap.get(method); - } - if (!handler) { - { - // No handler so Workbox will do nothing. If logs is set of debug - // i.e. verbose, we should print out this information. - logger.debug(`No route found for: ${getFriendlyURL(url)}`); - } - return; - } - { - // We have a handler, meaning Workbox is going to handle the route. - // print the routing details to the console. - logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`); - debugMessages.forEach(msg => { - if (Array.isArray(msg)) { - logger.log(...msg); - } else { - logger.log(msg); - } - }); - logger.groupEnd(); - } - // Wrap in try and catch in case the handle method throws a synchronous - // error. It should still callback to the catch handler. - let responsePromise; - try { - responsePromise = handler.handle({ - url, - request, - event, - params - }); - } catch (err) { - responsePromise = Promise.reject(err); - } - // Get route's catch handler, if it exists - const catchHandler = route && route.catchHandler; - if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) { - responsePromise = responsePromise.catch(async err => { - // If there's a route catch handler, process that first - if (catchHandler) { - { - // Still include URL here as it will be async from the console group - // and may not make sense without the URL - logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`); - logger.error(`Error thrown by:`, route); - logger.error(err); - logger.groupEnd(); - } - try { - return await catchHandler.handle({ - url, - request, - event, - params - }); - } catch (catchErr) { - if (catchErr instanceof Error) { - err = catchErr; - } - } - } - if (this._catchHandler) { - { - // Still include URL here as it will be async from the console group - // and may not make sense without the URL - logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`); - logger.error(`Error thrown by:`, route); - logger.error(err); - logger.groupEnd(); - } - return this._catchHandler.handle({ - url, - request, - event - }); - } - throw err; - }); - } - return responsePromise; - } - /** - * Checks a request and URL (and optionally an event) against the list of - * registered routes, and if there's a match, returns the corresponding - * route along with any params generated by the match. - * - * @param {Object} options - * @param {URL} options.url - * @param {boolean} options.sameOrigin The result of comparing `url.origin` - * against the current origin. - * @param {Request} options.request The request to match. - * @param {Event} options.event The corresponding event. - * @return {Object} An object with `route` and `params` properties. - * They are populated if a matching route was found or `undefined` - * otherwise. - */ - findMatchingRoute({ - url, - sameOrigin, - request, - event - }) { - const routes = this._routes.get(request.method) || []; - for (const route of routes) { - let params; - // route.match returns type any, not possible to change right now. - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const matchResult = route.match({ - url, - sameOrigin, - request, - event - }); - if (matchResult) { - { - // Warn developers that using an async matchCallback is almost always - // not the right thing to do. - if (matchResult instanceof Promise) { - logger.warn(`While routing ${getFriendlyURL(url)}, an async ` + `matchCallback function was used. Please convert the ` + `following route to use a synchronous matchCallback function:`, route); - } - } - // See https://github.com/GoogleChrome/workbox/issues/2079 - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - params = matchResult; - if (Array.isArray(params) && params.length === 0) { - // Instead of passing an empty array in as params, use undefined. - params = undefined; - } else if (matchResult.constructor === Object && - // eslint-disable-line - Object.keys(matchResult).length === 0) { - // Instead of passing an empty object in as params, use undefined. - params = undefined; - } else if (typeof matchResult === 'boolean') { - // For the boolean value true (rather than just something truth-y), - // don't set params. - // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353 - params = undefined; - } - // Return early if have a match. - return { - route, - params - }; - } - } - // If no match was found above, return and empty object. - return {}; - } - /** - * Define a default `handler` that's called when no routes explicitly - * match the incoming request. - * - * Each HTTP method ('GET', 'POST', etc.) gets its own default handler. - * - * Without a default handler, unmatched requests will go against the - * network as if there were no service worker present. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {string} [method='GET'] The HTTP method to associate with this - * default handler. Each method has its own default. - */ - setDefaultHandler(handler, method = defaultMethod) { - this._defaultHandlerMap.set(method, normalizeHandler(handler)); - } - /** - * If a Route throws an error while handling a request, this `handler` - * will be called and given a chance to provide a response. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - */ - setCatchHandler(handler) { - this._catchHandler = normalizeHandler(handler); - } - /** - * Registers a route with the router. - * - * @param {workbox-routing.Route} route The route to register. - */ - registerRoute(route) { - { - finalAssertExports.isType(route, 'object', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.hasMethod(route, 'match', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.isType(route.handler, 'object', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.hasMethod(route.handler, 'handle', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route.handler' - }); - finalAssertExports.isType(route.method, 'string', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route.method' - }); - } - if (!this._routes.has(route.method)) { - this._routes.set(route.method, []); - } - // Give precedence to all of the earlier routes by adding this additional - // route to the end of the array. - this._routes.get(route.method).push(route); - } - /** - * Unregisters a route with the router. - * - * @param {workbox-routing.Route} route The route to unregister. - */ - unregisterRoute(route) { - if (!this._routes.has(route.method)) { - throw new WorkboxError('unregister-route-but-not-found-with-method', { - method: route.method - }); - } - const routeIndex = this._routes.get(route.method).indexOf(route); - if (routeIndex > -1) { - this._routes.get(route.method).splice(routeIndex, 1); - } else { - throw new WorkboxError('unregister-route-route-not-registered'); - } - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let defaultRouter; - /** - * Creates a new, singleton Router instance if one does not exist. If one - * does already exist, that instance is returned. - * - * @private - * @return {Router} - */ - const getOrCreateDefaultRouter = () => { - if (!defaultRouter) { - defaultRouter = new Router(); - // The helpers that use the default Router assume these listeners exist. - defaultRouter.addFetchListener(); - defaultRouter.addCacheListener(); - } - return defaultRouter; - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Easily register a RegExp, string, or function with a caching - * strategy to a singleton Router instance. - * - * This method will generate a Route for you if needed and - * call {@link workbox-routing.Router#registerRoute}. - * - * @param {RegExp|string|workbox-routing.Route~matchCallback|workbox-routing.Route} capture - * If the capture param is a `Route`, all other arguments will be ignored. - * @param {workbox-routing~handlerCallback} [handler] A callback - * function that returns a Promise resulting in a Response. This parameter - * is required if `capture` is not a `Route` object. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - * @return {workbox-routing.Route} The generated `Route`. - * - * @memberof workbox-routing - */ - function registerRoute(capture, handler, method) { - let route; - if (typeof capture === 'string') { - const captureUrl = new URL(capture, location.href); - { - if (!(capture.startsWith('/') || capture.startsWith('http'))) { - throw new WorkboxError('invalid-string', { - moduleName: 'workbox-routing', - funcName: 'registerRoute', - paramName: 'capture' - }); - } - // We want to check if Express-style wildcards are in the pathname only. - // TODO: Remove this log message in v4. - const valueToCheck = capture.startsWith('http') ? captureUrl.pathname : capture; - // See https://github.com/pillarjs/path-to-regexp#parameters - const wildcards = '[*:?+]'; - if (new RegExp(`${wildcards}`).exec(valueToCheck)) { - logger.debug(`The '$capture' parameter contains an Express-style wildcard ` + `character (${wildcards}). Strings are now always interpreted as ` + `exact matches; use a RegExp for partial or wildcard matches.`); - } - } - const matchCallback = ({ - url - }) => { - { - if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) { - logger.debug(`${capture} only partially matches the cross-origin URL ` + `${url.toString()}. This route will only handle cross-origin requests ` + `if they match the entire URL.`); - } - } - return url.href === captureUrl.href; - }; - // If `capture` is a string then `handler` and `method` must be present. - route = new Route(matchCallback, handler, method); - } else if (capture instanceof RegExp) { - // If `capture` is a `RegExp` then `handler` and `method` must be present. - route = new RegExpRoute(capture, handler, method); - } else if (typeof capture === 'function') { - // If `capture` is a function then `handler` and `method` must be present. - route = new Route(capture, handler, method); - } else if (capture instanceof Route) { - route = capture; - } else { - throw new WorkboxError('unsupported-route-type', { - moduleName: 'workbox-routing', - funcName: 'registerRoute', - paramName: 'capture' - }); - } - const defaultRouter = getOrCreateDefaultRouter(); - defaultRouter.registerRoute(route); - return route; - } - - // @ts-ignore - try { - self['workbox:cacheable-response:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * This class allows you to set up rules determining what - * status codes and/or headers need to be present in order for a - * [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) - * to be considered cacheable. - * - * @memberof workbox-cacheable-response - */ - class CacheableResponse { - /** - * To construct a new CacheableResponse instance you must provide at least - * one of the `config` properties. - * - * If both `statuses` and `headers` are specified, then both conditions must - * be met for the `Response` to be considered cacheable. - * - * @param {Object} config - * @param {Array<number>} [config.statuses] One or more status codes that a - * `Response` can have and be considered cacheable. - * @param {Object<string,string>} [config.headers] A mapping of header names - * and expected values that a `Response` can have and be considered cacheable. - * If multiple headers are provided, only one needs to be present. - */ - constructor(config = {}) { - { - if (!(config.statuses || config.headers)) { - throw new WorkboxError('statuses-or-headers-required', { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor' - }); - } - if (config.statuses) { - finalAssertExports.isArray(config.statuses, { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor', - paramName: 'config.statuses' - }); - } - if (config.headers) { - finalAssertExports.isType(config.headers, 'object', { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor', - paramName: 'config.headers' - }); - } - } - this._statuses = config.statuses; - this._headers = config.headers; - } - /** - * Checks a response to see whether it's cacheable or not, based on this - * object's configuration. - * - * @param {Response} response The response whose cacheability is being - * checked. - * @return {boolean} `true` if the `Response` is cacheable, and `false` - * otherwise. - */ - isResponseCacheable(response) { - { - finalAssertExports.isInstance(response, Response, { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'isResponseCacheable', - paramName: 'response' - }); - } - let cacheable = true; - if (this._statuses) { - cacheable = this._statuses.includes(response.status); - } - if (this._headers && cacheable) { - cacheable = Object.keys(this._headers).some(headerName => { - return response.headers.get(headerName) === this._headers[headerName]; - }); - } - { - if (!cacheable) { - logger.groupCollapsed(`The request for ` + `'${getFriendlyURL(response.url)}' returned a response that does ` + `not meet the criteria for being cached.`); - logger.groupCollapsed(`View cacheability criteria here.`); - logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses)); - logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2)); - logger.groupEnd(); - const logFriendlyHeaders = {}; - response.headers.forEach((value, key) => { - logFriendlyHeaders[key] = value; - }); - logger.groupCollapsed(`View response status and headers here.`); - logger.log(`Response status: ${response.status}`); - logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2)); - logger.groupEnd(); - logger.groupCollapsed(`View full response details here.`); - logger.log(response.headers); - logger.log(response); - logger.groupEnd(); - logger.groupEnd(); - } - } - return cacheable; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A class implementing the `cacheWillUpdate` lifecycle callback. This makes it - * easier to add in cacheability checks to requests made via Workbox's built-in - * strategies. - * - * @memberof workbox-cacheable-response - */ - class CacheableResponsePlugin { - /** - * To construct a new CacheableResponsePlugin instance you must provide at - * least one of the `config` properties. - * - * If both `statuses` and `headers` are specified, then both conditions must - * be met for the `Response` to be considered cacheable. - * - * @param {Object} config - * @param {Array<number>} [config.statuses] One or more status codes that a - * `Response` can have and be considered cacheable. - * @param {Object<string,string>} [config.headers] A mapping of header names - * and expected values that a `Response` can have and be considered cacheable. - * If multiple headers are provided, only one needs to be present. - */ - constructor(config) { - /** - * @param {Object} options - * @param {Response} options.response - * @return {Response|null} - * @private - */ - this.cacheWillUpdate = async ({ - response - }) => { - if (this._cacheableResponse.isResponseCacheable(response)) { - return response; - } - return null; - }; - this._cacheableResponse = new CacheableResponse(config); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const _cacheNameDetails = { - googleAnalytics: 'googleAnalytics', - precache: 'precache-v2', - prefix: 'workbox', - runtime: 'runtime', - suffix: typeof registration !== 'undefined' ? registration.scope : '' - }; - const _createCacheName = cacheName => { - return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value && value.length > 0).join('-'); - }; - const eachCacheNameDetail = fn => { - for (const key of Object.keys(_cacheNameDetails)) { - fn(key); - } - }; - const cacheNames = { - updateDetails: details => { - eachCacheNameDetail(key => { - if (typeof details[key] === 'string') { - _cacheNameDetails[key] = details[key]; - } - }); - }, - getGoogleAnalyticsName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics); - }, - getPrecacheName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.precache); - }, - getPrefix: () => { - return _cacheNameDetails.prefix; - }, - getRuntimeName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.runtime); - }, - getSuffix: () => { - return _cacheNameDetails.suffix; - } - }; - - /* - Copyright 2020 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - function stripParams(fullURL, ignoreParams) { - const strippedURL = new URL(fullURL); - for (const param of ignoreParams) { - strippedURL.searchParams.delete(param); - } - return strippedURL.href; - } - /** - * Matches an item in the cache, ignoring specific URL params. This is similar - * to the `ignoreSearch` option, but it allows you to ignore just specific - * params (while continuing to match on the others). - * - * @private - * @param {Cache} cache - * @param {Request} request - * @param {Object} matchOptions - * @param {Array<string>} ignoreParams - * @return {Promise<Response|undefined>} - */ - async function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) { - const strippedRequestURL = stripParams(request.url, ignoreParams); - // If the request doesn't include any ignored params, match as normal. - if (request.url === strippedRequestURL) { - return cache.match(request, matchOptions); - } - // Otherwise, match by comparing keys - const keysOptions = Object.assign(Object.assign({}, matchOptions), { - ignoreSearch: true - }); - const cacheKeys = await cache.keys(request, keysOptions); - for (const cacheKey of cacheKeys) { - const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams); - if (strippedRequestURL === strippedCacheKeyURL) { - return cache.match(cacheKey, matchOptions); - } - } - return; - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The Deferred class composes Promises in a way that allows for them to be - * resolved or rejected from outside the constructor. In most cases promises - * should be used directly, but Deferreds can be necessary when the logic to - * resolve a promise must be separate. - * - * @private - */ - class Deferred { - /** - * Creates a promise and exposes its resolve and reject functions as methods. - */ - constructor() { - this.promise = new Promise((resolve, reject) => { - this.resolve = resolve; - this.reject = reject; - }); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - // Callbacks to be executed whenever there's a quota error. - // Can't change Function type right now. - // eslint-disable-next-line @typescript-eslint/ban-types - const quotaErrorCallbacks = new Set(); - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Runs all of the callback functions, one at a time sequentially, in the order - * in which they were registered. - * - * @memberof workbox-core - * @private - */ - async function executeQuotaErrorCallbacks() { - { - logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`); - } - for (const callback of quotaErrorCallbacks) { - await callback(); - { - logger.log(callback, 'is complete.'); - } - } - { - logger.log('Finished running callbacks.'); - } - } - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Returns a promise that resolves and the passed number of milliseconds. - * This utility is an async/await-friendly version of `setTimeout`. - * - * @param {number} ms - * @return {Promise} - * @private - */ - function timeout(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); - } - - // @ts-ignore - try { - self['workbox:strategies:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - function toRequest(input) { - return typeof input === 'string' ? new Request(input) : input; - } - /** - * A class created every time a Strategy instance instance calls - * {@link workbox-strategies.Strategy~handle} or - * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and - * cache actions around plugin callbacks and keeps track of when the strategy - * is "done" (i.e. all added `event.waitUntil()` promises have resolved). - * - * @memberof workbox-strategies - */ - class StrategyHandler { - /** - * Creates a new instance associated with the passed strategy and event - * that's handling the request. - * - * The constructor also initializes the state that will be passed to each of - * the plugins handling this request. - * - * @param {workbox-strategies.Strategy} strategy - * @param {Object} options - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] The return value from the - * {@link workbox-routing~matchCallback} (if applicable). - */ - constructor(strategy, options) { - this._cacheKeys = {}; - /** - * The request the strategy is performing (passed to the strategy's - * `handle()` or `handleAll()` method). - * @name request - * @instance - * @type {Request} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * The event associated with this request. - * @name event - * @instance - * @type {ExtendableEvent} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * A `URL` instance of `request.url` (if passed to the strategy's - * `handle()` or `handleAll()` method). - * Note: the `url` param will be present if the strategy was invoked - * from a workbox `Route` object. - * @name url - * @instance - * @type {URL|undefined} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * A `param` value (if passed to the strategy's - * `handle()` or `handleAll()` method). - * Note: the `param` param will be present if the strategy was invoked - * from a workbox `Route` object and the - * {@link workbox-routing~matchCallback} returned - * a truthy value (it will be that value). - * @name params - * @instance - * @type {*|undefined} - * @memberof workbox-strategies.StrategyHandler - */ - { - finalAssertExports.isInstance(options.event, ExtendableEvent, { - moduleName: 'workbox-strategies', - className: 'StrategyHandler', - funcName: 'constructor', - paramName: 'options.event' - }); - } - Object.assign(this, options); - this.event = options.event; - this._strategy = strategy; - this._handlerDeferred = new Deferred(); - this._extendLifetimePromises = []; - // Copy the plugins list (since it's mutable on the strategy), - // so any mutations don't affect this handler instance. - this._plugins = [...strategy.plugins]; - this._pluginStateMap = new Map(); - for (const plugin of this._plugins) { - this._pluginStateMap.set(plugin, {}); - } - this.event.waitUntil(this._handlerDeferred.promise); - } - /** - * Fetches a given request (and invokes any applicable plugin callback - * methods) using the `fetchOptions` (for non-navigation requests) and - * `plugins` defined on the `Strategy` object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - `requestWillFetch()` - * - `fetchDidSucceed()` - * - `fetchDidFail()` - * - * @param {Request|string} input The URL or request to fetch. - * @return {Promise<Response>} - */ - async fetch(input) { - const { - event - } = this; - let request = toRequest(input); - if (request.mode === 'navigate' && event instanceof FetchEvent && event.preloadResponse) { - const possiblePreloadResponse = await event.preloadResponse; - if (possiblePreloadResponse) { - { - logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`); - } - return possiblePreloadResponse; - } - } - // If there is a fetchDidFail plugin, we need to save a clone of the - // original request before it's either modified by a requestWillFetch - // plugin or before the original request's body is consumed via fetch(). - const originalRequest = this.hasCallback('fetchDidFail') ? request.clone() : null; - try { - for (const cb of this.iterateCallbacks('requestWillFetch')) { - request = await cb({ - request: request.clone(), - event - }); - } - } catch (err) { - if (err instanceof Error) { - throw new WorkboxError('plugin-error-request-will-fetch', { - thrownErrorMessage: err.message - }); - } - } - // The request can be altered by plugins with `requestWillFetch` making - // the original request (most likely from a `fetch` event) different - // from the Request we make. Pass both to `fetchDidFail` to aid debugging. - const pluginFilteredRequest = request.clone(); - try { - let fetchResponse; - // See https://github.com/GoogleChrome/workbox/issues/1796 - fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions); - if ("development" !== 'production') { - logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`); - } - for (const callback of this.iterateCallbacks('fetchDidSucceed')) { - fetchResponse = await callback({ - event, - request: pluginFilteredRequest, - response: fetchResponse - }); - } - return fetchResponse; - } catch (error) { - { - logger.log(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error); - } - // `originalRequest` will only exist if a `fetchDidFail` callback - // is being used (see above). - if (originalRequest) { - await this.runCallbacks('fetchDidFail', { - error: error, - event, - originalRequest: originalRequest.clone(), - request: pluginFilteredRequest.clone() - }); - } - throw error; - } - } - /** - * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on - * the response generated by `this.fetch()`. - * - * The call to `this.cachePut()` automatically invokes `this.waitUntil()`, - * so you do not have to manually call `waitUntil()` on the event. - * - * @param {Request|string} input The request or URL to fetch and cache. - * @return {Promise<Response>} - */ - async fetchAndCachePut(input) { - const response = await this.fetch(input); - const responseClone = response.clone(); - void this.waitUntil(this.cachePut(input, responseClone)); - return response; - } - /** - * Matches a request from the cache (and invokes any applicable plugin - * callback methods) using the `cacheName`, `matchOptions`, and `plugins` - * defined on the strategy object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - cacheKeyWillBeUsed() - * - cachedResponseWillBeUsed() - * - * @param {Request|string} key The Request or URL to use as the cache key. - * @return {Promise<Response|undefined>} A matching response, if found. - */ - async cacheMatch(key) { - const request = toRequest(key); - let cachedResponse; - const { - cacheName, - matchOptions - } = this._strategy; - const effectiveRequest = await this.getCacheKey(request, 'read'); - const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), { - cacheName - }); - cachedResponse = await caches.match(effectiveRequest, multiMatchOptions); - { - if (cachedResponse) { - logger.debug(`Found a cached response in '${cacheName}'.`); - } else { - logger.debug(`No cached response found in '${cacheName}'.`); - } - } - for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) { - cachedResponse = (await callback({ - cacheName, - matchOptions, - cachedResponse, - request: effectiveRequest, - event: this.event - })) || undefined; - } - return cachedResponse; - } - /** - * Puts a request/response pair in the cache (and invokes any applicable - * plugin callback methods) using the `cacheName` and `plugins` defined on - * the strategy object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - cacheKeyWillBeUsed() - * - cacheWillUpdate() - * - cacheDidUpdate() - * - * @param {Request|string} key The request or URL to use as the cache key. - * @param {Response} response The response to cache. - * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response - * not be cached, and `true` otherwise. - */ - async cachePut(key, response) { - const request = toRequest(key); - // Run in the next task to avoid blocking other cache reads. - // https://github.com/w3c/ServiceWorker/issues/1397 - await timeout(0); - const effectiveRequest = await this.getCacheKey(request, 'write'); - { - if (effectiveRequest.method && effectiveRequest.method !== 'GET') { - throw new WorkboxError('attempt-to-cache-non-get-request', { - url: getFriendlyURL(effectiveRequest.url), - method: effectiveRequest.method - }); - } - // See https://github.com/GoogleChrome/workbox/issues/2818 - const vary = response.headers.get('Vary'); - if (vary) { - logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` + `has a 'Vary: ${vary}' header. ` + `Consider setting the {ignoreVary: true} option on your strategy ` + `to ensure cache matching and deletion works as expected.`); - } - } - if (!response) { - { - logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`); - } - throw new WorkboxError('cache-put-with-no-response', { - url: getFriendlyURL(effectiveRequest.url) - }); - } - const responseToCache = await this._ensureResponseSafeToCache(response); - if (!responseToCache) { - { - logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` + `will not be cached.`, responseToCache); - } - return false; - } - const { - cacheName, - matchOptions - } = this._strategy; - const cache = await self.caches.open(cacheName); - const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate'); - const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams( - // TODO(philipwalton): the `__WB_REVISION__` param is a precaching - // feature. Consider into ways to only add this behavior if using - // precaching. - cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions) : null; - { - logger.debug(`Updating the '${cacheName}' cache with a new Response ` + `for ${getFriendlyURL(effectiveRequest.url)}.`); - } - try { - await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache); - } catch (error) { - if (error instanceof Error) { - // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError - if (error.name === 'QuotaExceededError') { - await executeQuotaErrorCallbacks(); - } - throw error; - } - } - for (const callback of this.iterateCallbacks('cacheDidUpdate')) { - await callback({ - cacheName, - oldResponse, - newResponse: responseToCache.clone(), - request: effectiveRequest, - event: this.event - }); - } - return true; - } - /** - * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and - * executes any of those callbacks found in sequence. The final `Request` - * object returned by the last plugin is treated as the cache key for cache - * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have - * been registered, the passed request is returned unmodified - * - * @param {Request} request - * @param {string} mode - * @return {Promise<Request>} - */ - async getCacheKey(request, mode) { - const key = `${request.url} | ${mode}`; - if (!this._cacheKeys[key]) { - let effectiveRequest = request; - for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) { - effectiveRequest = toRequest(await callback({ - mode, - request: effectiveRequest, - event: this.event, - // params has a type any can't change right now. - params: this.params // eslint-disable-line - })); - } - this._cacheKeys[key] = effectiveRequest; - } - return this._cacheKeys[key]; - } - /** - * Returns true if the strategy has at least one plugin with the given - * callback. - * - * @param {string} name The name of the callback to check for. - * @return {boolean} - */ - hasCallback(name) { - for (const plugin of this._strategy.plugins) { - if (name in plugin) { - return true; - } - } - return false; - } - /** - * Runs all plugin callbacks matching the given name, in order, passing the - * given param object (merged ith the current plugin state) as the only - * argument. - * - * Note: since this method runs all plugins, it's not suitable for cases - * where the return value of a callback needs to be applied prior to calling - * the next callback. See - * {@link workbox-strategies.StrategyHandler#iterateCallbacks} - * below for how to handle that case. - * - * @param {string} name The name of the callback to run within each plugin. - * @param {Object} param The object to pass as the first (and only) param - * when executing each callback. This object will be merged with the - * current plugin state prior to callback execution. - */ - async runCallbacks(name, param) { - for (const callback of this.iterateCallbacks(name)) { - // TODO(philipwalton): not sure why `any` is needed. It seems like - // this should work with `as WorkboxPluginCallbackParam[C]`. - await callback(param); - } - } - /** - * Accepts a callback and returns an iterable of matching plugin callbacks, - * where each callback is wrapped with the current handler state (i.e. when - * you call each callback, whatever object parameter you pass it will - * be merged with the plugin's current state). - * - * @param {string} name The name fo the callback to run - * @return {Array<Function>} - */ - *iterateCallbacks(name) { - for (const plugin of this._strategy.plugins) { - if (typeof plugin[name] === 'function') { - const state = this._pluginStateMap.get(plugin); - const statefulCallback = param => { - const statefulParam = Object.assign(Object.assign({}, param), { - state - }); - // TODO(philipwalton): not sure why `any` is needed. It seems like - // this should work with `as WorkboxPluginCallbackParam[C]`. - return plugin[name](statefulParam); - }; - yield statefulCallback; - } - } - } - /** - * Adds a promise to the - * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises} - * of the event event associated with the request being handled (usually a - * `FetchEvent`). - * - * Note: you can await - * {@link workbox-strategies.StrategyHandler~doneWaiting} - * to know when all added promises have settled. - * - * @param {Promise} promise A promise to add to the extend lifetime promises - * of the event that triggered the request. - */ - waitUntil(promise) { - this._extendLifetimePromises.push(promise); - return promise; - } - /** - * Returns a promise that resolves once all promises passed to - * {@link workbox-strategies.StrategyHandler~waitUntil} - * have settled. - * - * Note: any work done after `doneWaiting()` settles should be manually - * passed to an event's `waitUntil()` method (not this handler's - * `waitUntil()` method), otherwise the service worker thread my be killed - * prior to your work completing. - */ - async doneWaiting() { - let promise; - while (promise = this._extendLifetimePromises.shift()) { - await promise; - } - } - /** - * Stops running the strategy and immediately resolves any pending - * `waitUntil()` promises. - */ - destroy() { - this._handlerDeferred.resolve(null); - } - /** - * This method will call cacheWillUpdate on the available plugins (or use - * status === 200) to determine if the Response is safe and valid to cache. - * - * @param {Request} options.request - * @param {Response} options.response - * @return {Promise<Response|undefined>} - * - * @private - */ - async _ensureResponseSafeToCache(response) { - let responseToCache = response; - let pluginsUsed = false; - for (const callback of this.iterateCallbacks('cacheWillUpdate')) { - responseToCache = (await callback({ - request: this.request, - response: responseToCache, - event: this.event - })) || undefined; - pluginsUsed = true; - if (!responseToCache) { - break; - } - } - if (!pluginsUsed) { - if (responseToCache && responseToCache.status !== 200) { - responseToCache = undefined; - } - { - if (responseToCache) { - if (responseToCache.status !== 200) { - if (responseToCache.status === 0) { - logger.warn(`The response for '${this.request.url}' ` + `is an opaque response. The caching strategy that you're ` + `using will not cache opaque responses by default.`); - } else { - logger.debug(`The response for '${this.request.url}' ` + `returned a status code of '${response.status}' and won't ` + `be cached as a result.`); - } - } - } - } - } - return responseToCache; - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An abstract base class that all other strategy classes must extend from: - * - * @memberof workbox-strategies - */ - class Strategy { - /** - * Creates a new instance of the strategy and sets all documented option - * properties as public instance properties. - * - * Note: if a custom strategy class extends the base Strategy class and does - * not need more than these properties, it does not need to define its own - * constructor. - * - * @param {Object} [options] - * @param {string} [options.cacheName] Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796) - * `fetch()` requests made by this strategy. - * @param {Object} [options.matchOptions] The - * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - */ - constructor(options = {}) { - /** - * Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * - * @type {string} - */ - this.cacheName = cacheNames.getRuntimeName(options.cacheName); - /** - * The list - * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * used by this strategy. - * - * @type {Array<Object>} - */ - this.plugins = options.plugins || []; - /** - * Values passed along to the - * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters} - * of all fetch() requests made by this strategy. - * - * @type {Object} - */ - this.fetchOptions = options.fetchOptions; - /** - * The - * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - * - * @type {Object} - */ - this.matchOptions = options.matchOptions; - } - /** - * Perform a request strategy and returns a `Promise` that will resolve with - * a `Response`, invoking all relevant plugin callbacks. - * - * When a strategy instance is registered with a Workbox - * {@link workbox-routing.Route}, this method is automatically - * called when the route matches. - * - * Alternatively, this method can be used in a standalone `FetchEvent` - * listener by passing it to `event.respondWith()`. - * - * @param {FetchEvent|Object} options A `FetchEvent` or an object with the - * properties listed below. - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] - */ - handle(options) { - const [responseDone] = this.handleAll(options); - return responseDone; - } - /** - * Similar to {@link workbox-strategies.Strategy~handle}, but - * instead of just returning a `Promise` that resolves to a `Response` it - * it will return an tuple of `[response, done]` promises, where the former - * (`response`) is equivalent to what `handle()` returns, and the latter is a - * Promise that will resolve once any promises that were added to - * `event.waitUntil()` as part of performing the strategy have completed. - * - * You can await the `done` promise to ensure any extra work performed by - * the strategy (usually caching responses) completes successfully. - * - * @param {FetchEvent|Object} options A `FetchEvent` or an object with the - * properties listed below. - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] - * @return {Array<Promise>} A tuple of [response, done] - * promises that can be used to determine when the response resolves as - * well as when the handler has completed all its work. - */ - handleAll(options) { - // Allow for flexible options to be passed. - if (options instanceof FetchEvent) { - options = { - event: options, - request: options.request - }; - } - const event = options.event; - const request = typeof options.request === 'string' ? new Request(options.request) : options.request; - const params = 'params' in options ? options.params : undefined; - const handler = new StrategyHandler(this, { - event, - request, - params - }); - const responseDone = this._getResponse(handler, request, event); - const handlerDone = this._awaitComplete(responseDone, handler, request, event); - // Return an array of promises, suitable for use with Promise.all(). - return [responseDone, handlerDone]; - } - async _getResponse(handler, request, event) { - await handler.runCallbacks('handlerWillStart', { - event, - request - }); - let response = undefined; - try { - response = await this._handle(request, handler); - // The "official" Strategy subclasses all throw this error automatically, - // but in case a third-party Strategy doesn't, ensure that we have a - // consistent failure when there's no response or an error response. - if (!response || response.type === 'error') { - throw new WorkboxError('no-response', { - url: request.url - }); - } - } catch (error) { - if (error instanceof Error) { - for (const callback of handler.iterateCallbacks('handlerDidError')) { - response = await callback({ - error, - event, - request - }); - if (response) { - break; - } - } - } - if (!response) { - throw error; - } else { - logger.log(`While responding to '${getFriendlyURL(request.url)}', ` + `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` + `a handlerDidError plugin.`); - } - } - for (const callback of handler.iterateCallbacks('handlerWillRespond')) { - response = await callback({ - event, - request, - response - }); - } - return response; - } - async _awaitComplete(responseDone, handler, request, event) { - let response; - let error; - try { - response = await responseDone; - } catch (error) { - // Ignore errors, as response errors should be caught via the `response` - // promise above. The `done` promise will only throw for errors in - // promises passed to `handler.waitUntil()`. - } - try { - await handler.runCallbacks('handlerDidRespond', { - event, - request, - response - }); - await handler.doneWaiting(); - } catch (waitUntilError) { - if (waitUntilError instanceof Error) { - error = waitUntilError; - } - } - await handler.runCallbacks('handlerDidComplete', { - event, - request, - response, - error: error - }); - handler.destroy(); - if (error) { - throw error; - } - } - } - /** - * Classes extending the `Strategy` based class should implement this method, - * and leverage the {@link workbox-strategies.StrategyHandler} - * arg to perform all fetching and cache logic, which will ensure all relevant - * cache, cache options, fetch options and plugins are used (per the current - * strategy instance). - * - * @name _handle - * @instance - * @abstract - * @function - * @param {Request} request - * @param {workbox-strategies.StrategyHandler} handler - * @return {Promise<Response>} - * - * @memberof workbox-strategies.Strategy - */ - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const messages = { - strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`, - printFinalResponse: response => { - if (response) { - logger.groupCollapsed(`View the final response here.`); - logger.log(response || '[No response returned]'); - logger.groupEnd(); - } - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network) - * request strategy. - * - * A cache first strategy is useful for assets that have been revisioned, - * such as URLs like `/styles/example.a8f5f1.css`, since they - * can be cached for long periods of time. - * - * If the network request fails, and there is no cache match, this will throw - * a `WorkboxError` exception. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-strategies - */ - class CacheFirst extends Strategy { - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - const logs = []; - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-strategies', - className: this.constructor.name, - funcName: 'makeRequest', - paramName: 'request' - }); - } - let response = await handler.cacheMatch(request); - let error = undefined; - if (!response) { - { - logs.push(`No response found in the '${this.cacheName}' cache. ` + `Will respond with a network request.`); - } - try { - response = await handler.fetchAndCachePut(request); - } catch (err) { - if (err instanceof Error) { - error = err; - } - } - { - if (response) { - logs.push(`Got response from network.`); - } else { - logs.push(`Unable to get a response from the network.`); - } - } - } else { - { - logs.push(`Found a cached response in the '${this.cacheName}' cache.`); - } - } - { - logger.groupCollapsed(messages.strategyStart(this.constructor.name, request)); - for (const log of logs) { - logger.log(log); - } - messages.printFinalResponse(response); - logger.groupEnd(); - } - if (!response) { - throw new WorkboxError('no-response', { - url: request.url, - error - }); - } - return response; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An implementation of a - * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only) - * request strategy. - * - * This class is useful if you want to take advantage of any - * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/). - * - * If the network request fails, this will throw a `WorkboxError` exception. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-strategies - */ - class NetworkOnly extends Strategy { - /** - * @param {Object} [options] - * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796) - * `fetch()` requests made by this strategy. - * @param {number} [options.networkTimeoutSeconds] If set, any network requests - * that fail to respond within the timeout will result in a network error. - */ - constructor(options = {}) { - super(options); - this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0; - } - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-strategies', - className: this.constructor.name, - funcName: '_handle', - paramName: 'request' - }); - } - let error = undefined; - let response; - try { - const promises = [handler.fetch(request)]; - if (this._networkTimeoutSeconds) { - const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000); - promises.push(timeoutPromise); - } - response = await Promise.race(promises); - if (!response) { - throw new Error(`Timed out the network response after ` + `${this._networkTimeoutSeconds} seconds.`); - } - } catch (err) { - if (err instanceof Error) { - error = err; - } - } - { - logger.groupCollapsed(messages.strategyStart(this.constructor.name, request)); - if (response) { - logger.log(`Got response from network.`); - } else { - logger.log(`Unable to get a response from the network.`); - } - messages.printFinalResponse(response); - logger.groupEnd(); - } - if (!response) { - throw new WorkboxError('no-response', { - url: request.url, - error - }); - } - return response; - } - } - - /* - Copyright 2020 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A utility method that makes it easier to use `event.waitUntil` with - * async functions and return the result. - * - * @param {ExtendableEvent} event - * @param {Function} asyncFn - * @return {Function} - * @private - */ - function waitUntil(event, asyncFn) { - const returnPromise = asyncFn(); - event.waitUntil(returnPromise); - return returnPromise; - } - - // @ts-ignore - try { - self['workbox:precaching:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - // Name of the search parameter used to store revision info. - const REVISION_SEARCH_PARAM = '__WB_REVISION__'; - /** - * Converts a manifest entry into a versioned URL suitable for precaching. - * - * @param {Object|string} entry - * @return {string} A URL with versioning info. - * - * @private - * @memberof workbox-precaching - */ - function createCacheKey(entry) { - if (!entry) { - throw new WorkboxError('add-to-cache-list-unexpected-type', { - entry - }); - } - // If a precache manifest entry is a string, it's assumed to be a versioned - // URL, like '/app.abcd1234.js'. Return as-is. - if (typeof entry === 'string') { - const urlObject = new URL(entry, location.href); - return { - cacheKey: urlObject.href, - url: urlObject.href - }; - } - const { - revision, - url - } = entry; - if (!url) { - throw new WorkboxError('add-to-cache-list-unexpected-type', { - entry - }); - } - // If there's just a URL and no revision, then it's also assumed to be a - // versioned URL. - if (!revision) { - const urlObject = new URL(url, location.href); - return { - cacheKey: urlObject.href, - url: urlObject.href - }; - } - // Otherwise, construct a properly versioned URL using the custom Workbox - // search parameter along with the revision info. - const cacheKeyURL = new URL(url, location.href); - const originalURL = new URL(url, location.href); - cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision); - return { - cacheKey: cacheKeyURL.href, - url: originalURL.href - }; - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A plugin, designed to be used with PrecacheController, to determine the - * of assets that were updated (or not updated) during the install event. - * - * @private - */ - class PrecacheInstallReportPlugin { - constructor() { - this.updatedURLs = []; - this.notUpdatedURLs = []; - this.handlerWillStart = async ({ - request, - state - }) => { - // TODO: `state` should never be undefined... - if (state) { - state.originalRequest = request; - } - }; - this.cachedResponseWillBeUsed = async ({ - event, - state, - cachedResponse - }) => { - if (event.type === 'install') { - if (state && state.originalRequest && state.originalRequest instanceof Request) { - // TODO: `state` should never be undefined... - const url = state.originalRequest.url; - if (cachedResponse) { - this.notUpdatedURLs.push(url); - } else { - this.updatedURLs.push(url); - } - } - } - return cachedResponse; - }; - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A plugin, designed to be used with PrecacheController, to translate URLs into - * the corresponding cache key, based on the current revision info. - * - * @private - */ - class PrecacheCacheKeyPlugin { - constructor({ - precacheController - }) { - this.cacheKeyWillBeUsed = async ({ - request, - params - }) => { - // Params is type any, can't change right now. - /* eslint-disable */ - const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) || this._precacheController.getCacheKeyForURL(request.url); - /* eslint-enable */ - return cacheKey ? new Request(cacheKey, { - headers: request.headers - }) : request; - }; - this._precacheController = precacheController; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {string} groupTitle - * @param {Array<string>} deletedURLs - * - * @private - */ - const logGroup = (groupTitle, deletedURLs) => { - logger.groupCollapsed(groupTitle); - for (const url of deletedURLs) { - logger.log(url); - } - logger.groupEnd(); - }; - /** - * @param {Array<string>} deletedURLs - * - * @private - * @memberof workbox-precaching - */ - function printCleanupDetails(deletedURLs) { - const deletionCount = deletedURLs.length; - if (deletionCount > 0) { - logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`); - logGroup('Deleted Cache Requests', deletedURLs); - logger.groupEnd(); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {string} groupTitle - * @param {Array<string>} urls - * - * @private - */ - function _nestedGroup(groupTitle, urls) { - if (urls.length === 0) { - return; - } - logger.groupCollapsed(groupTitle); - for (const url of urls) { - logger.log(url); - } - logger.groupEnd(); - } - /** - * @param {Array<string>} urlsToPrecache - * @param {Array<string>} urlsAlreadyPrecached - * - * @private - * @memberof workbox-precaching - */ - function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) { - const precachedCount = urlsToPrecache.length; - const alreadyPrecachedCount = urlsAlreadyPrecached.length; - if (precachedCount || alreadyPrecachedCount) { - let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`; - if (alreadyPrecachedCount > 0) { - message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`; - } - logger.groupCollapsed(message); - _nestedGroup(`View newly precached URLs.`, urlsToPrecache); - _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached); - logger.groupEnd(); - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let supportStatus; - /** - * A utility function that determines whether the current browser supports - * constructing a new `Response` from a `response.body` stream. - * - * @return {boolean} `true`, if the current browser can successfully - * construct a `Response` from a `response.body` stream, `false` otherwise. - * - * @private - */ - function canConstructResponseFromBodyStream() { - if (supportStatus === undefined) { - const testResponse = new Response(''); - if ('body' in testResponse) { - try { - new Response(testResponse.body); - supportStatus = true; - } catch (error) { - supportStatus = false; - } - } - supportStatus = false; - } - return supportStatus; - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Allows developers to copy a response and modify its `headers`, `status`, - * or `statusText` values (the values settable via a - * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax} - * object in the constructor). - * To modify these values, pass a function as the second argument. That - * function will be invoked with a single object with the response properties - * `{headers, status, statusText}`. The return value of this function will - * be used as the `ResponseInit` for the new `Response`. To change the values - * either modify the passed parameter(s) and return it, or return a totally - * new object. - * - * This method is intentionally limited to same-origin responses, regardless of - * whether CORS was used or not. - * - * @param {Response} response - * @param {Function} modifier - * @memberof workbox-core - */ - async function copyResponse(response, modifier) { - let origin = null; - // If response.url isn't set, assume it's cross-origin and keep origin null. - if (response.url) { - const responseURL = new URL(response.url); - origin = responseURL.origin; - } - if (origin !== self.location.origin) { - throw new WorkboxError('cross-origin-copy-response', { - origin - }); - } - const clonedResponse = response.clone(); - // Create a fresh `ResponseInit` object by cloning the headers. - const responseInit = { - headers: new Headers(clonedResponse.headers), - status: clonedResponse.status, - statusText: clonedResponse.statusText - }; - // Apply any user modifications. - const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit; - // Create the new response from the body stream and `ResponseInit` - // modifications. Note: not all browsers support the Response.body stream, - // so fall back to reading the entire body into memory as a blob. - const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob(); - return new Response(body, modifiedResponseInit); - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A {@link workbox-strategies.Strategy} implementation - * specifically designed to work with - * {@link workbox-precaching.PrecacheController} - * to both cache and fetch precached assets. - * - * Note: an instance of this class is created automatically when creating a - * `PrecacheController`; it's generally not necessary to create this yourself. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-precaching - */ - class PrecacheStrategy extends Strategy { - /** - * - * @param {Object} [options] - * @param {string} [options.cacheName] Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init} - * of all fetch() requests made by this strategy. - * @param {Object} [options.matchOptions] The - * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to - * get the response from the network if there's a precache miss. - */ - constructor(options = {}) { - options.cacheName = cacheNames.getPrecacheName(options.cacheName); - super(options); - this._fallbackToNetwork = options.fallbackToNetwork === false ? false : true; - // Redirected responses cannot be used to satisfy a navigation request, so - // any redirected response must be "copied" rather than cloned, so the new - // response doesn't contain the `redirected` flag. See: - // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1 - this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin); - } - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - const response = await handler.cacheMatch(request); - if (response) { - return response; - } - // If this is an `install` event for an entry that isn't already cached, - // then populate the cache. - if (handler.event && handler.event.type === 'install') { - return await this._handleInstall(request, handler); - } - // Getting here means something went wrong. An entry that should have been - // precached wasn't found in the cache. - return await this._handleFetch(request, handler); - } - async _handleFetch(request, handler) { - let response; - const params = handler.params || {}; - // Fall back to the network if we're configured to do so. - if (this._fallbackToNetwork) { - { - logger.warn(`The precached response for ` + `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` + `found. Falling back to the network.`); - } - const integrityInManifest = params.integrity; - const integrityInRequest = request.integrity; - const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest; - // Do not add integrity if the original request is no-cors - // See https://github.com/GoogleChrome/workbox/issues/3096 - response = await handler.fetch(new Request(request, { - integrity: request.mode !== 'no-cors' ? integrityInRequest || integrityInManifest : undefined - })); - // It's only "safe" to repair the cache if we're using SRI to guarantee - // that the response matches the precache manifest's expectations, - // and there's either a) no integrity property in the incoming request - // or b) there is an integrity, and it matches the precache manifest. - // See https://github.com/GoogleChrome/workbox/issues/2858 - // Also if the original request users no-cors we don't use integrity. - // See https://github.com/GoogleChrome/workbox/issues/3096 - if (integrityInManifest && noIntegrityConflict && request.mode !== 'no-cors') { - this._useDefaultCacheabilityPluginIfNeeded(); - const wasCached = await handler.cachePut(request, response.clone()); - { - if (wasCached) { - logger.log(`A response for ${getFriendlyURL(request.url)} ` + `was used to "repair" the precache.`); - } - } - } - } else { - // This shouldn't normally happen, but there are edge cases: - // https://github.com/GoogleChrome/workbox/issues/1441 - throw new WorkboxError('missing-precache-entry', { - cacheName: this.cacheName, - url: request.url - }); - } - { - const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read')); - // Workbox is going to handle the route. - // print the routing details to the console. - logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL(request.url)); - logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`); - logger.groupCollapsed(`View request details here.`); - logger.log(request); - logger.groupEnd(); - logger.groupCollapsed(`View response details here.`); - logger.log(response); - logger.groupEnd(); - logger.groupEnd(); - } - return response; - } - async _handleInstall(request, handler) { - this._useDefaultCacheabilityPluginIfNeeded(); - const response = await handler.fetch(request); - // Make sure we defer cachePut() until after we know the response - // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737 - const wasCached = await handler.cachePut(request, response.clone()); - if (!wasCached) { - // Throwing here will lead to the `install` handler failing, which - // we want to do if *any* of the responses aren't safe to cache. - throw new WorkboxError('bad-precaching-response', { - url: request.url, - status: response.status - }); - } - return response; - } - /** - * This method is complex, as there a number of things to account for: - * - * The `plugins` array can be set at construction, and/or it might be added to - * to at any time before the strategy is used. - * - * At the time the strategy is used (i.e. during an `install` event), there - * needs to be at least one plugin that implements `cacheWillUpdate` in the - * array, other than `copyRedirectedCacheableResponsesPlugin`. - * - * - If this method is called and there are no suitable `cacheWillUpdate` - * plugins, we need to add `defaultPrecacheCacheabilityPlugin`. - * - * - If this method is called and there is exactly one `cacheWillUpdate`, then - * we don't have to do anything (this might be a previously added - * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin). - * - * - If this method is called and there is more than one `cacheWillUpdate`, - * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so, - * we need to remove it. (This situation is unlikely, but it could happen if - * the strategy is used multiple times, the first without a `cacheWillUpdate`, - * and then later on after manually adding a custom `cacheWillUpdate`.) - * - * See https://github.com/GoogleChrome/workbox/issues/2737 for more context. - * - * @private - */ - _useDefaultCacheabilityPluginIfNeeded() { - let defaultPluginIndex = null; - let cacheWillUpdatePluginCount = 0; - for (const [index, plugin] of this.plugins.entries()) { - // Ignore the copy redirected plugin when determining what to do. - if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) { - continue; - } - // Save the default plugin's index, in case it needs to be removed. - if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) { - defaultPluginIndex = index; - } - if (plugin.cacheWillUpdate) { - cacheWillUpdatePluginCount++; - } - } - if (cacheWillUpdatePluginCount === 0) { - this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin); - } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) { - // Only remove the default plugin; multiple custom plugins are allowed. - this.plugins.splice(defaultPluginIndex, 1); - } - // Nothing needs to be done if cacheWillUpdatePluginCount is 1 - } - } - PrecacheStrategy.defaultPrecacheCacheabilityPlugin = { - async cacheWillUpdate({ - response - }) { - if (!response || response.status >= 400) { - return null; - } - return response; - } - }; - PrecacheStrategy.copyRedirectedCacheableResponsesPlugin = { - async cacheWillUpdate({ - response - }) { - return response.redirected ? await copyResponse(response) : response; - } - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Performs efficient precaching of assets. - * - * @memberof workbox-precaching - */ - class PrecacheController { - /** - * Create a new PrecacheController. - * - * @param {Object} [options] - * @param {string} [options.cacheName] The cache to use for precaching. - * @param {string} [options.plugins] Plugins to use when precaching as well - * as responding to fetch events for precached assets. - * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to - * get the response from the network if there's a precache miss. - */ - constructor({ - cacheName, - plugins = [], - fallbackToNetwork = true - } = {}) { - this._urlsToCacheKeys = new Map(); - this._urlsToCacheModes = new Map(); - this._cacheKeysToIntegrities = new Map(); - this._strategy = new PrecacheStrategy({ - cacheName: cacheNames.getPrecacheName(cacheName), - plugins: [...plugins, new PrecacheCacheKeyPlugin({ - precacheController: this - })], - fallbackToNetwork - }); - // Bind the install and activate methods to the instance. - this.install = this.install.bind(this); - this.activate = this.activate.bind(this); - } - /** - * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and - * used to cache assets and respond to fetch events. - */ - get strategy() { - return this._strategy; - } - /** - * Adds items to the precache list, removing any duplicates and - * stores the files in the - * {@link workbox-core.cacheNames|"precache cache"} when the service - * worker installs. - * - * This method can be called multiple times. - * - * @param {Array<Object|string>} [entries=[]] Array of entries to precache. - */ - precache(entries) { - this.addToCacheList(entries); - if (!this._installAndActiveListenersAdded) { - self.addEventListener('install', this.install); - self.addEventListener('activate', this.activate); - this._installAndActiveListenersAdded = true; - } - } - /** - * This method will add items to the precache list, removing duplicates - * and ensuring the information is valid. - * - * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries - * Array of entries to precache. - */ - addToCacheList(entries) { - { - finalAssertExports.isArray(entries, { - moduleName: 'workbox-precaching', - className: 'PrecacheController', - funcName: 'addToCacheList', - paramName: 'entries' - }); - } - const urlsToWarnAbout = []; - for (const entry of entries) { - // See https://github.com/GoogleChrome/workbox/issues/2259 - if (typeof entry === 'string') { - urlsToWarnAbout.push(entry); - } else if (entry && entry.revision === undefined) { - urlsToWarnAbout.push(entry.url); - } - const { - cacheKey, - url - } = createCacheKey(entry); - const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default'; - if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) { - throw new WorkboxError('add-to-cache-list-conflicting-entries', { - firstEntry: this._urlsToCacheKeys.get(url), - secondEntry: cacheKey - }); - } - if (typeof entry !== 'string' && entry.integrity) { - if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) { - throw new WorkboxError('add-to-cache-list-conflicting-integrities', { - url - }); - } - this._cacheKeysToIntegrities.set(cacheKey, entry.integrity); - } - this._urlsToCacheKeys.set(url, cacheKey); - this._urlsToCacheModes.set(url, cacheMode); - if (urlsToWarnAbout.length > 0) { - const warningMessage = `Workbox is precaching URLs without revision ` + `info: ${urlsToWarnAbout.join(', ')}\nThis is generally NOT safe. ` + `Learn more at https://bit.ly/wb-precache`; - { - logger.warn(warningMessage); - } - } - } - } - /** - * Precaches new and updated assets. Call this method from the service worker - * install event. - * - * Note: this method calls `event.waitUntil()` for you, so you do not need - * to call it yourself in your event handlers. - * - * @param {ExtendableEvent} event - * @return {Promise<workbox-precaching.InstallResult>} - */ - install(event) { - // waitUntil returns Promise<any> - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return waitUntil(event, async () => { - const installReportPlugin = new PrecacheInstallReportPlugin(); - this.strategy.plugins.push(installReportPlugin); - // Cache entries one at a time. - // See https://github.com/GoogleChrome/workbox/issues/2528 - for (const [url, cacheKey] of this._urlsToCacheKeys) { - const integrity = this._cacheKeysToIntegrities.get(cacheKey); - const cacheMode = this._urlsToCacheModes.get(url); - const request = new Request(url, { - integrity, - cache: cacheMode, - credentials: 'same-origin' - }); - await Promise.all(this.strategy.handleAll({ - params: { - cacheKey - }, - request, - event - })); - } - const { - updatedURLs, - notUpdatedURLs - } = installReportPlugin; - { - printInstallDetails(updatedURLs, notUpdatedURLs); - } - return { - updatedURLs, - notUpdatedURLs - }; - }); - } - /** - * Deletes assets that are no longer present in the current precache manifest. - * Call this method from the service worker activate event. - * - * Note: this method calls `event.waitUntil()` for you, so you do not need - * to call it yourself in your event handlers. - * - * @param {ExtendableEvent} event - * @return {Promise<workbox-precaching.CleanupResult>} - */ - activate(event) { - // waitUntil returns Promise<any> - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return waitUntil(event, async () => { - const cache = await self.caches.open(this.strategy.cacheName); - const currentlyCachedRequests = await cache.keys(); - const expectedCacheKeys = new Set(this._urlsToCacheKeys.values()); - const deletedURLs = []; - for (const request of currentlyCachedRequests) { - if (!expectedCacheKeys.has(request.url)) { - await cache.delete(request); - deletedURLs.push(request.url); - } - } - { - printCleanupDetails(deletedURLs); - } - return { - deletedURLs - }; - }); - } - /** - * Returns a mapping of a precached URL to the corresponding cache key, taking - * into account the revision information for the URL. - * - * @return {Map<string, string>} A URL to cache key mapping. - */ - getURLsToCacheKeys() { - return this._urlsToCacheKeys; - } - /** - * Returns a list of all the URLs that have been precached by the current - * service worker. - * - * @return {Array<string>} The precached URLs. - */ - getCachedURLs() { - return [...this._urlsToCacheKeys.keys()]; - } - /** - * Returns the cache key used for storing a given URL. If that URL is - * unversioned, like `/index.html', then the cache key will be the original - * URL with a search parameter appended to it. - * - * @param {string} url A URL whose cache key you want to look up. - * @return {string} The versioned URL that corresponds to a cache key - * for the original URL, or undefined if that URL isn't precached. - */ - getCacheKeyForURL(url) { - const urlObject = new URL(url, location.href); - return this._urlsToCacheKeys.get(urlObject.href); - } - /** - * @param {string} url A cache key whose SRI you want to look up. - * @return {string} The subresource integrity associated with the cache key, - * or undefined if it's not set. - */ - getIntegrityForCacheKey(cacheKey) { - return this._cacheKeysToIntegrities.get(cacheKey); - } - /** - * This acts as a drop-in replacement for - * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) - * with the following differences: - * - * - It knows what the name of the precache is, and only checks in that cache. - * - It allows you to pass in an "original" URL without versioning parameters, - * and it will automatically look up the correct cache key for the currently - * active revision of that URL. - * - * E.g., `matchPrecache('index.html')` will find the correct precached - * response for the currently active service worker, even if the actual cache - * key is `'/index.html?__WB_REVISION__=1234abcd'`. - * - * @param {string|Request} request The key (without revisioning parameters) - * to look up in the precache. - * @return {Promise<Response|undefined>} - */ - async matchPrecache(request) { - const url = request instanceof Request ? request.url : request; - const cacheKey = this.getCacheKeyForURL(url); - if (cacheKey) { - const cache = await self.caches.open(this.strategy.cacheName); - return cache.match(cacheKey); - } - return undefined; - } - /** - * Returns a function that looks up `url` in the precache (taking into - * account revision information), and returns the corresponding `Response`. - * - * @param {string} url The precached URL which will be used to lookup the - * `Response`. - * @return {workbox-routing~handlerCallback} - */ - createHandlerBoundToURL(url) { - const cacheKey = this.getCacheKeyForURL(url); - if (!cacheKey) { - throw new WorkboxError('non-precached-url', { - url - }); - } - return options => { - options.request = new Request(url); - options.params = Object.assign({ - cacheKey - }, options.params); - return this.strategy.handle(options); - }; - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let precacheController; - /** - * @return {PrecacheController} - * @private - */ - const getOrCreatePrecacheController = () => { - if (!precacheController) { - precacheController = new PrecacheController(); - } - return precacheController; - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Removes any URL search parameters that should be ignored. - * - * @param {URL} urlObject The original URL. - * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against - * each search parameter name. Matches mean that the search parameter should be - * ignored. - * @return {URL} The URL with any ignored search parameters removed. - * - * @private - * @memberof workbox-precaching - */ - function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) { - // Convert the iterable into an array at the start of the loop to make sure - // deletion doesn't mess up iteration. - for (const paramName of [...urlObject.searchParams.keys()]) { - if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) { - urlObject.searchParams.delete(paramName); - } - } - return urlObject; - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Generator function that yields possible variations on the original URL to - * check, one at a time. - * - * @param {string} url - * @param {Object} options - * - * @private - * @memberof workbox-precaching - */ - function* generateURLVariations(url, { - ignoreURLParametersMatching = [/^utm_/, /^fbclid$/], - directoryIndex = 'index.html', - cleanURLs = true, - urlManipulation - } = {}) { - const urlObject = new URL(url, location.href); - urlObject.hash = ''; - yield urlObject.href; - const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching); - yield urlWithoutIgnoredParams.href; - if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) { - const directoryURL = new URL(urlWithoutIgnoredParams.href); - directoryURL.pathname += directoryIndex; - yield directoryURL.href; - } - if (cleanURLs) { - const cleanURL = new URL(urlWithoutIgnoredParams.href); - cleanURL.pathname += '.html'; - yield cleanURL.href; - } - if (urlManipulation) { - const additionalURLs = urlManipulation({ - url: urlObject - }); - for (const urlToAttempt of additionalURLs) { - yield urlToAttempt.href; - } - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A subclass of {@link workbox-routing.Route} that takes a - * {@link workbox-precaching.PrecacheController} - * instance and uses it to match incoming requests and handle fetching - * responses from the precache. - * - * @memberof workbox-precaching - * @extends workbox-routing.Route - */ - class PrecacheRoute extends Route { - /** - * @param {PrecacheController} precacheController A `PrecacheController` - * instance used to both match requests and respond to fetch events. - * @param {Object} [options] Options to control how requests are matched - * against the list of precached URLs. - * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will - * check cache entries for a URLs ending with '/' to see if there is a hit when - * appending the `directoryIndex` value. - * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An - * array of regex's to remove search params when looking for a cache match. - * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will - * check the cache for the URL with a `.html` added to the end of the end. - * @param {workbox-precaching~urlManipulation} [options.urlManipulation] - * This is a function that should take a URL and return an array of - * alternative URLs that should be checked for precache matches. - */ - constructor(precacheController, options) { - const match = ({ - request - }) => { - const urlsToCacheKeys = precacheController.getURLsToCacheKeys(); - for (const possibleURL of generateURLVariations(request.url, options)) { - const cacheKey = urlsToCacheKeys.get(possibleURL); - if (cacheKey) { - const integrity = precacheController.getIntegrityForCacheKey(cacheKey); - return { - cacheKey, - integrity - }; - } - } - { - logger.debug(`Precaching did not find a match for ` + getFriendlyURL(request.url)); - } - return; - }; - super(match, precacheController.strategy); - } - } - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Add a `fetch` listener to the service worker that will - * respond to - * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests} - * with precached assets. - * - * Requests for assets that aren't precached, the `FetchEvent` will not be - * responded to, allowing the event to fall through to other `fetch` event - * listeners. - * - * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute} - * options. - * - * @memberof workbox-precaching - */ - function addRoute(options) { - const precacheController = getOrCreatePrecacheController(); - const precacheRoute = new PrecacheRoute(precacheController, options); - registerRoute(precacheRoute); - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Adds items to the precache list, removing any duplicates and - * stores the files in the - * {@link workbox-core.cacheNames|"precache cache"} when the service - * worker installs. - * - * This method can be called multiple times. - * - * Please note: This method **will not** serve any of the cached files for you. - * It only precaches files. To respond to a network request you call - * {@link workbox-precaching.addRoute}. - * - * If you have a single array of files to precache, you can just call - * {@link workbox-precaching.precacheAndRoute}. - * - * @param {Array<Object|string>} [entries=[]] Array of entries to precache. - * - * @memberof workbox-precaching - */ - function precache(entries) { - const precacheController = getOrCreatePrecacheController(); - precacheController.precache(entries); - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * This method will add entries to the precache list and add a route to - * respond to fetch events. - * - * This is a convenience method that will call - * {@link workbox-precaching.precache} and - * {@link workbox-precaching.addRoute} in a single call. - * - * @param {Array<Object|string>} entries Array of entries to precache. - * @param {Object} [options] See the - * {@link workbox-precaching.PrecacheRoute} options. - * - * @memberof workbox-precaching - */ - function precacheAndRoute(entries, options) { - precache(entries); - addRoute(options); - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const SUBSTRING_TO_FIND = '-precache-'; - /** - * Cleans up incompatible precaches that were created by older versions of - * Workbox, by a service worker registered under the current scope. - * - * This is meant to be called as part of the `activate` event. - * - * This should be safe to use as long as you don't include `substringToFind` - * (defaulting to `-precache-`) in your non-precache cache names. - * - * @param {string} currentPrecacheName The cache name currently in use for - * precaching. This cache won't be deleted. - * @param {string} [substringToFind='-precache-'] Cache names which include this - * substring will be deleted (excluding `currentPrecacheName`). - * @return {Array<string>} A list of all the cache names that were deleted. - * - * @private - * @memberof workbox-precaching - */ - const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => { - const cacheNames = await self.caches.keys(); - const cacheNamesToDelete = cacheNames.filter(cacheName => { - return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName; - }); - await Promise.all(cacheNamesToDelete.map(cacheName => self.caches.delete(cacheName))); - return cacheNamesToDelete; - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Adds an `activate` event listener which will clean up incompatible - * precaches that were created by older versions of Workbox. - * - * @memberof workbox-precaching - */ - function cleanupOutdatedCaches() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('activate', event => { - const cacheName = cacheNames.getPrecacheName(); - event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => { - { - if (cachesDeleted.length > 0) { - logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted); - } - } - })); - }); - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * NavigationRoute makes it easy to create a - * {@link workbox-routing.Route} that matches for browser - * [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}. - * - * It will only match incoming Requests whose - * {@link https://fetch.spec.whatwg.org/#concept-request-mode|mode} - * is set to `navigate`. - * - * You can optionally only apply this route to a subset of navigation requests - * by using one or both of the `denylist` and `allowlist` parameters. - * - * @memberof workbox-routing - * @extends workbox-routing.Route - */ - class NavigationRoute extends Route { - /** - * If both `denylist` and `allowlist` are provided, the `denylist` will - * take precedence and the request will not match this route. - * - * The regular expressions in `allowlist` and `denylist` - * are matched against the concatenated - * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname} - * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search} - * portions of the requested URL. - * - * *Note*: These RegExps may be evaluated against every destination URL during - * a navigation. Avoid using - * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077), - * or else your users may see delays when navigating your site. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {Object} options - * @param {Array<RegExp>} [options.denylist] If any of these patterns match, - * the route will not handle the request (even if a allowlist RegExp matches). - * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns - * match the URL's pathname and search parameter, the route will handle the - * request (assuming the denylist doesn't match). - */ - constructor(handler, { - allowlist = [/./], - denylist = [] - } = {}) { - { - finalAssertExports.isArrayOfClass(allowlist, RegExp, { - moduleName: 'workbox-routing', - className: 'NavigationRoute', - funcName: 'constructor', - paramName: 'options.allowlist' - }); - finalAssertExports.isArrayOfClass(denylist, RegExp, { - moduleName: 'workbox-routing', - className: 'NavigationRoute', - funcName: 'constructor', - paramName: 'options.denylist' - }); - } - super(options => this._match(options), handler); - this._allowlist = allowlist; - this._denylist = denylist; - } - /** - * Routes match handler. - * - * @param {Object} options - * @param {URL} options.url - * @param {Request} options.request - * @return {boolean} - * - * @private - */ - _match({ - url, - request - }) { - if (request && request.mode !== 'navigate') { - return false; - } - const pathnameAndSearch = url.pathname + url.search; - for (const regExp of this._denylist) { - if (regExp.test(pathnameAndSearch)) { - { - logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL matches this denylist pattern: ` + `${regExp.toString()}`); - } - return false; - } - } - if (this._allowlist.some(regExp => regExp.test(pathnameAndSearch))) { - { - logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`); - } - return true; - } - { - logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL being navigated to doesn't ` + `match the allowlist.`); - } - return false; - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Helper function that calls - * {@link PrecacheController#createHandlerBoundToURL} on the default - * {@link PrecacheController} instance. - * - * If you are creating your own {@link PrecacheController}, then call the - * {@link PrecacheController#createHandlerBoundToURL} on that instance, - * instead of using this function. - * - * @param {string} url The precached URL which will be used to lookup the - * `Response`. - * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the - * response from the network if there's a precache miss. - * @return {workbox-routing~handlerCallback} - * - * @memberof workbox-precaching - */ - function createHandlerBoundToURL(url) { - const precacheController = getOrCreatePrecacheController(); - return precacheController.createHandlerBoundToURL(url); - } - - exports.CacheFirst = CacheFirst; - exports.CacheableResponsePlugin = CacheableResponsePlugin; - exports.NavigationRoute = NavigationRoute; - exports.NetworkOnly = NetworkOnly; - exports.cleanupOutdatedCaches = cleanupOutdatedCaches; - exports.createHandlerBoundToURL = createHandlerBoundToURL; - exports.precacheAndRoute = precacheAndRoute; - exports.registerRoute = registerRoute; - -})); -//# sourceMappingURL=workbox-6f774a69.js.map diff --git a/dev-dist/workbox-6f774a69.js.map b/dev-dist/workbox-6f774a69.js.map deleted file mode 100644 index 4eece05..0000000 --- a/dev-dist/workbox-6f774a69.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"workbox-6f774a69.js","sources":["node_modules/workbox-core/_version.js","node_modules/workbox-core/_private/logger.js","node_modules/workbox-core/models/messages/messages.js","node_modules/workbox-core/models/messages/messageGenerator.js","node_modules/workbox-core/_private/WorkboxError.js","node_modules/workbox-core/_private/assert.js","node_modules/workbox-routing/_version.js","node_modules/workbox-routing/utils/constants.js","node_modules/workbox-routing/utils/normalizeHandler.js","node_modules/workbox-routing/Route.js","node_modules/workbox-routing/RegExpRoute.js","node_modules/workbox-core/_private/getFriendlyURL.js","node_modules/workbox-routing/Router.js","node_modules/workbox-routing/utils/getOrCreateDefaultRouter.js","node_modules/workbox-routing/registerRoute.js","node_modules/workbox-cacheable-response/_version.js","node_modules/workbox-cacheable-response/CacheableResponse.js","node_modules/workbox-cacheable-response/CacheableResponsePlugin.js","node_modules/workbox-core/_private/cacheNames.js","node_modules/workbox-core/_private/cacheMatchIgnoreParams.js","node_modules/workbox-core/_private/Deferred.js","node_modules/workbox-core/models/quotaErrorCallbacks.js","node_modules/workbox-core/_private/executeQuotaErrorCallbacks.js","node_modules/workbox-core/_private/timeout.js","node_modules/workbox-strategies/_version.js","node_modules/workbox-strategies/StrategyHandler.js","node_modules/workbox-strategies/Strategy.js","node_modules/workbox-strategies/utils/messages.js","node_modules/workbox-strategies/CacheFirst.js","node_modules/workbox-strategies/NetworkOnly.js","node_modules/workbox-core/_private/waitUntil.js","node_modules/workbox-precaching/_version.js","node_modules/workbox-precaching/utils/createCacheKey.js","node_modules/workbox-precaching/utils/PrecacheInstallReportPlugin.js","node_modules/workbox-precaching/utils/PrecacheCacheKeyPlugin.js","node_modules/workbox-precaching/utils/printCleanupDetails.js","node_modules/workbox-precaching/utils/printInstallDetails.js","node_modules/workbox-core/_private/canConstructResponseFromBodyStream.js","node_modules/workbox-core/copyResponse.js","node_modules/workbox-precaching/PrecacheStrategy.js","node_modules/workbox-precaching/PrecacheController.js","node_modules/workbox-precaching/utils/getOrCreatePrecacheController.js","node_modules/workbox-precaching/utils/removeIgnoredSearchParams.js","node_modules/workbox-precaching/utils/generateURLVariations.js","node_modules/workbox-precaching/PrecacheRoute.js","node_modules/workbox-precaching/addRoute.js","node_modules/workbox-precaching/precache.js","node_modules/workbox-precaching/precacheAndRoute.js","node_modules/workbox-precaching/utils/deleteOutdatedCaches.js","node_modules/workbox-precaching/cleanupOutdatedCaches.js","node_modules/workbox-routing/NavigationRoute.js","node_modules/workbox-precaching/createHandlerBoundToURL.js"],"sourcesContent":["\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:core:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst logger = (process.env.NODE_ENV === 'production'\n ? null\n : (() => {\n // Don't overwrite this value if it's already set.\n // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923\n if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) {\n self.__WB_DISABLE_DEV_LOGS = false;\n }\n let inGroup = false;\n const methodToColorMap = {\n debug: `#7f8c8d`,\n log: `#2ecc71`,\n warn: `#f39c12`,\n error: `#c0392b`,\n groupCollapsed: `#3498db`,\n groupEnd: null, // No colored prefix on groupEnd\n };\n const print = function (method, args) {\n if (self.__WB_DISABLE_DEV_LOGS) {\n return;\n }\n if (method === 'groupCollapsed') {\n // Safari doesn't print all console.groupCollapsed() arguments:\n // https://bugs.webkit.org/show_bug.cgi?id=182754\n if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {\n console[method](...args);\n return;\n }\n }\n const styles = [\n `background: ${methodToColorMap[method]}`,\n `border-radius: 0.5em`,\n `color: white`,\n `font-weight: bold`,\n `padding: 2px 0.5em`,\n ];\n // When in a group, the workbox prefix is not displayed.\n const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];\n console[method](...logPrefix, ...args);\n if (method === 'groupCollapsed') {\n inGroup = true;\n }\n if (method === 'groupEnd') {\n inGroup = false;\n }\n };\n // eslint-disable-next-line @typescript-eslint/ban-types\n const api = {};\n const loggerMethods = Object.keys(methodToColorMap);\n for (const key of loggerMethods) {\n const method = key;\n api[method] = (...args) => {\n print(method, args);\n };\n }\n return api;\n })());\nexport { logger };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../../_version.js';\nexport const messages = {\n 'invalid-value': ({ paramName, validValueDescription, value }) => {\n if (!paramName || !validValueDescription) {\n throw new Error(`Unexpected input to 'invalid-value' error.`);\n }\n return (`The '${paramName}' parameter was given a value with an ` +\n `unexpected value. ${validValueDescription} Received a value of ` +\n `${JSON.stringify(value)}.`);\n },\n 'not-an-array': ({ moduleName, className, funcName, paramName }) => {\n if (!moduleName || !className || !funcName || !paramName) {\n throw new Error(`Unexpected input to 'not-an-array' error.`);\n }\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${className}.${funcName}()' must be an array.`);\n },\n 'incorrect-type': ({ expectedType, paramName, moduleName, className, funcName, }) => {\n if (!expectedType || !paramName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'incorrect-type' error.`);\n }\n const classNameStr = className ? `${className}.` : '';\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${classNameStr}` +\n `${funcName}()' must be of type ${expectedType}.`);\n },\n 'incorrect-class': ({ expectedClassName, paramName, moduleName, className, funcName, isReturnValueProblem, }) => {\n if (!expectedClassName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'incorrect-class' error.`);\n }\n const classNameStr = className ? `${className}.` : '';\n if (isReturnValueProblem) {\n return (`The return value from ` +\n `'${moduleName}.${classNameStr}${funcName}()' ` +\n `must be an instance of class ${expectedClassName}.`);\n }\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${classNameStr}${funcName}()' ` +\n `must be an instance of class ${expectedClassName}.`);\n },\n 'missing-a-method': ({ expectedMethod, paramName, moduleName, className, funcName, }) => {\n if (!expectedMethod ||\n !paramName ||\n !moduleName ||\n !className ||\n !funcName) {\n throw new Error(`Unexpected input to 'missing-a-method' error.`);\n }\n return (`${moduleName}.${className}.${funcName}() expected the ` +\n `'${paramName}' parameter to expose a '${expectedMethod}' method.`);\n },\n 'add-to-cache-list-unexpected-type': ({ entry }) => {\n return (`An unexpected entry was passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` +\n `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` +\n `strings with one or more characters, objects with a url property or ` +\n `Request objects.`);\n },\n 'add-to-cache-list-conflicting-entries': ({ firstEntry, secondEntry }) => {\n if (!firstEntry || !secondEntry) {\n throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`);\n }\n return (`Two of the entries passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` +\n `${firstEntry} but different revision details. Workbox is ` +\n `unable to cache and version the asset correctly. Please remove one ` +\n `of the entries.`);\n },\n 'plugin-error-request-will-fetch': ({ thrownErrorMessage }) => {\n if (!thrownErrorMessage) {\n throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`);\n }\n return (`An error was thrown by a plugins 'requestWillFetch()' method. ` +\n `The thrown error message was: '${thrownErrorMessage}'.`);\n },\n 'invalid-cache-name': ({ cacheNameId, value }) => {\n if (!cacheNameId) {\n throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`);\n }\n return (`You must provide a name containing at least one character for ` +\n `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` +\n `'${JSON.stringify(value)}'`);\n },\n 'unregister-route-but-not-found-with-method': ({ method }) => {\n if (!method) {\n throw new Error(`Unexpected input to ` +\n `'unregister-route-but-not-found-with-method' error.`);\n }\n return (`The route you're trying to unregister was not previously ` +\n `registered for the method type '${method}'.`);\n },\n 'unregister-route-route-not-registered': () => {\n return (`The route you're trying to unregister was not previously ` +\n `registered.`);\n },\n 'queue-replay-failed': ({ name }) => {\n return `Replaying the background sync queue '${name}' failed.`;\n },\n 'duplicate-queue-name': ({ name }) => {\n return (`The Queue name '${name}' is already being used. ` +\n `All instances of backgroundSync.Queue must be given unique names.`);\n },\n 'expired-test-without-max-age': ({ methodName, paramName }) => {\n return (`The '${methodName}()' method can only be used when the ` +\n `'${paramName}' is used in the constructor.`);\n },\n 'unsupported-route-type': ({ moduleName, className, funcName, paramName }) => {\n return (`The supplied '${paramName}' parameter was an unsupported type. ` +\n `Please check the docs for ${moduleName}.${className}.${funcName} for ` +\n `valid input types.`);\n },\n 'not-array-of-class': ({ value, expectedClass, moduleName, className, funcName, paramName, }) => {\n return (`The supplied '${paramName}' parameter must be an array of ` +\n `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` +\n `Please check the call to ${moduleName}.${className}.${funcName}() ` +\n `to fix the issue.`);\n },\n 'max-entries-or-age-required': ({ moduleName, className, funcName }) => {\n return (`You must define either config.maxEntries or config.maxAgeSeconds` +\n `in ${moduleName}.${className}.${funcName}`);\n },\n 'statuses-or-headers-required': ({ moduleName, className, funcName }) => {\n return (`You must define either config.statuses or config.headers` +\n `in ${moduleName}.${className}.${funcName}`);\n },\n 'invalid-string': ({ moduleName, funcName, paramName }) => {\n if (!paramName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'invalid-string' error.`);\n }\n return (`When using strings, the '${paramName}' parameter must start with ` +\n `'http' (for cross-origin matches) or '/' (for same-origin matches). ` +\n `Please see the docs for ${moduleName}.${funcName}() for ` +\n `more info.`);\n },\n 'channel-name-required': () => {\n return (`You must provide a channelName to construct a ` +\n `BroadcastCacheUpdate instance.`);\n },\n 'invalid-responses-are-same-args': () => {\n return (`The arguments passed into responsesAreSame() appear to be ` +\n `invalid. Please ensure valid Responses are used.`);\n },\n 'expire-custom-caches-only': () => {\n return (`You must provide a 'cacheName' property when using the ` +\n `expiration plugin with a runtime caching strategy.`);\n },\n 'unit-must-be-bytes': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`);\n }\n return (`The 'unit' portion of the Range header must be set to 'bytes'. ` +\n `The Range header provided was \"${normalizedRangeHeader}\"`);\n },\n 'single-range-only': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'single-range-only' error.`);\n }\n return (`Multiple ranges are not supported. Please use a single start ` +\n `value, and optional end value. The Range header provided was ` +\n `\"${normalizedRangeHeader}\"`);\n },\n 'invalid-range-values': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'invalid-range-values' error.`);\n }\n return (`The Range header is missing both start and end values. At least ` +\n `one of those values is needed. The Range header provided was ` +\n `\"${normalizedRangeHeader}\"`);\n },\n 'no-range-header': () => {\n return `No Range header was found in the Request provided.`;\n },\n 'range-not-satisfiable': ({ size, start, end }) => {\n return (`The start (${start}) and end (${end}) values in the Range are ` +\n `not satisfiable by the cached response, which is ${size} bytes.`);\n },\n 'attempt-to-cache-non-get-request': ({ url, method }) => {\n return (`Unable to cache '${url}' because it is a '${method}' request and ` +\n `only 'GET' requests can be cached.`);\n },\n 'cache-put-with-no-response': ({ url }) => {\n return (`There was an attempt to cache '${url}' but the response was not ` +\n `defined.`);\n },\n 'no-response': ({ url, error }) => {\n let message = `The strategy could not generate a response for '${url}'.`;\n if (error) {\n message += ` The underlying error is ${error}.`;\n }\n return message;\n },\n 'bad-precaching-response': ({ url, status }) => {\n return (`The precaching request for '${url}' failed` +\n (status ? ` with an HTTP status of ${status}.` : `.`));\n },\n 'non-precached-url': ({ url }) => {\n return (`createHandlerBoundToURL('${url}') was called, but that URL is ` +\n `not precached. Please pass in a URL that is precached instead.`);\n },\n 'add-to-cache-list-conflicting-integrities': ({ url }) => {\n return (`Two of the entries passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` +\n `${url} with different integrity values. Please remove one of them.`);\n },\n 'missing-precache-entry': ({ cacheName, url }) => {\n return `Unable to find a precached response in ${cacheName} for ${url}.`;\n },\n 'cross-origin-copy-response': ({ origin }) => {\n return (`workbox-core.copyResponse() can only be used with same-origin ` +\n `responses. It was passed a response with origin ${origin}.`);\n },\n 'opaque-streams-source': ({ type }) => {\n const message = `One of the workbox-streams sources resulted in an ` +\n `'${type}' response.`;\n if (type === 'opaqueredirect') {\n return (`${message} Please do not use a navigation request that results ` +\n `in a redirect as a source.`);\n }\n return `${message} Please ensure your sources are CORS-enabled.`;\n },\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { messages } from './messages.js';\nimport '../../_version.js';\nconst fallback = (code, ...args) => {\n let msg = code;\n if (args.length > 0) {\n msg += ` :: ${JSON.stringify(args)}`;\n }\n return msg;\n};\nconst generatorFunction = (code, details = {}) => {\n const message = messages[code];\n if (!message) {\n throw new Error(`Unable to find message for code '${code}'.`);\n }\n return message(details);\n};\nexport const messageGenerator = process.env.NODE_ENV === 'production' ? fallback : generatorFunction;\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { messageGenerator } from '../models/messages/messageGenerator.js';\nimport '../_version.js';\n/**\n * Workbox errors should be thrown with this class.\n * This allows use to ensure the type easily in tests,\n * helps developers identify errors from workbox\n * easily and allows use to optimise error\n * messages correctly.\n *\n * @private\n */\nclass WorkboxError extends Error {\n /**\n *\n * @param {string} errorCode The error code that\n * identifies this particular error.\n * @param {Object=} details Any relevant arguments\n * that will help developers identify issues should\n * be added as a key on the context object.\n */\n constructor(errorCode, details) {\n const message = messageGenerator(errorCode, details);\n super(message);\n this.name = errorCode;\n this.details = details;\n }\n}\nexport { WorkboxError };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { WorkboxError } from '../_private/WorkboxError.js';\nimport '../_version.js';\n/*\n * This method throws if the supplied value is not an array.\n * The destructed values are required to produce a meaningful error for users.\n * The destructed and restructured object is so it's clear what is\n * needed.\n */\nconst isArray = (value, details) => {\n if (!Array.isArray(value)) {\n throw new WorkboxError('not-an-array', details);\n }\n};\nconst hasMethod = (object, expectedMethod, details) => {\n const type = typeof object[expectedMethod];\n if (type !== 'function') {\n details['expectedMethod'] = expectedMethod;\n throw new WorkboxError('missing-a-method', details);\n }\n};\nconst isType = (object, expectedType, details) => {\n if (typeof object !== expectedType) {\n details['expectedType'] = expectedType;\n throw new WorkboxError('incorrect-type', details);\n }\n};\nconst isInstance = (object, \n// Need the general type to do the check later.\n// eslint-disable-next-line @typescript-eslint/ban-types\nexpectedClass, details) => {\n if (!(object instanceof expectedClass)) {\n details['expectedClassName'] = expectedClass.name;\n throw new WorkboxError('incorrect-class', details);\n }\n};\nconst isOneOf = (value, validValues, details) => {\n if (!validValues.includes(value)) {\n details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`;\n throw new WorkboxError('invalid-value', details);\n }\n};\nconst isArrayOfClass = (value, \n// Need general type to do check later.\nexpectedClass, // eslint-disable-line\ndetails) => {\n const error = new WorkboxError('not-array-of-class', details);\n if (!Array.isArray(value)) {\n throw error;\n }\n for (const item of value) {\n if (!(item instanceof expectedClass)) {\n throw error;\n }\n }\n};\nconst finalAssertExports = process.env.NODE_ENV === 'production'\n ? null\n : {\n hasMethod,\n isArray,\n isInstance,\n isOneOf,\n isType,\n isArrayOfClass,\n };\nexport { finalAssertExports as assert };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:routing:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * The default HTTP method, 'GET', used when there's no specific method\n * configured for a route.\n *\n * @type {string}\n *\n * @private\n */\nexport const defaultMethod = 'GET';\n/**\n * The list of valid HTTP methods associated with requests that could be routed.\n *\n * @type {Array<string>}\n *\n * @private\n */\nexport const validMethods = [\n 'DELETE',\n 'GET',\n 'HEAD',\n 'PATCH',\n 'POST',\n 'PUT',\n];\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport '../_version.js';\n/**\n * @param {function()|Object} handler Either a function, or an object with a\n * 'handle' method.\n * @return {Object} An object with a handle method.\n *\n * @private\n */\nexport const normalizeHandler = (handler) => {\n if (handler && typeof handler === 'object') {\n if (process.env.NODE_ENV !== 'production') {\n assert.hasMethod(handler, 'handle', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'handler',\n });\n }\n return handler;\n }\n else {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(handler, 'function', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'handler',\n });\n }\n return { handle: handler };\n }\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { defaultMethod, validMethods } from './utils/constants.js';\nimport { normalizeHandler } from './utils/normalizeHandler.js';\nimport './_version.js';\n/**\n * A `Route` consists of a pair of callback functions, \"match\" and \"handler\".\n * The \"match\" callback determine if a route should be used to \"handle\" a\n * request by returning a non-falsy value if it can. The \"handler\" callback\n * is called when there is a match and should return a Promise that resolves\n * to a `Response`.\n *\n * @memberof workbox-routing\n */\nclass Route {\n /**\n * Constructor for Route class.\n *\n * @param {workbox-routing~matchCallback} match\n * A callback function that determines whether the route matches a given\n * `fetch` event by returning a non-falsy value.\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resolving to a Response.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n */\n constructor(match, handler, method = defaultMethod) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(match, 'function', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'match',\n });\n if (method) {\n assert.isOneOf(method, validMethods, { paramName: 'method' });\n }\n }\n // These values are referenced directly by Router so cannot be\n // altered by minificaton.\n this.handler = normalizeHandler(handler);\n this.match = match;\n this.method = method;\n }\n /**\n *\n * @param {workbox-routing-handlerCallback} handler A callback\n * function that returns a Promise resolving to a Response\n */\n setCatchHandler(handler) {\n this.catchHandler = normalizeHandler(handler);\n }\n}\nexport { Route };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { Route } from './Route.js';\nimport './_version.js';\n/**\n * RegExpRoute makes it easy to create a regular expression based\n * {@link workbox-routing.Route}.\n *\n * For same-origin requests the RegExp only needs to match part of the URL. For\n * requests against third-party servers, you must define a RegExp that matches\n * the start of the URL.\n *\n * @memberof workbox-routing\n * @extends workbox-routing.Route\n */\nclass RegExpRoute extends Route {\n /**\n * If the regular expression contains\n * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},\n * the captured values will be passed to the\n * {@link workbox-routing~handlerCallback} `params`\n * argument.\n *\n * @param {RegExp} regExp The regular expression to match against URLs.\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n */\n constructor(regExp, handler, method) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(regExp, RegExp, {\n moduleName: 'workbox-routing',\n className: 'RegExpRoute',\n funcName: 'constructor',\n paramName: 'pattern',\n });\n }\n const match = ({ url }) => {\n const result = regExp.exec(url.href);\n // Return immediately if there's no match.\n if (!result) {\n return;\n }\n // Require that the match start at the first character in the URL string\n // if it's a cross-origin request.\n // See https://github.com/GoogleChrome/workbox/issues/281 for the context\n // behind this behavior.\n if (url.origin !== location.origin && result.index !== 0) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`The regular expression '${regExp.toString()}' only partially matched ` +\n `against the cross-origin URL '${url.toString()}'. RegExpRoute's will only ` +\n `handle cross-origin requests if they match the entire URL.`);\n }\n return;\n }\n // If the route matches, but there aren't any capture groups defined, then\n // this will return [], which is truthy and therefore sufficient to\n // indicate a match.\n // If there are capture groups, then it will return their values.\n return result.slice(1);\n };\n super(match, handler, method);\n }\n}\nexport { RegExpRoute };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst getFriendlyURL = (url) => {\n const urlObj = new URL(String(url), location.href);\n // See https://github.com/GoogleChrome/workbox/issues/2323\n // We want to include everything, except for the origin if it's same-origin.\n return urlObj.href.replace(new RegExp(`^${location.origin}`), '');\n};\nexport { getFriendlyURL };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { defaultMethod } from './utils/constants.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { normalizeHandler } from './utils/normalizeHandler.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport './_version.js';\n/**\n * The Router can be used to process a `FetchEvent` using one or more\n * {@link workbox-routing.Route}, responding with a `Response` if\n * a matching route exists.\n *\n * If no route matches a given a request, the Router will use a \"default\"\n * handler if one is defined.\n *\n * Should the matching Route throw an error, the Router will use a \"catch\"\n * handler if one is defined to gracefully deal with issues and respond with a\n * Request.\n *\n * If a request matches multiple routes, the **earliest** registered route will\n * be used to respond to the request.\n *\n * @memberof workbox-routing\n */\nclass Router {\n /**\n * Initializes a new Router.\n */\n constructor() {\n this._routes = new Map();\n this._defaultHandlerMap = new Map();\n }\n /**\n * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP\n * method name ('GET', etc.) to an array of all the corresponding `Route`\n * instances that are registered.\n */\n get routes() {\n return this._routes;\n }\n /**\n * Adds a fetch event listener to respond to events when a route matches\n * the event's request.\n */\n addFetchListener() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('fetch', ((event) => {\n const { request } = event;\n const responsePromise = this.handleRequest({ request, event });\n if (responsePromise) {\n event.respondWith(responsePromise);\n }\n }));\n }\n /**\n * Adds a message event listener for URLs to cache from the window.\n * This is useful to cache resources loaded on the page prior to when the\n * service worker started controlling it.\n *\n * The format of the message data sent from the window should be as follows.\n * Where the `urlsToCache` array may consist of URL strings or an array of\n * URL string + `requestInit` object (the same as you'd pass to `fetch()`).\n *\n * ```\n * {\n * type: 'CACHE_URLS',\n * payload: {\n * urlsToCache: [\n * './script1.js',\n * './script2.js',\n * ['./script3.js', {mode: 'no-cors'}],\n * ],\n * },\n * }\n * ```\n */\n addCacheListener() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('message', ((event) => {\n // event.data is type 'any'\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (event.data && event.data.type === 'CACHE_URLS') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { payload } = event.data;\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Caching URLs from the window`, payload.urlsToCache);\n }\n const requestPromises = Promise.all(payload.urlsToCache.map((entry) => {\n if (typeof entry === 'string') {\n entry = [entry];\n }\n const request = new Request(...entry);\n return this.handleRequest({ request, event });\n // TODO(philipwalton): TypeScript errors without this typecast for\n // some reason (probably a bug). The real type here should work but\n // doesn't: `Array<Promise<Response> | undefined>`.\n })); // TypeScript\n event.waitUntil(requestPromises);\n // If a MessageChannel was used, reply to the message on success.\n if (event.ports && event.ports[0]) {\n void requestPromises.then(() => event.ports[0].postMessage(true));\n }\n }\n }));\n }\n /**\n * Apply the routing rules to a FetchEvent object to get a Response from an\n * appropriate Route's handler.\n *\n * @param {Object} options\n * @param {Request} options.request The request to handle.\n * @param {ExtendableEvent} options.event The event that triggered the\n * request.\n * @return {Promise<Response>|undefined} A promise is returned if a\n * registered route can handle the request. If there is no matching\n * route and there's no `defaultHandler`, `undefined` is returned.\n */\n handleRequest({ request, event, }) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'handleRequest',\n paramName: 'options.request',\n });\n }\n const url = new URL(request.url, location.href);\n if (!url.protocol.startsWith('http')) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Workbox Router only supports URLs that start with 'http'.`);\n }\n return;\n }\n const sameOrigin = url.origin === location.origin;\n const { params, route } = this.findMatchingRoute({\n event,\n request,\n sameOrigin,\n url,\n });\n let handler = route && route.handler;\n const debugMessages = [];\n if (process.env.NODE_ENV !== 'production') {\n if (handler) {\n debugMessages.push([`Found a route to handle this request:`, route]);\n if (params) {\n debugMessages.push([\n `Passing the following params to the route's handler:`,\n params,\n ]);\n }\n }\n }\n // If we don't have a handler because there was no matching route, then\n // fall back to defaultHandler if that's defined.\n const method = request.method;\n if (!handler && this._defaultHandlerMap.has(method)) {\n if (process.env.NODE_ENV !== 'production') {\n debugMessages.push(`Failed to find a matching route. Falling ` +\n `back to the default handler for ${method}.`);\n }\n handler = this._defaultHandlerMap.get(method);\n }\n if (!handler) {\n if (process.env.NODE_ENV !== 'production') {\n // No handler so Workbox will do nothing. If logs is set of debug\n // i.e. verbose, we should print out this information.\n logger.debug(`No route found for: ${getFriendlyURL(url)}`);\n }\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // We have a handler, meaning Workbox is going to handle the route.\n // print the routing details to the console.\n logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);\n debugMessages.forEach((msg) => {\n if (Array.isArray(msg)) {\n logger.log(...msg);\n }\n else {\n logger.log(msg);\n }\n });\n logger.groupEnd();\n }\n // Wrap in try and catch in case the handle method throws a synchronous\n // error. It should still callback to the catch handler.\n let responsePromise;\n try {\n responsePromise = handler.handle({ url, request, event, params });\n }\n catch (err) {\n responsePromise = Promise.reject(err);\n }\n // Get route's catch handler, if it exists\n const catchHandler = route && route.catchHandler;\n if (responsePromise instanceof Promise &&\n (this._catchHandler || catchHandler)) {\n responsePromise = responsePromise.catch(async (err) => {\n // If there's a route catch handler, process that first\n if (catchHandler) {\n if (process.env.NODE_ENV !== 'production') {\n // Still include URL here as it will be async from the console group\n // and may not make sense without the URL\n logger.groupCollapsed(`Error thrown when responding to: ` +\n ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);\n logger.error(`Error thrown by:`, route);\n logger.error(err);\n logger.groupEnd();\n }\n try {\n return await catchHandler.handle({ url, request, event, params });\n }\n catch (catchErr) {\n if (catchErr instanceof Error) {\n err = catchErr;\n }\n }\n }\n if (this._catchHandler) {\n if (process.env.NODE_ENV !== 'production') {\n // Still include URL here as it will be async from the console group\n // and may not make sense without the URL\n logger.groupCollapsed(`Error thrown when responding to: ` +\n ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);\n logger.error(`Error thrown by:`, route);\n logger.error(err);\n logger.groupEnd();\n }\n return this._catchHandler.handle({ url, request, event });\n }\n throw err;\n });\n }\n return responsePromise;\n }\n /**\n * Checks a request and URL (and optionally an event) against the list of\n * registered routes, and if there's a match, returns the corresponding\n * route along with any params generated by the match.\n *\n * @param {Object} options\n * @param {URL} options.url\n * @param {boolean} options.sameOrigin The result of comparing `url.origin`\n * against the current origin.\n * @param {Request} options.request The request to match.\n * @param {Event} options.event The corresponding event.\n * @return {Object} An object with `route` and `params` properties.\n * They are populated if a matching route was found or `undefined`\n * otherwise.\n */\n findMatchingRoute({ url, sameOrigin, request, event, }) {\n const routes = this._routes.get(request.method) || [];\n for (const route of routes) {\n let params;\n // route.match returns type any, not possible to change right now.\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const matchResult = route.match({ url, sameOrigin, request, event });\n if (matchResult) {\n if (process.env.NODE_ENV !== 'production') {\n // Warn developers that using an async matchCallback is almost always\n // not the right thing to do.\n if (matchResult instanceof Promise) {\n logger.warn(`While routing ${getFriendlyURL(url)}, an async ` +\n `matchCallback function was used. Please convert the ` +\n `following route to use a synchronous matchCallback function:`, route);\n }\n }\n // See https://github.com/GoogleChrome/workbox/issues/2079\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n params = matchResult;\n if (Array.isArray(params) && params.length === 0) {\n // Instead of passing an empty array in as params, use undefined.\n params = undefined;\n }\n else if (matchResult.constructor === Object && // eslint-disable-line\n Object.keys(matchResult).length === 0) {\n // Instead of passing an empty object in as params, use undefined.\n params = undefined;\n }\n else if (typeof matchResult === 'boolean') {\n // For the boolean value true (rather than just something truth-y),\n // don't set params.\n // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353\n params = undefined;\n }\n // Return early if have a match.\n return { route, params };\n }\n }\n // If no match was found above, return and empty object.\n return {};\n }\n /**\n * Define a default `handler` that's called when no routes explicitly\n * match the incoming request.\n *\n * Each HTTP method ('GET', 'POST', etc.) gets its own default handler.\n *\n * Without a default handler, unmatched requests will go against the\n * network as if there were no service worker present.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {string} [method='GET'] The HTTP method to associate with this\n * default handler. Each method has its own default.\n */\n setDefaultHandler(handler, method = defaultMethod) {\n this._defaultHandlerMap.set(method, normalizeHandler(handler));\n }\n /**\n * If a Route throws an error while handling a request, this `handler`\n * will be called and given a chance to provide a response.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n */\n setCatchHandler(handler) {\n this._catchHandler = normalizeHandler(handler);\n }\n /**\n * Registers a route with the router.\n *\n * @param {workbox-routing.Route} route The route to register.\n */\n registerRoute(route) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(route, 'object', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.hasMethod(route, 'match', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.isType(route.handler, 'object', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.hasMethod(route.handler, 'handle', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route.handler',\n });\n assert.isType(route.method, 'string', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route.method',\n });\n }\n if (!this._routes.has(route.method)) {\n this._routes.set(route.method, []);\n }\n // Give precedence to all of the earlier routes by adding this additional\n // route to the end of the array.\n this._routes.get(route.method).push(route);\n }\n /**\n * Unregisters a route with the router.\n *\n * @param {workbox-routing.Route} route The route to unregister.\n */\n unregisterRoute(route) {\n if (!this._routes.has(route.method)) {\n throw new WorkboxError('unregister-route-but-not-found-with-method', {\n method: route.method,\n });\n }\n const routeIndex = this._routes.get(route.method).indexOf(route);\n if (routeIndex > -1) {\n this._routes.get(route.method).splice(routeIndex, 1);\n }\n else {\n throw new WorkboxError('unregister-route-route-not-registered');\n }\n }\n}\nexport { Router };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { Router } from '../Router.js';\nimport '../_version.js';\nlet defaultRouter;\n/**\n * Creates a new, singleton Router instance if one does not exist. If one\n * does already exist, that instance is returned.\n *\n * @private\n * @return {Router}\n */\nexport const getOrCreateDefaultRouter = () => {\n if (!defaultRouter) {\n defaultRouter = new Router();\n // The helpers that use the default Router assume these listeners exist.\n defaultRouter.addFetchListener();\n defaultRouter.addCacheListener();\n }\n return defaultRouter;\n};\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Route } from './Route.js';\nimport { RegExpRoute } from './RegExpRoute.js';\nimport { getOrCreateDefaultRouter } from './utils/getOrCreateDefaultRouter.js';\nimport './_version.js';\n/**\n * Easily register a RegExp, string, or function with a caching\n * strategy to a singleton Router instance.\n *\n * This method will generate a Route for you if needed and\n * call {@link workbox-routing.Router#registerRoute}.\n *\n * @param {RegExp|string|workbox-routing.Route~matchCallback|workbox-routing.Route} capture\n * If the capture param is a `Route`, all other arguments will be ignored.\n * @param {workbox-routing~handlerCallback} [handler] A callback\n * function that returns a Promise resulting in a Response. This parameter\n * is required if `capture` is not a `Route` object.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n * @return {workbox-routing.Route} The generated `Route`.\n *\n * @memberof workbox-routing\n */\nfunction registerRoute(capture, handler, method) {\n let route;\n if (typeof capture === 'string') {\n const captureUrl = new URL(capture, location.href);\n if (process.env.NODE_ENV !== 'production') {\n if (!(capture.startsWith('/') || capture.startsWith('http'))) {\n throw new WorkboxError('invalid-string', {\n moduleName: 'workbox-routing',\n funcName: 'registerRoute',\n paramName: 'capture',\n });\n }\n // We want to check if Express-style wildcards are in the pathname only.\n // TODO: Remove this log message in v4.\n const valueToCheck = capture.startsWith('http')\n ? captureUrl.pathname\n : capture;\n // See https://github.com/pillarjs/path-to-regexp#parameters\n const wildcards = '[*:?+]';\n if (new RegExp(`${wildcards}`).exec(valueToCheck)) {\n logger.debug(`The '$capture' parameter contains an Express-style wildcard ` +\n `character (${wildcards}). Strings are now always interpreted as ` +\n `exact matches; use a RegExp for partial or wildcard matches.`);\n }\n }\n const matchCallback = ({ url }) => {\n if (process.env.NODE_ENV !== 'production') {\n if (url.pathname === captureUrl.pathname &&\n url.origin !== captureUrl.origin) {\n logger.debug(`${capture} only partially matches the cross-origin URL ` +\n `${url.toString()}. This route will only handle cross-origin requests ` +\n `if they match the entire URL.`);\n }\n }\n return url.href === captureUrl.href;\n };\n // If `capture` is a string then `handler` and `method` must be present.\n route = new Route(matchCallback, handler, method);\n }\n else if (capture instanceof RegExp) {\n // If `capture` is a `RegExp` then `handler` and `method` must be present.\n route = new RegExpRoute(capture, handler, method);\n }\n else if (typeof capture === 'function') {\n // If `capture` is a function then `handler` and `method` must be present.\n route = new Route(capture, handler, method);\n }\n else if (capture instanceof Route) {\n route = capture;\n }\n else {\n throw new WorkboxError('unsupported-route-type', {\n moduleName: 'workbox-routing',\n funcName: 'registerRoute',\n paramName: 'capture',\n });\n }\n const defaultRouter = getOrCreateDefaultRouter();\n defaultRouter.registerRoute(route);\n return route;\n}\nexport { registerRoute };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:cacheable-response:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport './_version.js';\n/**\n * This class allows you to set up rules determining what\n * status codes and/or headers need to be present in order for a\n * [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)\n * to be considered cacheable.\n *\n * @memberof workbox-cacheable-response\n */\nclass CacheableResponse {\n /**\n * To construct a new CacheableResponse instance you must provide at least\n * one of the `config` properties.\n *\n * If both `statuses` and `headers` are specified, then both conditions must\n * be met for the `Response` to be considered cacheable.\n *\n * @param {Object} config\n * @param {Array<number>} [config.statuses] One or more status codes that a\n * `Response` can have and be considered cacheable.\n * @param {Object<string,string>} [config.headers] A mapping of header names\n * and expected values that a `Response` can have and be considered cacheable.\n * If multiple headers are provided, only one needs to be present.\n */\n constructor(config = {}) {\n if (process.env.NODE_ENV !== 'production') {\n if (!(config.statuses || config.headers)) {\n throw new WorkboxError('statuses-or-headers-required', {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n });\n }\n if (config.statuses) {\n assert.isArray(config.statuses, {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n paramName: 'config.statuses',\n });\n }\n if (config.headers) {\n assert.isType(config.headers, 'object', {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n paramName: 'config.headers',\n });\n }\n }\n this._statuses = config.statuses;\n this._headers = config.headers;\n }\n /**\n * Checks a response to see whether it's cacheable or not, based on this\n * object's configuration.\n *\n * @param {Response} response The response whose cacheability is being\n * checked.\n * @return {boolean} `true` if the `Response` is cacheable, and `false`\n * otherwise.\n */\n isResponseCacheable(response) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(response, Response, {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'isResponseCacheable',\n paramName: 'response',\n });\n }\n let cacheable = true;\n if (this._statuses) {\n cacheable = this._statuses.includes(response.status);\n }\n if (this._headers && cacheable) {\n cacheable = Object.keys(this._headers).some((headerName) => {\n return response.headers.get(headerName) === this._headers[headerName];\n });\n }\n if (process.env.NODE_ENV !== 'production') {\n if (!cacheable) {\n logger.groupCollapsed(`The request for ` +\n `'${getFriendlyURL(response.url)}' returned a response that does ` +\n `not meet the criteria for being cached.`);\n logger.groupCollapsed(`View cacheability criteria here.`);\n logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses));\n logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2));\n logger.groupEnd();\n const logFriendlyHeaders = {};\n response.headers.forEach((value, key) => {\n logFriendlyHeaders[key] = value;\n });\n logger.groupCollapsed(`View response status and headers here.`);\n logger.log(`Response status: ${response.status}`);\n logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2));\n logger.groupEnd();\n logger.groupCollapsed(`View full response details here.`);\n logger.log(response.headers);\n logger.log(response);\n logger.groupEnd();\n logger.groupEnd();\n }\n }\n return cacheable;\n }\n}\nexport { CacheableResponse };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { CacheableResponse, } from './CacheableResponse.js';\nimport './_version.js';\n/**\n * A class implementing the `cacheWillUpdate` lifecycle callback. This makes it\n * easier to add in cacheability checks to requests made via Workbox's built-in\n * strategies.\n *\n * @memberof workbox-cacheable-response\n */\nclass CacheableResponsePlugin {\n /**\n * To construct a new CacheableResponsePlugin instance you must provide at\n * least one of the `config` properties.\n *\n * If both `statuses` and `headers` are specified, then both conditions must\n * be met for the `Response` to be considered cacheable.\n *\n * @param {Object} config\n * @param {Array<number>} [config.statuses] One or more status codes that a\n * `Response` can have and be considered cacheable.\n * @param {Object<string,string>} [config.headers] A mapping of header names\n * and expected values that a `Response` can have and be considered cacheable.\n * If multiple headers are provided, only one needs to be present.\n */\n constructor(config) {\n /**\n * @param {Object} options\n * @param {Response} options.response\n * @return {Response|null}\n * @private\n */\n this.cacheWillUpdate = async ({ response }) => {\n if (this._cacheableResponse.isResponseCacheable(response)) {\n return response;\n }\n return null;\n };\n this._cacheableResponse = new CacheableResponse(config);\n }\n}\nexport { CacheableResponsePlugin };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst _cacheNameDetails = {\n googleAnalytics: 'googleAnalytics',\n precache: 'precache-v2',\n prefix: 'workbox',\n runtime: 'runtime',\n suffix: typeof registration !== 'undefined' ? registration.scope : '',\n};\nconst _createCacheName = (cacheName) => {\n return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix]\n .filter((value) => value && value.length > 0)\n .join('-');\n};\nconst eachCacheNameDetail = (fn) => {\n for (const key of Object.keys(_cacheNameDetails)) {\n fn(key);\n }\n};\nexport const cacheNames = {\n updateDetails: (details) => {\n eachCacheNameDetail((key) => {\n if (typeof details[key] === 'string') {\n _cacheNameDetails[key] = details[key];\n }\n });\n },\n getGoogleAnalyticsName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);\n },\n getPrecacheName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.precache);\n },\n getPrefix: () => {\n return _cacheNameDetails.prefix;\n },\n getRuntimeName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.runtime);\n },\n getSuffix: () => {\n return _cacheNameDetails.suffix;\n },\n};\n","/*\n Copyright 2020 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nfunction stripParams(fullURL, ignoreParams) {\n const strippedURL = new URL(fullURL);\n for (const param of ignoreParams) {\n strippedURL.searchParams.delete(param);\n }\n return strippedURL.href;\n}\n/**\n * Matches an item in the cache, ignoring specific URL params. This is similar\n * to the `ignoreSearch` option, but it allows you to ignore just specific\n * params (while continuing to match on the others).\n *\n * @private\n * @param {Cache} cache\n * @param {Request} request\n * @param {Object} matchOptions\n * @param {Array<string>} ignoreParams\n * @return {Promise<Response|undefined>}\n */\nasync function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) {\n const strippedRequestURL = stripParams(request.url, ignoreParams);\n // If the request doesn't include any ignored params, match as normal.\n if (request.url === strippedRequestURL) {\n return cache.match(request, matchOptions);\n }\n // Otherwise, match by comparing keys\n const keysOptions = Object.assign(Object.assign({}, matchOptions), { ignoreSearch: true });\n const cacheKeys = await cache.keys(request, keysOptions);\n for (const cacheKey of cacheKeys) {\n const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams);\n if (strippedRequestURL === strippedCacheKeyURL) {\n return cache.match(cacheKey, matchOptions);\n }\n }\n return;\n}\nexport { cacheMatchIgnoreParams };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * The Deferred class composes Promises in a way that allows for them to be\n * resolved or rejected from outside the constructor. In most cases promises\n * should be used directly, but Deferreds can be necessary when the logic to\n * resolve a promise must be separate.\n *\n * @private\n */\nclass Deferred {\n /**\n * Creates a promise and exposes its resolve and reject functions as methods.\n */\n constructor() {\n this.promise = new Promise((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\nexport { Deferred };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n// Callbacks to be executed whenever there's a quota error.\n// Can't change Function type right now.\n// eslint-disable-next-line @typescript-eslint/ban-types\nconst quotaErrorCallbacks = new Set();\nexport { quotaErrorCallbacks };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from '../_private/logger.js';\nimport { quotaErrorCallbacks } from '../models/quotaErrorCallbacks.js';\nimport '../_version.js';\n/**\n * Runs all of the callback functions, one at a time sequentially, in the order\n * in which they were registered.\n *\n * @memberof workbox-core\n * @private\n */\nasync function executeQuotaErrorCallbacks() {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`About to run ${quotaErrorCallbacks.size} ` +\n `callbacks to clean up caches.`);\n }\n for (const callback of quotaErrorCallbacks) {\n await callback();\n if (process.env.NODE_ENV !== 'production') {\n logger.log(callback, 'is complete.');\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.log('Finished running callbacks.');\n }\n}\nexport { executeQuotaErrorCallbacks };\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * Returns a promise that resolves and the passed number of milliseconds.\n * This utility is an async/await-friendly version of `setTimeout`.\n *\n * @param {number} ms\n * @return {Promise}\n * @private\n */\nexport function timeout(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:strategies:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { cacheMatchIgnoreParams } from 'workbox-core/_private/cacheMatchIgnoreParams.js';\nimport { Deferred } from 'workbox-core/_private/Deferred.js';\nimport { executeQuotaErrorCallbacks } from 'workbox-core/_private/executeQuotaErrorCallbacks.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { timeout } from 'workbox-core/_private/timeout.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport './_version.js';\nfunction toRequest(input) {\n return typeof input === 'string' ? new Request(input) : input;\n}\n/**\n * A class created every time a Strategy instance instance calls\n * {@link workbox-strategies.Strategy~handle} or\n * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and\n * cache actions around plugin callbacks and keeps track of when the strategy\n * is \"done\" (i.e. all added `event.waitUntil()` promises have resolved).\n *\n * @memberof workbox-strategies\n */\nclass StrategyHandler {\n /**\n * Creates a new instance associated with the passed strategy and event\n * that's handling the request.\n *\n * The constructor also initializes the state that will be passed to each of\n * the plugins handling this request.\n *\n * @param {workbox-strategies.Strategy} strategy\n * @param {Object} options\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params] The return value from the\n * {@link workbox-routing~matchCallback} (if applicable).\n */\n constructor(strategy, options) {\n this._cacheKeys = {};\n /**\n * The request the strategy is performing (passed to the strategy's\n * `handle()` or `handleAll()` method).\n * @name request\n * @instance\n * @type {Request}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * The event associated with this request.\n * @name event\n * @instance\n * @type {ExtendableEvent}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * A `URL` instance of `request.url` (if passed to the strategy's\n * `handle()` or `handleAll()` method).\n * Note: the `url` param will be present if the strategy was invoked\n * from a workbox `Route` object.\n * @name url\n * @instance\n * @type {URL|undefined}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * A `param` value (if passed to the strategy's\n * `handle()` or `handleAll()` method).\n * Note: the `param` param will be present if the strategy was invoked\n * from a workbox `Route` object and the\n * {@link workbox-routing~matchCallback} returned\n * a truthy value (it will be that value).\n * @name params\n * @instance\n * @type {*|undefined}\n * @memberof workbox-strategies.StrategyHandler\n */\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(options.event, ExtendableEvent, {\n moduleName: 'workbox-strategies',\n className: 'StrategyHandler',\n funcName: 'constructor',\n paramName: 'options.event',\n });\n }\n Object.assign(this, options);\n this.event = options.event;\n this._strategy = strategy;\n this._handlerDeferred = new Deferred();\n this._extendLifetimePromises = [];\n // Copy the plugins list (since it's mutable on the strategy),\n // so any mutations don't affect this handler instance.\n this._plugins = [...strategy.plugins];\n this._pluginStateMap = new Map();\n for (const plugin of this._plugins) {\n this._pluginStateMap.set(plugin, {});\n }\n this.event.waitUntil(this._handlerDeferred.promise);\n }\n /**\n * Fetches a given request (and invokes any applicable plugin callback\n * methods) using the `fetchOptions` (for non-navigation requests) and\n * `plugins` defined on the `Strategy` object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - `requestWillFetch()`\n * - `fetchDidSucceed()`\n * - `fetchDidFail()`\n *\n * @param {Request|string} input The URL or request to fetch.\n * @return {Promise<Response>}\n */\n async fetch(input) {\n const { event } = this;\n let request = toRequest(input);\n if (request.mode === 'navigate' &&\n event instanceof FetchEvent &&\n event.preloadResponse) {\n const possiblePreloadResponse = (await event.preloadResponse);\n if (possiblePreloadResponse) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Using a preloaded navigation response for ` +\n `'${getFriendlyURL(request.url)}'`);\n }\n return possiblePreloadResponse;\n }\n }\n // If there is a fetchDidFail plugin, we need to save a clone of the\n // original request before it's either modified by a requestWillFetch\n // plugin or before the original request's body is consumed via fetch().\n const originalRequest = this.hasCallback('fetchDidFail')\n ? request.clone()\n : null;\n try {\n for (const cb of this.iterateCallbacks('requestWillFetch')) {\n request = await cb({ request: request.clone(), event });\n }\n }\n catch (err) {\n if (err instanceof Error) {\n throw new WorkboxError('plugin-error-request-will-fetch', {\n thrownErrorMessage: err.message,\n });\n }\n }\n // The request can be altered by plugins with `requestWillFetch` making\n // the original request (most likely from a `fetch` event) different\n // from the Request we make. Pass both to `fetchDidFail` to aid debugging.\n const pluginFilteredRequest = request.clone();\n try {\n let fetchResponse;\n // See https://github.com/GoogleChrome/workbox/issues/1796\n fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions);\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Network request for ` +\n `'${getFriendlyURL(request.url)}' returned a response with ` +\n `status '${fetchResponse.status}'.`);\n }\n for (const callback of this.iterateCallbacks('fetchDidSucceed')) {\n fetchResponse = await callback({\n event,\n request: pluginFilteredRequest,\n response: fetchResponse,\n });\n }\n return fetchResponse;\n }\n catch (error) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Network request for ` +\n `'${getFriendlyURL(request.url)}' threw an error.`, error);\n }\n // `originalRequest` will only exist if a `fetchDidFail` callback\n // is being used (see above).\n if (originalRequest) {\n await this.runCallbacks('fetchDidFail', {\n error: error,\n event,\n originalRequest: originalRequest.clone(),\n request: pluginFilteredRequest.clone(),\n });\n }\n throw error;\n }\n }\n /**\n * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on\n * the response generated by `this.fetch()`.\n *\n * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,\n * so you do not have to manually call `waitUntil()` on the event.\n *\n * @param {Request|string} input The request or URL to fetch and cache.\n * @return {Promise<Response>}\n */\n async fetchAndCachePut(input) {\n const response = await this.fetch(input);\n const responseClone = response.clone();\n void this.waitUntil(this.cachePut(input, responseClone));\n return response;\n }\n /**\n * Matches a request from the cache (and invokes any applicable plugin\n * callback methods) using the `cacheName`, `matchOptions`, and `plugins`\n * defined on the strategy object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - cacheKeyWillBeUsed()\n * - cachedResponseWillBeUsed()\n *\n * @param {Request|string} key The Request or URL to use as the cache key.\n * @return {Promise<Response|undefined>} A matching response, if found.\n */\n async cacheMatch(key) {\n const request = toRequest(key);\n let cachedResponse;\n const { cacheName, matchOptions } = this._strategy;\n const effectiveRequest = await this.getCacheKey(request, 'read');\n const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), { cacheName });\n cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);\n if (process.env.NODE_ENV !== 'production') {\n if (cachedResponse) {\n logger.debug(`Found a cached response in '${cacheName}'.`);\n }\n else {\n logger.debug(`No cached response found in '${cacheName}'.`);\n }\n }\n for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {\n cachedResponse =\n (await callback({\n cacheName,\n matchOptions,\n cachedResponse,\n request: effectiveRequest,\n event: this.event,\n })) || undefined;\n }\n return cachedResponse;\n }\n /**\n * Puts a request/response pair in the cache (and invokes any applicable\n * plugin callback methods) using the `cacheName` and `plugins` defined on\n * the strategy object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - cacheKeyWillBeUsed()\n * - cacheWillUpdate()\n * - cacheDidUpdate()\n *\n * @param {Request|string} key The request or URL to use as the cache key.\n * @param {Response} response The response to cache.\n * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response\n * not be cached, and `true` otherwise.\n */\n async cachePut(key, response) {\n const request = toRequest(key);\n // Run in the next task to avoid blocking other cache reads.\n // https://github.com/w3c/ServiceWorker/issues/1397\n await timeout(0);\n const effectiveRequest = await this.getCacheKey(request, 'write');\n if (process.env.NODE_ENV !== 'production') {\n if (effectiveRequest.method && effectiveRequest.method !== 'GET') {\n throw new WorkboxError('attempt-to-cache-non-get-request', {\n url: getFriendlyURL(effectiveRequest.url),\n method: effectiveRequest.method,\n });\n }\n // See https://github.com/GoogleChrome/workbox/issues/2818\n const vary = response.headers.get('Vary');\n if (vary) {\n logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` +\n `has a 'Vary: ${vary}' header. ` +\n `Consider setting the {ignoreVary: true} option on your strategy ` +\n `to ensure cache matching and deletion works as expected.`);\n }\n }\n if (!response) {\n if (process.env.NODE_ENV !== 'production') {\n logger.error(`Cannot cache non-existent response for ` +\n `'${getFriendlyURL(effectiveRequest.url)}'.`);\n }\n throw new WorkboxError('cache-put-with-no-response', {\n url: getFriendlyURL(effectiveRequest.url),\n });\n }\n const responseToCache = await this._ensureResponseSafeToCache(response);\n if (!responseToCache) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` +\n `will not be cached.`, responseToCache);\n }\n return false;\n }\n const { cacheName, matchOptions } = this._strategy;\n const cache = await self.caches.open(cacheName);\n const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');\n const oldResponse = hasCacheUpdateCallback\n ? await cacheMatchIgnoreParams(\n // TODO(philipwalton): the `__WB_REVISION__` param is a precaching\n // feature. Consider into ways to only add this behavior if using\n // precaching.\n cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions)\n : null;\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Updating the '${cacheName}' cache with a new Response ` +\n `for ${getFriendlyURL(effectiveRequest.url)}.`);\n }\n try {\n await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);\n }\n catch (error) {\n if (error instanceof Error) {\n // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError\n if (error.name === 'QuotaExceededError') {\n await executeQuotaErrorCallbacks();\n }\n throw error;\n }\n }\n for (const callback of this.iterateCallbacks('cacheDidUpdate')) {\n await callback({\n cacheName,\n oldResponse,\n newResponse: responseToCache.clone(),\n request: effectiveRequest,\n event: this.event,\n });\n }\n return true;\n }\n /**\n * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and\n * executes any of those callbacks found in sequence. The final `Request`\n * object returned by the last plugin is treated as the cache key for cache\n * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have\n * been registered, the passed request is returned unmodified\n *\n * @param {Request} request\n * @param {string} mode\n * @return {Promise<Request>}\n */\n async getCacheKey(request, mode) {\n const key = `${request.url} | ${mode}`;\n if (!this._cacheKeys[key]) {\n let effectiveRequest = request;\n for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {\n effectiveRequest = toRequest(await callback({\n mode,\n request: effectiveRequest,\n event: this.event,\n // params has a type any can't change right now.\n params: this.params, // eslint-disable-line\n }));\n }\n this._cacheKeys[key] = effectiveRequest;\n }\n return this._cacheKeys[key];\n }\n /**\n * Returns true if the strategy has at least one plugin with the given\n * callback.\n *\n * @param {string} name The name of the callback to check for.\n * @return {boolean}\n */\n hasCallback(name) {\n for (const plugin of this._strategy.plugins) {\n if (name in plugin) {\n return true;\n }\n }\n return false;\n }\n /**\n * Runs all plugin callbacks matching the given name, in order, passing the\n * given param object (merged ith the current plugin state) as the only\n * argument.\n *\n * Note: since this method runs all plugins, it's not suitable for cases\n * where the return value of a callback needs to be applied prior to calling\n * the next callback. See\n * {@link workbox-strategies.StrategyHandler#iterateCallbacks}\n * below for how to handle that case.\n *\n * @param {string} name The name of the callback to run within each plugin.\n * @param {Object} param The object to pass as the first (and only) param\n * when executing each callback. This object will be merged with the\n * current plugin state prior to callback execution.\n */\n async runCallbacks(name, param) {\n for (const callback of this.iterateCallbacks(name)) {\n // TODO(philipwalton): not sure why `any` is needed. It seems like\n // this should work with `as WorkboxPluginCallbackParam[C]`.\n await callback(param);\n }\n }\n /**\n * Accepts a callback and returns an iterable of matching plugin callbacks,\n * where each callback is wrapped with the current handler state (i.e. when\n * you call each callback, whatever object parameter you pass it will\n * be merged with the plugin's current state).\n *\n * @param {string} name The name fo the callback to run\n * @return {Array<Function>}\n */\n *iterateCallbacks(name) {\n for (const plugin of this._strategy.plugins) {\n if (typeof plugin[name] === 'function') {\n const state = this._pluginStateMap.get(plugin);\n const statefulCallback = (param) => {\n const statefulParam = Object.assign(Object.assign({}, param), { state });\n // TODO(philipwalton): not sure why `any` is needed. It seems like\n // this should work with `as WorkboxPluginCallbackParam[C]`.\n return plugin[name](statefulParam);\n };\n yield statefulCallback;\n }\n }\n }\n /**\n * Adds a promise to the\n * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}\n * of the event event associated with the request being handled (usually a\n * `FetchEvent`).\n *\n * Note: you can await\n * {@link workbox-strategies.StrategyHandler~doneWaiting}\n * to know when all added promises have settled.\n *\n * @param {Promise} promise A promise to add to the extend lifetime promises\n * of the event that triggered the request.\n */\n waitUntil(promise) {\n this._extendLifetimePromises.push(promise);\n return promise;\n }\n /**\n * Returns a promise that resolves once all promises passed to\n * {@link workbox-strategies.StrategyHandler~waitUntil}\n * have settled.\n *\n * Note: any work done after `doneWaiting()` settles should be manually\n * passed to an event's `waitUntil()` method (not this handler's\n * `waitUntil()` method), otherwise the service worker thread my be killed\n * prior to your work completing.\n */\n async doneWaiting() {\n let promise;\n while ((promise = this._extendLifetimePromises.shift())) {\n await promise;\n }\n }\n /**\n * Stops running the strategy and immediately resolves any pending\n * `waitUntil()` promises.\n */\n destroy() {\n this._handlerDeferred.resolve(null);\n }\n /**\n * This method will call cacheWillUpdate on the available plugins (or use\n * status === 200) to determine if the Response is safe and valid to cache.\n *\n * @param {Request} options.request\n * @param {Response} options.response\n * @return {Promise<Response|undefined>}\n *\n * @private\n */\n async _ensureResponseSafeToCache(response) {\n let responseToCache = response;\n let pluginsUsed = false;\n for (const callback of this.iterateCallbacks('cacheWillUpdate')) {\n responseToCache =\n (await callback({\n request: this.request,\n response: responseToCache,\n event: this.event,\n })) || undefined;\n pluginsUsed = true;\n if (!responseToCache) {\n break;\n }\n }\n if (!pluginsUsed) {\n if (responseToCache && responseToCache.status !== 200) {\n responseToCache = undefined;\n }\n if (process.env.NODE_ENV !== 'production') {\n if (responseToCache) {\n if (responseToCache.status !== 200) {\n if (responseToCache.status === 0) {\n logger.warn(`The response for '${this.request.url}' ` +\n `is an opaque response. The caching strategy that you're ` +\n `using will not cache opaque responses by default.`);\n }\n else {\n logger.debug(`The response for '${this.request.url}' ` +\n `returned a status code of '${response.status}' and won't ` +\n `be cached as a result.`);\n }\n }\n }\n }\n }\n return responseToCache;\n }\n}\nexport { StrategyHandler };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { StrategyHandler } from './StrategyHandler.js';\nimport './_version.js';\n/**\n * An abstract base class that all other strategy classes must extend from:\n *\n * @memberof workbox-strategies\n */\nclass Strategy {\n /**\n * Creates a new instance of the strategy and sets all documented option\n * properties as public instance properties.\n *\n * Note: if a custom strategy class extends the base Strategy class and does\n * not need more than these properties, it does not need to define its own\n * constructor.\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {Object} [options.matchOptions] The\n * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n */\n constructor(options = {}) {\n /**\n * Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n *\n * @type {string}\n */\n this.cacheName = cacheNames.getRuntimeName(options.cacheName);\n /**\n * The list\n * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * used by this strategy.\n *\n * @type {Array<Object>}\n */\n this.plugins = options.plugins || [];\n /**\n * Values passed along to the\n * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}\n * of all fetch() requests made by this strategy.\n *\n * @type {Object}\n */\n this.fetchOptions = options.fetchOptions;\n /**\n * The\n * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n *\n * @type {Object}\n */\n this.matchOptions = options.matchOptions;\n }\n /**\n * Perform a request strategy and returns a `Promise` that will resolve with\n * a `Response`, invoking all relevant plugin callbacks.\n *\n * When a strategy instance is registered with a Workbox\n * {@link workbox-routing.Route}, this method is automatically\n * called when the route matches.\n *\n * Alternatively, this method can be used in a standalone `FetchEvent`\n * listener by passing it to `event.respondWith()`.\n *\n * @param {FetchEvent|Object} options A `FetchEvent` or an object with the\n * properties listed below.\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n */\n handle(options) {\n const [responseDone] = this.handleAll(options);\n return responseDone;\n }\n /**\n * Similar to {@link workbox-strategies.Strategy~handle}, but\n * instead of just returning a `Promise` that resolves to a `Response` it\n * it will return an tuple of `[response, done]` promises, where the former\n * (`response`) is equivalent to what `handle()` returns, and the latter is a\n * Promise that will resolve once any promises that were added to\n * `event.waitUntil()` as part of performing the strategy have completed.\n *\n * You can await the `done` promise to ensure any extra work performed by\n * the strategy (usually caching responses) completes successfully.\n *\n * @param {FetchEvent|Object} options A `FetchEvent` or an object with the\n * properties listed below.\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n * @return {Array<Promise>} A tuple of [response, done]\n * promises that can be used to determine when the response resolves as\n * well as when the handler has completed all its work.\n */\n handleAll(options) {\n // Allow for flexible options to be passed.\n if (options instanceof FetchEvent) {\n options = {\n event: options,\n request: options.request,\n };\n }\n const event = options.event;\n const request = typeof options.request === 'string'\n ? new Request(options.request)\n : options.request;\n const params = 'params' in options ? options.params : undefined;\n const handler = new StrategyHandler(this, { event, request, params });\n const responseDone = this._getResponse(handler, request, event);\n const handlerDone = this._awaitComplete(responseDone, handler, request, event);\n // Return an array of promises, suitable for use with Promise.all().\n return [responseDone, handlerDone];\n }\n async _getResponse(handler, request, event) {\n await handler.runCallbacks('handlerWillStart', { event, request });\n let response = undefined;\n try {\n response = await this._handle(request, handler);\n // The \"official\" Strategy subclasses all throw this error automatically,\n // but in case a third-party Strategy doesn't, ensure that we have a\n // consistent failure when there's no response or an error response.\n if (!response || response.type === 'error') {\n throw new WorkboxError('no-response', { url: request.url });\n }\n }\n catch (error) {\n if (error instanceof Error) {\n for (const callback of handler.iterateCallbacks('handlerDidError')) {\n response = await callback({ error, event, request });\n if (response) {\n break;\n }\n }\n }\n if (!response) {\n throw error;\n }\n else if (process.env.NODE_ENV !== 'production') {\n logger.log(`While responding to '${getFriendlyURL(request.url)}', ` +\n `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` +\n `a handlerDidError plugin.`);\n }\n }\n for (const callback of handler.iterateCallbacks('handlerWillRespond')) {\n response = await callback({ event, request, response });\n }\n return response;\n }\n async _awaitComplete(responseDone, handler, request, event) {\n let response;\n let error;\n try {\n response = await responseDone;\n }\n catch (error) {\n // Ignore errors, as response errors should be caught via the `response`\n // promise above. The `done` promise will only throw for errors in\n // promises passed to `handler.waitUntil()`.\n }\n try {\n await handler.runCallbacks('handlerDidRespond', {\n event,\n request,\n response,\n });\n await handler.doneWaiting();\n }\n catch (waitUntilError) {\n if (waitUntilError instanceof Error) {\n error = waitUntilError;\n }\n }\n await handler.runCallbacks('handlerDidComplete', {\n event,\n request,\n response,\n error: error,\n });\n handler.destroy();\n if (error) {\n throw error;\n }\n }\n}\nexport { Strategy };\n/**\n * Classes extending the `Strategy` based class should implement this method,\n * and leverage the {@link workbox-strategies.StrategyHandler}\n * arg to perform all fetching and cache logic, which will ensure all relevant\n * cache, cache options, fetch options and plugins are used (per the current\n * strategy instance).\n *\n * @name _handle\n * @instance\n * @abstract\n * @function\n * @param {Request} request\n * @param {workbox-strategies.StrategyHandler} handler\n * @return {Promise<Response>}\n *\n * @memberof workbox-strategies.Strategy\n */\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport '../_version.js';\nexport const messages = {\n strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,\n printFinalResponse: (response) => {\n if (response) {\n logger.groupCollapsed(`View the final response here.`);\n logger.log(response || '[No response returned]');\n logger.groupEnd();\n }\n },\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from './Strategy.js';\nimport { messages } from './utils/messages.js';\nimport './_version.js';\n/**\n * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network)\n * request strategy.\n *\n * A cache first strategy is useful for assets that have been revisioned,\n * such as URLs like `/styles/example.a8f5f1.css`, since they\n * can be cached for long periods of time.\n *\n * If the network request fails, and there is no cache match, this will throw\n * a `WorkboxError` exception.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-strategies\n */\nclass CacheFirst extends Strategy {\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n const logs = [];\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: 'makeRequest',\n paramName: 'request',\n });\n }\n let response = await handler.cacheMatch(request);\n let error = undefined;\n if (!response) {\n if (process.env.NODE_ENV !== 'production') {\n logs.push(`No response found in the '${this.cacheName}' cache. ` +\n `Will respond with a network request.`);\n }\n try {\n response = await handler.fetchAndCachePut(request);\n }\n catch (err) {\n if (err instanceof Error) {\n error = err;\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n if (response) {\n logs.push(`Got response from network.`);\n }\n else {\n logs.push(`Unable to get a response from the network.`);\n }\n }\n }\n else {\n if (process.env.NODE_ENV !== 'production') {\n logs.push(`Found a cached response in the '${this.cacheName}' cache.`);\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));\n for (const log of logs) {\n logger.log(log);\n }\n messages.printFinalResponse(response);\n logger.groupEnd();\n }\n if (!response) {\n throw new WorkboxError('no-response', { url: request.url, error });\n }\n return response;\n }\n}\nexport { CacheFirst };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { timeout } from 'workbox-core/_private/timeout.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from './Strategy.js';\nimport { messages } from './utils/messages.js';\nimport './_version.js';\n/**\n * An implementation of a\n * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)\n * request strategy.\n *\n * This class is useful if you want to take advantage of any\n * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).\n *\n * If the network request fails, this will throw a `WorkboxError` exception.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-strategies\n */\nclass NetworkOnly extends Strategy {\n /**\n * @param {Object} [options]\n * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {number} [options.networkTimeoutSeconds] If set, any network requests\n * that fail to respond within the timeout will result in a network error.\n */\n constructor(options = {}) {\n super(options);\n this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;\n }\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: '_handle',\n paramName: 'request',\n });\n }\n let error = undefined;\n let response;\n try {\n const promises = [\n handler.fetch(request),\n ];\n if (this._networkTimeoutSeconds) {\n const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);\n promises.push(timeoutPromise);\n }\n response = await Promise.race(promises);\n if (!response) {\n throw new Error(`Timed out the network response after ` +\n `${this._networkTimeoutSeconds} seconds.`);\n }\n }\n catch (err) {\n if (err instanceof Error) {\n error = err;\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));\n if (response) {\n logger.log(`Got response from network.`);\n }\n else {\n logger.log(`Unable to get a response from the network.`);\n }\n messages.printFinalResponse(response);\n logger.groupEnd();\n }\n if (!response) {\n throw new WorkboxError('no-response', { url: request.url, error });\n }\n return response;\n }\n}\nexport { NetworkOnly };\n","/*\n Copyright 2020 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A utility method that makes it easier to use `event.waitUntil` with\n * async functions and return the result.\n *\n * @param {ExtendableEvent} event\n * @param {Function} asyncFn\n * @return {Function}\n * @private\n */\nfunction waitUntil(event, asyncFn) {\n const returnPromise = asyncFn();\n event.waitUntil(returnPromise);\n return returnPromise;\n}\nexport { waitUntil };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:precaching:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport '../_version.js';\n// Name of the search parameter used to store revision info.\nconst REVISION_SEARCH_PARAM = '__WB_REVISION__';\n/**\n * Converts a manifest entry into a versioned URL suitable for precaching.\n *\n * @param {Object|string} entry\n * @return {string} A URL with versioning info.\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function createCacheKey(entry) {\n if (!entry) {\n throw new WorkboxError('add-to-cache-list-unexpected-type', { entry });\n }\n // If a precache manifest entry is a string, it's assumed to be a versioned\n // URL, like '/app.abcd1234.js'. Return as-is.\n if (typeof entry === 'string') {\n const urlObject = new URL(entry, location.href);\n return {\n cacheKey: urlObject.href,\n url: urlObject.href,\n };\n }\n const { revision, url } = entry;\n if (!url) {\n throw new WorkboxError('add-to-cache-list-unexpected-type', { entry });\n }\n // If there's just a URL and no revision, then it's also assumed to be a\n // versioned URL.\n if (!revision) {\n const urlObject = new URL(url, location.href);\n return {\n cacheKey: urlObject.href,\n url: urlObject.href,\n };\n }\n // Otherwise, construct a properly versioned URL using the custom Workbox\n // search parameter along with the revision info.\n const cacheKeyURL = new URL(url, location.href);\n const originalURL = new URL(url, location.href);\n cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);\n return {\n cacheKey: cacheKeyURL.href,\n url: originalURL.href,\n };\n}\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A plugin, designed to be used with PrecacheController, to determine the\n * of assets that were updated (or not updated) during the install event.\n *\n * @private\n */\nclass PrecacheInstallReportPlugin {\n constructor() {\n this.updatedURLs = [];\n this.notUpdatedURLs = [];\n this.handlerWillStart = async ({ request, state, }) => {\n // TODO: `state` should never be undefined...\n if (state) {\n state.originalRequest = request;\n }\n };\n this.cachedResponseWillBeUsed = async ({ event, state, cachedResponse, }) => {\n if (event.type === 'install') {\n if (state &&\n state.originalRequest &&\n state.originalRequest instanceof Request) {\n // TODO: `state` should never be undefined...\n const url = state.originalRequest.url;\n if (cachedResponse) {\n this.notUpdatedURLs.push(url);\n }\n else {\n this.updatedURLs.push(url);\n }\n }\n }\n return cachedResponse;\n };\n }\n}\nexport { PrecacheInstallReportPlugin };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A plugin, designed to be used with PrecacheController, to translate URLs into\n * the corresponding cache key, based on the current revision info.\n *\n * @private\n */\nclass PrecacheCacheKeyPlugin {\n constructor({ precacheController }) {\n this.cacheKeyWillBeUsed = async ({ request, params, }) => {\n // Params is type any, can't change right now.\n /* eslint-disable */\n const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) ||\n this._precacheController.getCacheKeyForURL(request.url);\n /* eslint-enable */\n return cacheKey\n ? new Request(cacheKey, { headers: request.headers })\n : request;\n };\n this._precacheController = precacheController;\n }\n}\nexport { PrecacheCacheKeyPlugin };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport '../_version.js';\n/**\n * @param {string} groupTitle\n * @param {Array<string>} deletedURLs\n *\n * @private\n */\nconst logGroup = (groupTitle, deletedURLs) => {\n logger.groupCollapsed(groupTitle);\n for (const url of deletedURLs) {\n logger.log(url);\n }\n logger.groupEnd();\n};\n/**\n * @param {Array<string>} deletedURLs\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function printCleanupDetails(deletedURLs) {\n const deletionCount = deletedURLs.length;\n if (deletionCount > 0) {\n logger.groupCollapsed(`During precaching cleanup, ` +\n `${deletionCount} cached ` +\n `request${deletionCount === 1 ? ' was' : 's were'} deleted.`);\n logGroup('Deleted Cache Requests', deletedURLs);\n logger.groupEnd();\n }\n}\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport '../_version.js';\n/**\n * @param {string} groupTitle\n * @param {Array<string>} urls\n *\n * @private\n */\nfunction _nestedGroup(groupTitle, urls) {\n if (urls.length === 0) {\n return;\n }\n logger.groupCollapsed(groupTitle);\n for (const url of urls) {\n logger.log(url);\n }\n logger.groupEnd();\n}\n/**\n * @param {Array<string>} urlsToPrecache\n * @param {Array<string>} urlsAlreadyPrecached\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) {\n const precachedCount = urlsToPrecache.length;\n const alreadyPrecachedCount = urlsAlreadyPrecached.length;\n if (precachedCount || alreadyPrecachedCount) {\n let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;\n if (alreadyPrecachedCount > 0) {\n message +=\n ` ${alreadyPrecachedCount} ` +\n `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;\n }\n logger.groupCollapsed(message);\n _nestedGroup(`View newly precached URLs.`, urlsToPrecache);\n _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached);\n logger.groupEnd();\n }\n}\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nlet supportStatus;\n/**\n * A utility function that determines whether the current browser supports\n * constructing a new `Response` from a `response.body` stream.\n *\n * @return {boolean} `true`, if the current browser can successfully\n * construct a `Response` from a `response.body` stream, `false` otherwise.\n *\n * @private\n */\nfunction canConstructResponseFromBodyStream() {\n if (supportStatus === undefined) {\n const testResponse = new Response('');\n if ('body' in testResponse) {\n try {\n new Response(testResponse.body);\n supportStatus = true;\n }\n catch (error) {\n supportStatus = false;\n }\n }\n supportStatus = false;\n }\n return supportStatus;\n}\nexport { canConstructResponseFromBodyStream };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { canConstructResponseFromBodyStream } from './_private/canConstructResponseFromBodyStream.js';\nimport { WorkboxError } from './_private/WorkboxError.js';\nimport './_version.js';\n/**\n * Allows developers to copy a response and modify its `headers`, `status`,\n * or `statusText` values (the values settable via a\n * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax}\n * object in the constructor).\n * To modify these values, pass a function as the second argument. That\n * function will be invoked with a single object with the response properties\n * `{headers, status, statusText}`. The return value of this function will\n * be used as the `ResponseInit` for the new `Response`. To change the values\n * either modify the passed parameter(s) and return it, or return a totally\n * new object.\n *\n * This method is intentionally limited to same-origin responses, regardless of\n * whether CORS was used or not.\n *\n * @param {Response} response\n * @param {Function} modifier\n * @memberof workbox-core\n */\nasync function copyResponse(response, modifier) {\n let origin = null;\n // If response.url isn't set, assume it's cross-origin and keep origin null.\n if (response.url) {\n const responseURL = new URL(response.url);\n origin = responseURL.origin;\n }\n if (origin !== self.location.origin) {\n throw new WorkboxError('cross-origin-copy-response', { origin });\n }\n const clonedResponse = response.clone();\n // Create a fresh `ResponseInit` object by cloning the headers.\n const responseInit = {\n headers: new Headers(clonedResponse.headers),\n status: clonedResponse.status,\n statusText: clonedResponse.statusText,\n };\n // Apply any user modifications.\n const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;\n // Create the new response from the body stream and `ResponseInit`\n // modifications. Note: not all browsers support the Response.body stream,\n // so fall back to reading the entire body into memory as a blob.\n const body = canConstructResponseFromBodyStream()\n ? clonedResponse.body\n : await clonedResponse.blob();\n return new Response(body, modifiedResponseInit);\n}\nexport { copyResponse };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { copyResponse } from 'workbox-core/copyResponse.js';\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from 'workbox-strategies/Strategy.js';\nimport './_version.js';\n/**\n * A {@link workbox-strategies.Strategy} implementation\n * specifically designed to work with\n * {@link workbox-precaching.PrecacheController}\n * to both cache and fetch precached assets.\n *\n * Note: an instance of this class is created automatically when creating a\n * `PrecacheController`; it's generally not necessary to create this yourself.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-precaching\n */\nclass PrecacheStrategy extends Strategy {\n /**\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init}\n * of all fetch() requests made by this strategy.\n * @param {Object} [options.matchOptions] The\n * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to\n * get the response from the network if there's a precache miss.\n */\n constructor(options = {}) {\n options.cacheName = cacheNames.getPrecacheName(options.cacheName);\n super(options);\n this._fallbackToNetwork =\n options.fallbackToNetwork === false ? false : true;\n // Redirected responses cannot be used to satisfy a navigation request, so\n // any redirected response must be \"copied\" rather than cloned, so the new\n // response doesn't contain the `redirected` flag. See:\n // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1\n this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);\n }\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n const response = await handler.cacheMatch(request);\n if (response) {\n return response;\n }\n // If this is an `install` event for an entry that isn't already cached,\n // then populate the cache.\n if (handler.event && handler.event.type === 'install') {\n return await this._handleInstall(request, handler);\n }\n // Getting here means something went wrong. An entry that should have been\n // precached wasn't found in the cache.\n return await this._handleFetch(request, handler);\n }\n async _handleFetch(request, handler) {\n let response;\n const params = (handler.params || {});\n // Fall back to the network if we're configured to do so.\n if (this._fallbackToNetwork) {\n if (process.env.NODE_ENV !== 'production') {\n logger.warn(`The precached response for ` +\n `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` +\n `found. Falling back to the network.`);\n }\n const integrityInManifest = params.integrity;\n const integrityInRequest = request.integrity;\n const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;\n // Do not add integrity if the original request is no-cors\n // See https://github.com/GoogleChrome/workbox/issues/3096\n response = await handler.fetch(new Request(request, {\n integrity: request.mode !== 'no-cors'\n ? integrityInRequest || integrityInManifest\n : undefined,\n }));\n // It's only \"safe\" to repair the cache if we're using SRI to guarantee\n // that the response matches the precache manifest's expectations,\n // and there's either a) no integrity property in the incoming request\n // or b) there is an integrity, and it matches the precache manifest.\n // See https://github.com/GoogleChrome/workbox/issues/2858\n // Also if the original request users no-cors we don't use integrity.\n // See https://github.com/GoogleChrome/workbox/issues/3096\n if (integrityInManifest &&\n noIntegrityConflict &&\n request.mode !== 'no-cors') {\n this._useDefaultCacheabilityPluginIfNeeded();\n const wasCached = await handler.cachePut(request, response.clone());\n if (process.env.NODE_ENV !== 'production') {\n if (wasCached) {\n logger.log(`A response for ${getFriendlyURL(request.url)} ` +\n `was used to \"repair\" the precache.`);\n }\n }\n }\n }\n else {\n // This shouldn't normally happen, but there are edge cases:\n // https://github.com/GoogleChrome/workbox/issues/1441\n throw new WorkboxError('missing-precache-entry', {\n cacheName: this.cacheName,\n url: request.url,\n });\n }\n if (process.env.NODE_ENV !== 'production') {\n const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read'));\n // Workbox is going to handle the route.\n // print the routing details to the console.\n logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL(request.url));\n logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);\n logger.groupCollapsed(`View request details here.`);\n logger.log(request);\n logger.groupEnd();\n logger.groupCollapsed(`View response details here.`);\n logger.log(response);\n logger.groupEnd();\n logger.groupEnd();\n }\n return response;\n }\n async _handleInstall(request, handler) {\n this._useDefaultCacheabilityPluginIfNeeded();\n const response = await handler.fetch(request);\n // Make sure we defer cachePut() until after we know the response\n // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737\n const wasCached = await handler.cachePut(request, response.clone());\n if (!wasCached) {\n // Throwing here will lead to the `install` handler failing, which\n // we want to do if *any* of the responses aren't safe to cache.\n throw new WorkboxError('bad-precaching-response', {\n url: request.url,\n status: response.status,\n });\n }\n return response;\n }\n /**\n * This method is complex, as there a number of things to account for:\n *\n * The `plugins` array can be set at construction, and/or it might be added to\n * to at any time before the strategy is used.\n *\n * At the time the strategy is used (i.e. during an `install` event), there\n * needs to be at least one plugin that implements `cacheWillUpdate` in the\n * array, other than `copyRedirectedCacheableResponsesPlugin`.\n *\n * - If this method is called and there are no suitable `cacheWillUpdate`\n * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.\n *\n * - If this method is called and there is exactly one `cacheWillUpdate`, then\n * we don't have to do anything (this might be a previously added\n * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).\n *\n * - If this method is called and there is more than one `cacheWillUpdate`,\n * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,\n * we need to remove it. (This situation is unlikely, but it could happen if\n * the strategy is used multiple times, the first without a `cacheWillUpdate`,\n * and then later on after manually adding a custom `cacheWillUpdate`.)\n *\n * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.\n *\n * @private\n */\n _useDefaultCacheabilityPluginIfNeeded() {\n let defaultPluginIndex = null;\n let cacheWillUpdatePluginCount = 0;\n for (const [index, plugin] of this.plugins.entries()) {\n // Ignore the copy redirected plugin when determining what to do.\n if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {\n continue;\n }\n // Save the default plugin's index, in case it needs to be removed.\n if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {\n defaultPluginIndex = index;\n }\n if (plugin.cacheWillUpdate) {\n cacheWillUpdatePluginCount++;\n }\n }\n if (cacheWillUpdatePluginCount === 0) {\n this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);\n }\n else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {\n // Only remove the default plugin; multiple custom plugins are allowed.\n this.plugins.splice(defaultPluginIndex, 1);\n }\n // Nothing needs to be done if cacheWillUpdatePluginCount is 1\n }\n}\nPrecacheStrategy.defaultPrecacheCacheabilityPlugin = {\n async cacheWillUpdate({ response }) {\n if (!response || response.status >= 400) {\n return null;\n }\n return response;\n },\n};\nPrecacheStrategy.copyRedirectedCacheableResponsesPlugin = {\n async cacheWillUpdate({ response }) {\n return response.redirected ? await copyResponse(response) : response;\n },\n};\nexport { PrecacheStrategy };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { waitUntil } from 'workbox-core/_private/waitUntil.js';\nimport { createCacheKey } from './utils/createCacheKey.js';\nimport { PrecacheInstallReportPlugin } from './utils/PrecacheInstallReportPlugin.js';\nimport { PrecacheCacheKeyPlugin } from './utils/PrecacheCacheKeyPlugin.js';\nimport { printCleanupDetails } from './utils/printCleanupDetails.js';\nimport { printInstallDetails } from './utils/printInstallDetails.js';\nimport { PrecacheStrategy } from './PrecacheStrategy.js';\nimport './_version.js';\n/**\n * Performs efficient precaching of assets.\n *\n * @memberof workbox-precaching\n */\nclass PrecacheController {\n /**\n * Create a new PrecacheController.\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] The cache to use for precaching.\n * @param {string} [options.plugins] Plugins to use when precaching as well\n * as responding to fetch events for precached assets.\n * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to\n * get the response from the network if there's a precache miss.\n */\n constructor({ cacheName, plugins = [], fallbackToNetwork = true, } = {}) {\n this._urlsToCacheKeys = new Map();\n this._urlsToCacheModes = new Map();\n this._cacheKeysToIntegrities = new Map();\n this._strategy = new PrecacheStrategy({\n cacheName: cacheNames.getPrecacheName(cacheName),\n plugins: [\n ...plugins,\n new PrecacheCacheKeyPlugin({ precacheController: this }),\n ],\n fallbackToNetwork,\n });\n // Bind the install and activate methods to the instance.\n this.install = this.install.bind(this);\n this.activate = this.activate.bind(this);\n }\n /**\n * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and\n * used to cache assets and respond to fetch events.\n */\n get strategy() {\n return this._strategy;\n }\n /**\n * Adds items to the precache list, removing any duplicates and\n * stores the files in the\n * {@link workbox-core.cacheNames|\"precache cache\"} when the service\n * worker installs.\n *\n * This method can be called multiple times.\n *\n * @param {Array<Object|string>} [entries=[]] Array of entries to precache.\n */\n precache(entries) {\n this.addToCacheList(entries);\n if (!this._installAndActiveListenersAdded) {\n self.addEventListener('install', this.install);\n self.addEventListener('activate', this.activate);\n this._installAndActiveListenersAdded = true;\n }\n }\n /**\n * This method will add items to the precache list, removing duplicates\n * and ensuring the information is valid.\n *\n * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries\n * Array of entries to precache.\n */\n addToCacheList(entries) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isArray(entries, {\n moduleName: 'workbox-precaching',\n className: 'PrecacheController',\n funcName: 'addToCacheList',\n paramName: 'entries',\n });\n }\n const urlsToWarnAbout = [];\n for (const entry of entries) {\n // See https://github.com/GoogleChrome/workbox/issues/2259\n if (typeof entry === 'string') {\n urlsToWarnAbout.push(entry);\n }\n else if (entry && entry.revision === undefined) {\n urlsToWarnAbout.push(entry.url);\n }\n const { cacheKey, url } = createCacheKey(entry);\n const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default';\n if (this._urlsToCacheKeys.has(url) &&\n this._urlsToCacheKeys.get(url) !== cacheKey) {\n throw new WorkboxError('add-to-cache-list-conflicting-entries', {\n firstEntry: this._urlsToCacheKeys.get(url),\n secondEntry: cacheKey,\n });\n }\n if (typeof entry !== 'string' && entry.integrity) {\n if (this._cacheKeysToIntegrities.has(cacheKey) &&\n this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {\n throw new WorkboxError('add-to-cache-list-conflicting-integrities', {\n url,\n });\n }\n this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);\n }\n this._urlsToCacheKeys.set(url, cacheKey);\n this._urlsToCacheModes.set(url, cacheMode);\n if (urlsToWarnAbout.length > 0) {\n const warningMessage = `Workbox is precaching URLs without revision ` +\n `info: ${urlsToWarnAbout.join(', ')}\\nThis is generally NOT safe. ` +\n `Learn more at https://bit.ly/wb-precache`;\n if (process.env.NODE_ENV === 'production') {\n // Use console directly to display this warning without bloating\n // bundle sizes by pulling in all of the logger codebase in prod.\n console.warn(warningMessage);\n }\n else {\n logger.warn(warningMessage);\n }\n }\n }\n }\n /**\n * Precaches new and updated assets. Call this method from the service worker\n * install event.\n *\n * Note: this method calls `event.waitUntil()` for you, so you do not need\n * to call it yourself in your event handlers.\n *\n * @param {ExtendableEvent} event\n * @return {Promise<workbox-precaching.InstallResult>}\n */\n install(event) {\n // waitUntil returns Promise<any>\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return waitUntil(event, async () => {\n const installReportPlugin = new PrecacheInstallReportPlugin();\n this.strategy.plugins.push(installReportPlugin);\n // Cache entries one at a time.\n // See https://github.com/GoogleChrome/workbox/issues/2528\n for (const [url, cacheKey] of this._urlsToCacheKeys) {\n const integrity = this._cacheKeysToIntegrities.get(cacheKey);\n const cacheMode = this._urlsToCacheModes.get(url);\n const request = new Request(url, {\n integrity,\n cache: cacheMode,\n credentials: 'same-origin',\n });\n await Promise.all(this.strategy.handleAll({\n params: { cacheKey },\n request,\n event,\n }));\n }\n const { updatedURLs, notUpdatedURLs } = installReportPlugin;\n if (process.env.NODE_ENV !== 'production') {\n printInstallDetails(updatedURLs, notUpdatedURLs);\n }\n return { updatedURLs, notUpdatedURLs };\n });\n }\n /**\n * Deletes assets that are no longer present in the current precache manifest.\n * Call this method from the service worker activate event.\n *\n * Note: this method calls `event.waitUntil()` for you, so you do not need\n * to call it yourself in your event handlers.\n *\n * @param {ExtendableEvent} event\n * @return {Promise<workbox-precaching.CleanupResult>}\n */\n activate(event) {\n // waitUntil returns Promise<any>\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return waitUntil(event, async () => {\n const cache = await self.caches.open(this.strategy.cacheName);\n const currentlyCachedRequests = await cache.keys();\n const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());\n const deletedURLs = [];\n for (const request of currentlyCachedRequests) {\n if (!expectedCacheKeys.has(request.url)) {\n await cache.delete(request);\n deletedURLs.push(request.url);\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n printCleanupDetails(deletedURLs);\n }\n return { deletedURLs };\n });\n }\n /**\n * Returns a mapping of a precached URL to the corresponding cache key, taking\n * into account the revision information for the URL.\n *\n * @return {Map<string, string>} A URL to cache key mapping.\n */\n getURLsToCacheKeys() {\n return this._urlsToCacheKeys;\n }\n /**\n * Returns a list of all the URLs that have been precached by the current\n * service worker.\n *\n * @return {Array<string>} The precached URLs.\n */\n getCachedURLs() {\n return [...this._urlsToCacheKeys.keys()];\n }\n /**\n * Returns the cache key used for storing a given URL. If that URL is\n * unversioned, like `/index.html', then the cache key will be the original\n * URL with a search parameter appended to it.\n *\n * @param {string} url A URL whose cache key you want to look up.\n * @return {string} The versioned URL that corresponds to a cache key\n * for the original URL, or undefined if that URL isn't precached.\n */\n getCacheKeyForURL(url) {\n const urlObject = new URL(url, location.href);\n return this._urlsToCacheKeys.get(urlObject.href);\n }\n /**\n * @param {string} url A cache key whose SRI you want to look up.\n * @return {string} The subresource integrity associated with the cache key,\n * or undefined if it's not set.\n */\n getIntegrityForCacheKey(cacheKey) {\n return this._cacheKeysToIntegrities.get(cacheKey);\n }\n /**\n * This acts as a drop-in replacement for\n * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)\n * with the following differences:\n *\n * - It knows what the name of the precache is, and only checks in that cache.\n * - It allows you to pass in an \"original\" URL without versioning parameters,\n * and it will automatically look up the correct cache key for the currently\n * active revision of that URL.\n *\n * E.g., `matchPrecache('index.html')` will find the correct precached\n * response for the currently active service worker, even if the actual cache\n * key is `'/index.html?__WB_REVISION__=1234abcd'`.\n *\n * @param {string|Request} request The key (without revisioning parameters)\n * to look up in the precache.\n * @return {Promise<Response|undefined>}\n */\n async matchPrecache(request) {\n const url = request instanceof Request ? request.url : request;\n const cacheKey = this.getCacheKeyForURL(url);\n if (cacheKey) {\n const cache = await self.caches.open(this.strategy.cacheName);\n return cache.match(cacheKey);\n }\n return undefined;\n }\n /**\n * Returns a function that looks up `url` in the precache (taking into\n * account revision information), and returns the corresponding `Response`.\n *\n * @param {string} url The precached URL which will be used to lookup the\n * `Response`.\n * @return {workbox-routing~handlerCallback}\n */\n createHandlerBoundToURL(url) {\n const cacheKey = this.getCacheKeyForURL(url);\n if (!cacheKey) {\n throw new WorkboxError('non-precached-url', { url });\n }\n return (options) => {\n options.request = new Request(url);\n options.params = Object.assign({ cacheKey }, options.params);\n return this.strategy.handle(options);\n };\n }\n}\nexport { PrecacheController };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { PrecacheController } from '../PrecacheController.js';\nimport '../_version.js';\nlet precacheController;\n/**\n * @return {PrecacheController}\n * @private\n */\nexport const getOrCreatePrecacheController = () => {\n if (!precacheController) {\n precacheController = new PrecacheController();\n }\n return precacheController;\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * Removes any URL search parameters that should be ignored.\n *\n * @param {URL} urlObject The original URL.\n * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against\n * each search parameter name. Matches mean that the search parameter should be\n * ignored.\n * @return {URL} The URL with any ignored search parameters removed.\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) {\n // Convert the iterable into an array at the start of the loop to make sure\n // deletion doesn't mess up iteration.\n for (const paramName of [...urlObject.searchParams.keys()]) {\n if (ignoreURLParametersMatching.some((regExp) => regExp.test(paramName))) {\n urlObject.searchParams.delete(paramName);\n }\n }\n return urlObject;\n}\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { removeIgnoredSearchParams } from './removeIgnoredSearchParams.js';\nimport '../_version.js';\n/**\n * Generator function that yields possible variations on the original URL to\n * check, one at a time.\n *\n * @param {string} url\n * @param {Object} options\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function* generateURLVariations(url, { ignoreURLParametersMatching = [/^utm_/, /^fbclid$/], directoryIndex = 'index.html', cleanURLs = true, urlManipulation, } = {}) {\n const urlObject = new URL(url, location.href);\n urlObject.hash = '';\n yield urlObject.href;\n const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);\n yield urlWithoutIgnoredParams.href;\n if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {\n const directoryURL = new URL(urlWithoutIgnoredParams.href);\n directoryURL.pathname += directoryIndex;\n yield directoryURL.href;\n }\n if (cleanURLs) {\n const cleanURL = new URL(urlWithoutIgnoredParams.href);\n cleanURL.pathname += '.html';\n yield cleanURL.href;\n }\n if (urlManipulation) {\n const additionalURLs = urlManipulation({ url: urlObject });\n for (const urlToAttempt of additionalURLs) {\n yield urlToAttempt.href;\n }\n }\n}\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { Route } from 'workbox-routing/Route.js';\nimport { generateURLVariations } from './utils/generateURLVariations.js';\nimport './_version.js';\n/**\n * A subclass of {@link workbox-routing.Route} that takes a\n * {@link workbox-precaching.PrecacheController}\n * instance and uses it to match incoming requests and handle fetching\n * responses from the precache.\n *\n * @memberof workbox-precaching\n * @extends workbox-routing.Route\n */\nclass PrecacheRoute extends Route {\n /**\n * @param {PrecacheController} precacheController A `PrecacheController`\n * instance used to both match requests and respond to fetch events.\n * @param {Object} [options] Options to control how requests are matched\n * against the list of precached URLs.\n * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will\n * check cache entries for a URLs ending with '/' to see if there is a hit when\n * appending the `directoryIndex` value.\n * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An\n * array of regex's to remove search params when looking for a cache match.\n * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will\n * check the cache for the URL with a `.html` added to the end of the end.\n * @param {workbox-precaching~urlManipulation} [options.urlManipulation]\n * This is a function that should take a URL and return an array of\n * alternative URLs that should be checked for precache matches.\n */\n constructor(precacheController, options) {\n const match = ({ request, }) => {\n const urlsToCacheKeys = precacheController.getURLsToCacheKeys();\n for (const possibleURL of generateURLVariations(request.url, options)) {\n const cacheKey = urlsToCacheKeys.get(possibleURL);\n if (cacheKey) {\n const integrity = precacheController.getIntegrityForCacheKey(cacheKey);\n return { cacheKey, integrity };\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Precaching did not find a match for ` + getFriendlyURL(request.url));\n }\n return;\n };\n super(match, precacheController.strategy);\n }\n}\nexport { PrecacheRoute };\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { registerRoute } from 'workbox-routing/registerRoute.js';\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport { PrecacheRoute } from './PrecacheRoute.js';\nimport './_version.js';\n/**\n * Add a `fetch` listener to the service worker that will\n * respond to\n * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}\n * with precached assets.\n *\n * Requests for assets that aren't precached, the `FetchEvent` will not be\n * responded to, allowing the event to fall through to other `fetch` event\n * listeners.\n *\n * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute}\n * options.\n *\n * @memberof workbox-precaching\n */\nfunction addRoute(options) {\n const precacheController = getOrCreatePrecacheController();\n const precacheRoute = new PrecacheRoute(precacheController, options);\n registerRoute(precacheRoute);\n}\nexport { addRoute };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport './_version.js';\n/**\n * Adds items to the precache list, removing any duplicates and\n * stores the files in the\n * {@link workbox-core.cacheNames|\"precache cache\"} when the service\n * worker installs.\n *\n * This method can be called multiple times.\n *\n * Please note: This method **will not** serve any of the cached files for you.\n * It only precaches files. To respond to a network request you call\n * {@link workbox-precaching.addRoute}.\n *\n * If you have a single array of files to precache, you can just call\n * {@link workbox-precaching.precacheAndRoute}.\n *\n * @param {Array<Object|string>} [entries=[]] Array of entries to precache.\n *\n * @memberof workbox-precaching\n */\nfunction precache(entries) {\n const precacheController = getOrCreatePrecacheController();\n precacheController.precache(entries);\n}\nexport { precache };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { addRoute } from './addRoute.js';\nimport { precache } from './precache.js';\nimport './_version.js';\n/**\n * This method will add entries to the precache list and add a route to\n * respond to fetch events.\n *\n * This is a convenience method that will call\n * {@link workbox-precaching.precache} and\n * {@link workbox-precaching.addRoute} in a single call.\n *\n * @param {Array<Object|string>} entries Array of entries to precache.\n * @param {Object} [options] See the\n * {@link workbox-precaching.PrecacheRoute} options.\n *\n * @memberof workbox-precaching\n */\nfunction precacheAndRoute(entries, options) {\n precache(entries);\n addRoute(options);\n}\nexport { precacheAndRoute };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst SUBSTRING_TO_FIND = '-precache-';\n/**\n * Cleans up incompatible precaches that were created by older versions of\n * Workbox, by a service worker registered under the current scope.\n *\n * This is meant to be called as part of the `activate` event.\n *\n * This should be safe to use as long as you don't include `substringToFind`\n * (defaulting to `-precache-`) in your non-precache cache names.\n *\n * @param {string} currentPrecacheName The cache name currently in use for\n * precaching. This cache won't be deleted.\n * @param {string} [substringToFind='-precache-'] Cache names which include this\n * substring will be deleted (excluding `currentPrecacheName`).\n * @return {Array<string>} A list of all the cache names that were deleted.\n *\n * @private\n * @memberof workbox-precaching\n */\nconst deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => {\n const cacheNames = await self.caches.keys();\n const cacheNamesToDelete = cacheNames.filter((cacheName) => {\n return (cacheName.includes(substringToFind) &&\n cacheName.includes(self.registration.scope) &&\n cacheName !== currentPrecacheName);\n });\n await Promise.all(cacheNamesToDelete.map((cacheName) => self.caches.delete(cacheName)));\n return cacheNamesToDelete;\n};\nexport { deleteOutdatedCaches };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { deleteOutdatedCaches } from './utils/deleteOutdatedCaches.js';\nimport './_version.js';\n/**\n * Adds an `activate` event listener which will clean up incompatible\n * precaches that were created by older versions of Workbox.\n *\n * @memberof workbox-precaching\n */\nfunction cleanupOutdatedCaches() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('activate', ((event) => {\n const cacheName = cacheNames.getPrecacheName();\n event.waitUntil(deleteOutdatedCaches(cacheName).then((cachesDeleted) => {\n if (process.env.NODE_ENV !== 'production') {\n if (cachesDeleted.length > 0) {\n logger.log(`The following out-of-date precaches were cleaned up ` +\n `automatically:`, cachesDeleted);\n }\n }\n }));\n }));\n}\nexport { cleanupOutdatedCaches };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { Route } from './Route.js';\nimport './_version.js';\n/**\n * NavigationRoute makes it easy to create a\n * {@link workbox-routing.Route} that matches for browser\n * [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}.\n *\n * It will only match incoming Requests whose\n * {@link https://fetch.spec.whatwg.org/#concept-request-mode|mode}\n * is set to `navigate`.\n *\n * You can optionally only apply this route to a subset of navigation requests\n * by using one or both of the `denylist` and `allowlist` parameters.\n *\n * @memberof workbox-routing\n * @extends workbox-routing.Route\n */\nclass NavigationRoute extends Route {\n /**\n * If both `denylist` and `allowlist` are provided, the `denylist` will\n * take precedence and the request will not match this route.\n *\n * The regular expressions in `allowlist` and `denylist`\n * are matched against the concatenated\n * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname}\n * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search}\n * portions of the requested URL.\n *\n * *Note*: These RegExps may be evaluated against every destination URL during\n * a navigation. Avoid using\n * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077),\n * or else your users may see delays when navigating your site.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {Object} options\n * @param {Array<RegExp>} [options.denylist] If any of these patterns match,\n * the route will not handle the request (even if a allowlist RegExp matches).\n * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns\n * match the URL's pathname and search parameter, the route will handle the\n * request (assuming the denylist doesn't match).\n */\n constructor(handler, { allowlist = [/./], denylist = [] } = {}) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isArrayOfClass(allowlist, RegExp, {\n moduleName: 'workbox-routing',\n className: 'NavigationRoute',\n funcName: 'constructor',\n paramName: 'options.allowlist',\n });\n assert.isArrayOfClass(denylist, RegExp, {\n moduleName: 'workbox-routing',\n className: 'NavigationRoute',\n funcName: 'constructor',\n paramName: 'options.denylist',\n });\n }\n super((options) => this._match(options), handler);\n this._allowlist = allowlist;\n this._denylist = denylist;\n }\n /**\n * Routes match handler.\n *\n * @param {Object} options\n * @param {URL} options.url\n * @param {Request} options.request\n * @return {boolean}\n *\n * @private\n */\n _match({ url, request }) {\n if (request && request.mode !== 'navigate') {\n return false;\n }\n const pathnameAndSearch = url.pathname + url.search;\n for (const regExp of this._denylist) {\n if (regExp.test(pathnameAndSearch)) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`The navigation route ${pathnameAndSearch} is not ` +\n `being used, since the URL matches this denylist pattern: ` +\n `${regExp.toString()}`);\n }\n return false;\n }\n }\n if (this._allowlist.some((regExp) => regExp.test(pathnameAndSearch))) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`);\n }\n return true;\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`The navigation route ${pathnameAndSearch} is not ` +\n `being used, since the URL being navigated to doesn't ` +\n `match the allowlist.`);\n }\n return false;\n }\n}\nexport { NavigationRoute };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport './_version.js';\n/**\n * Helper function that calls\n * {@link PrecacheController#createHandlerBoundToURL} on the default\n * {@link PrecacheController} instance.\n *\n * If you are creating your own {@link PrecacheController}, then call the\n * {@link PrecacheController#createHandlerBoundToURL} on that instance,\n * instead of using this function.\n *\n * @param {string} url The precached URL which will be used to lookup the\n * `Response`.\n * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the\n * response from the network if there's a precache miss.\n * @return {workbox-routing~handlerCallback}\n *\n * @memberof workbox-precaching\n */\nfunction createHandlerBoundToURL(url) {\n const precacheController = getOrCreatePrecacheController();\n return precacheController.createHandlerBoundToURL(url);\n}\nexport { createHandlerBoundToURL };\n"],"names":["self","_","e","logger","globalThis","__WB_DISABLE_DEV_LOGS","inGroup","methodToColorMap","debug","log","warn","error","groupCollapsed","groupEnd","print","method","args","test","navigator","userAgent","console","styles","logPrefix","join","api","loggerMethods","Object","keys","key","messages","invalid-value","paramName","validValueDescription","value","Error","JSON","stringify","not-an-array","moduleName","className","funcName","incorrect-type","expectedType","classNameStr","incorrect-class","expectedClassName","isReturnValueProblem","missing-a-method","expectedMethod","add-to-cache-list-unexpected-type","entry","add-to-cache-list-conflicting-entries","firstEntry","secondEntry","plugin-error-request-will-fetch","thrownErrorMessage","invalid-cache-name","cacheNameId","unregister-route-but-not-found-with-method","unregister-route-route-not-registered","queue-replay-failed","name","duplicate-queue-name","expired-test-without-max-age","methodName","unsupported-route-type","not-array-of-class","expectedClass","max-entries-or-age-required","statuses-or-headers-required","invalid-string","channel-name-required","invalid-responses-are-same-args","expire-custom-caches-only","unit-must-be-bytes","normalizedRangeHeader","single-range-only","invalid-range-values","no-range-header","range-not-satisfiable","size","start","end","attempt-to-cache-non-get-request","url","cache-put-with-no-response","no-response","message","bad-precaching-response","status","non-precached-url","add-to-cache-list-conflicting-integrities","missing-precache-entry","cacheName","cross-origin-copy-response","origin","opaque-streams-source","type","generatorFunction","code","details","messageGenerator","WorkboxError","constructor","errorCode","isArray","Array","hasMethod","object","isType","isInstance","isOneOf","validValues","includes","isArrayOfClass","item","finalAssertExports","defaultMethod","validMethods","normalizeHandler","handler","assert","handle","Route","match","setCatchHandler","catchHandler","RegExpRoute","regExp","RegExp","result","exec","href","location","index","toString","slice","getFriendlyURL","urlObj","URL","String","replace","Router","_routes","Map","_defaultHandlerMap","routes","addFetchListener","addEventListener","event","request","responsePromise","handleRequest","respondWith","addCacheListener","data","payload","urlsToCache","requestPromises","Promise","all","map","Request","waitUntil","ports","then","postMessage","protocol","startsWith","sameOrigin","params","route","findMatchingRoute","debugMessages","push","has","get","forEach","msg","err","reject","_catchHandler","catch","catchErr","matchResult","length","undefined","setDefaultHandler","set","registerRoute","unregisterRoute","routeIndex","indexOf","splice","defaultRouter","getOrCreateDefaultRouter","capture","captureUrl","valueToCheck","pathname","wildcards","matchCallback","CacheableResponse","config","statuses","headers","_statuses","_headers","isResponseCacheable","response","Response","cacheable","some","headerName","logFriendlyHeaders","CacheableResponsePlugin","cacheWillUpdate","_cacheableResponse","_cacheNameDetails","googleAnalytics","precache","prefix","runtime","suffix","registration","scope","_createCacheName","filter","eachCacheNameDetail","fn","cacheNames","updateDetails","getGoogleAnalyticsName","userCacheName","getPrecacheName","getPrefix","getRuntimeName","getSuffix","stripParams","fullURL","ignoreParams","strippedURL","param","searchParams","delete","cacheMatchIgnoreParams","cache","matchOptions","strippedRequestURL","keysOptions","assign","ignoreSearch","cacheKeys","cacheKey","strippedCacheKeyURL","Deferred","promise","resolve","quotaErrorCallbacks","Set","executeQuotaErrorCallbacks","callback","timeout","ms","setTimeout","toRequest","input","StrategyHandler","strategy","options","_cacheKeys","ExtendableEvent","_strategy","_handlerDeferred","_extendLifetimePromises","_plugins","plugins","_pluginStateMap","plugin","fetch","mode","FetchEvent","preloadResponse","possiblePreloadResponse","originalRequest","hasCallback","clone","cb","iterateCallbacks","pluginFilteredRequest","fetchResponse","fetchOptions","runCallbacks","fetchAndCachePut","responseClone","cachePut","cacheMatch","cachedResponse","effectiveRequest","getCacheKey","multiMatchOptions","caches","vary","responseToCache","_ensureResponseSafeToCache","open","hasCacheUpdateCallback","oldResponse","put","newResponse","state","statefulCallback","statefulParam","doneWaiting","shift","destroy","pluginsUsed","Strategy","responseDone","handleAll","_getResponse","handlerDone","_awaitComplete","_handle","waitUntilError","strategyStart","strategyName","printFinalResponse","CacheFirst","logs","NetworkOnly","_networkTimeoutSeconds","networkTimeoutSeconds","promises","timeoutPromise","race","asyncFn","returnPromise","REVISION_SEARCH_PARAM","createCacheKey","urlObject","revision","cacheKeyURL","originalURL","PrecacheInstallReportPlugin","updatedURLs","notUpdatedURLs","handlerWillStart","cachedResponseWillBeUsed","PrecacheCacheKeyPlugin","precacheController","cacheKeyWillBeUsed","_precacheController","getCacheKeyForURL","logGroup","groupTitle","deletedURLs","printCleanupDetails","deletionCount","_nestedGroup","urls","printInstallDetails","urlsToPrecache","urlsAlreadyPrecached","precachedCount","alreadyPrecachedCount","supportStatus","canConstructResponseFromBodyStream","testResponse","body","copyResponse","modifier","responseURL","clonedResponse","responseInit","Headers","statusText","modifiedResponseInit","blob","PrecacheStrategy","_fallbackToNetwork","fallbackToNetwork","copyRedirectedCacheableResponsesPlugin","_handleInstall","_handleFetch","integrityInManifest","integrity","integrityInRequest","noIntegrityConflict","_useDefaultCacheabilityPluginIfNeeded","wasCached","defaultPluginIndex","cacheWillUpdatePluginCount","entries","defaultPrecacheCacheabilityPlugin","redirected","PrecacheController","_urlsToCacheKeys","_urlsToCacheModes","_cacheKeysToIntegrities","install","bind","activate","addToCacheList","_installAndActiveListenersAdded","urlsToWarnAbout","cacheMode","warningMessage","installReportPlugin","credentials","currentlyCachedRequests","expectedCacheKeys","values","getURLsToCacheKeys","getCachedURLs","getIntegrityForCacheKey","matchPrecache","createHandlerBoundToURL","getOrCreatePrecacheController","removeIgnoredSearchParams","ignoreURLParametersMatching","generateURLVariations","directoryIndex","cleanURLs","urlManipulation","hash","urlWithoutIgnoredParams","endsWith","directoryURL","cleanURL","additionalURLs","urlToAttempt","PrecacheRoute","urlsToCacheKeys","possibleURL","addRoute","precacheRoute","precacheAndRoute","SUBSTRING_TO_FIND","deleteOutdatedCaches","currentPrecacheName","substringToFind","cacheNamesToDelete","cleanupOutdatedCaches","cachesDeleted","NavigationRoute","allowlist","denylist","_match","_allowlist","_denylist","pathnameAndSearch","search"],"mappings":"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xH,MAAM,CAAc,YAAA,CAAA,GAC3D,wBAAwB,CAAC,CAAA;IACjC,aAAA;IACJ,WAAA;IACJ,SAAA;IACJ,OAAA;IACJ,KAAA;IACA,IAAA,OAAOsN,eAAe,CAAA;IAC1B,GAAA;IACJ;;ICngBA;IACA;AACA;IACA;IACA;IACA;IACA;IAOA;IACA;IACA;IACA;IACA;IACA,MAAMc,QAAQ,CAAC;IACX;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACItN,EAAAA,WAAWA,CAACsK,OAAO,GAAG,EAAE,EAAE;IACtB;IACR;IACA;IACA;IACA;IACA;IACA;QACQ,IAAI,CAAChL,SAAS,GAAGyI,UAAU,CAACM,cAAc,CAACiC,OAAO,CAAChL,SAAS,CAAC,CAAA;IAC7D;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAACuL,OAAO,GAAGP,OAAO,CAACO,OAAO,IAAI,EAAE,CAAA;IACpC;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAACe,YAAY,GAAGtB,OAAO,CAACsB,YAAY,CAAA;IACxC;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAAC5C,YAAY,GAAGsB,OAAO,CAACtB,YAAY,CAAA;IAC5C,GAAA;IACA;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;MACI7H,MAAMA,CAACmJ,OAAO,EAAE;QACZ,MAAM,CAACiD,YAAY,CAAC,GAAG,IAAI,CAACC,SAAS,CAAClD,OAAO,CAAC,CAAA;IAC9C,IAAA,OAAOiD,YAAY,CAAA;IACvB,GAAA;IACA;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;MACIC,SAASA,CAAClD,OAAO,EAAE;IACf;QACA,IAAIA,OAAO,YAAYY,UAAU,EAAE;I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"} \ No newline at end of file diff --git a/dev-dist/workbox-be0f3d84.js b/dev-dist/workbox-be0f3d84.js deleted file mode 100644 index c0124bd..0000000 --- a/dev-dist/workbox-be0f3d84.js +++ /dev/null @@ -1,3749 +0,0 @@ -define(['exports'], (function (exports) { 'use strict'; - - // @ts-ignore - try { - self['workbox:core:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const logger = (() => { - // Don't overwrite this value if it's already set. - // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923 - if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) { - self.__WB_DISABLE_DEV_LOGS = false; - } - let inGroup = false; - const methodToColorMap = { - debug: `#7f8c8d`, - log: `#2ecc71`, - warn: `#f39c12`, - error: `#c0392b`, - groupCollapsed: `#3498db`, - groupEnd: null // No colored prefix on groupEnd - }; - const print = function (method, args) { - if (self.__WB_DISABLE_DEV_LOGS) { - return; - } - if (method === 'groupCollapsed') { - // Safari doesn't print all console.groupCollapsed() arguments: - // https://bugs.webkit.org/show_bug.cgi?id=182754 - if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { - console[method](...args); - return; - } - } - const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`]; - // When in a group, the workbox prefix is not displayed. - const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')]; - console[method](...logPrefix, ...args); - if (method === 'groupCollapsed') { - inGroup = true; - } - if (method === 'groupEnd') { - inGroup = false; - } - }; - // eslint-disable-next-line @typescript-eslint/ban-types - const api = {}; - const loggerMethods = Object.keys(methodToColorMap); - for (const key of loggerMethods) { - const method = key; - api[method] = (...args) => { - print(method, args); - }; - } - return api; - })(); - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const messages$1 = { - 'invalid-value': ({ - paramName, - validValueDescription, - value - }) => { - if (!paramName || !validValueDescription) { - throw new Error(`Unexpected input to 'invalid-value' error.`); - } - return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`; - }, - 'not-an-array': ({ - moduleName, - className, - funcName, - paramName - }) => { - if (!moduleName || !className || !funcName || !paramName) { - throw new Error(`Unexpected input to 'not-an-array' error.`); - } - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`; - }, - 'incorrect-type': ({ - expectedType, - paramName, - moduleName, - className, - funcName - }) => { - if (!expectedType || !paramName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'incorrect-type' error.`); - } - const classNameStr = className ? `${className}.` : ''; - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}` + `${funcName}()' must be of type ${expectedType}.`; - }, - 'incorrect-class': ({ - expectedClassName, - paramName, - moduleName, - className, - funcName, - isReturnValueProblem - }) => { - if (!expectedClassName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'incorrect-class' error.`); - } - const classNameStr = className ? `${className}.` : ''; - if (isReturnValueProblem) { - return `The return value from ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`; - } - return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`; - }, - 'missing-a-method': ({ - expectedMethod, - paramName, - moduleName, - className, - funcName - }) => { - if (!expectedMethod || !paramName || !moduleName || !className || !funcName) { - throw new Error(`Unexpected input to 'missing-a-method' error.`); - } - return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`; - }, - 'add-to-cache-list-unexpected-type': ({ - entry - }) => { - return `An unexpected entry was passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` + `strings with one or more characters, objects with a url property or ` + `Request objects.`; - }, - 'add-to-cache-list-conflicting-entries': ({ - firstEntry, - secondEntry - }) => { - if (!firstEntry || !secondEntry) { - throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`); - } - return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${firstEntry} but different revision details. Workbox is ` + `unable to cache and version the asset correctly. Please remove one ` + `of the entries.`; - }, - 'plugin-error-request-will-fetch': ({ - thrownErrorMessage - }) => { - if (!thrownErrorMessage) { - throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`); - } - return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownErrorMessage}'.`; - }, - 'invalid-cache-name': ({ - cacheNameId, - value - }) => { - if (!cacheNameId) { - throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`); - } - return `You must provide a name containing at least one character for ` + `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`; - }, - 'unregister-route-but-not-found-with-method': ({ - method - }) => { - if (!method) { - throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`); - } - return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`; - }, - 'unregister-route-route-not-registered': () => { - return `The route you're trying to unregister was not previously ` + `registered.`; - }, - 'queue-replay-failed': ({ - name - }) => { - return `Replaying the background sync queue '${name}' failed.`; - }, - 'duplicate-queue-name': ({ - name - }) => { - return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`; - }, - 'expired-test-without-max-age': ({ - methodName, - paramName - }) => { - return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`; - }, - 'unsupported-route-type': ({ - moduleName, - className, - funcName, - paramName - }) => { - return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`; - }, - 'not-array-of-class': ({ - value, - expectedClass, - moduleName, - className, - funcName, - paramName - }) => { - return `The supplied '${paramName}' parameter must be an array of ` + `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` + `Please check the call to ${moduleName}.${className}.${funcName}() ` + `to fix the issue.`; - }, - 'max-entries-or-age-required': ({ - moduleName, - className, - funcName - }) => { - return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`; - }, - 'statuses-or-headers-required': ({ - moduleName, - className, - funcName - }) => { - return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`; - }, - 'invalid-string': ({ - moduleName, - funcName, - paramName - }) => { - if (!paramName || !moduleName || !funcName) { - throw new Error(`Unexpected input to 'invalid-string' error.`); - } - return `When using strings, the '${paramName}' parameter must start with ` + `'http' (for cross-origin matches) or '/' (for same-origin matches). ` + `Please see the docs for ${moduleName}.${funcName}() for ` + `more info.`; - }, - 'channel-name-required': () => { - return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`; - }, - 'invalid-responses-are-same-args': () => { - return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`; - }, - 'expire-custom-caches-only': () => { - return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`; - }, - 'unit-must-be-bytes': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`); - } - return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`; - }, - 'single-range-only': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'single-range-only' error.`); - } - return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`; - }, - 'invalid-range-values': ({ - normalizedRangeHeader - }) => { - if (!normalizedRangeHeader) { - throw new Error(`Unexpected input to 'invalid-range-values' error.`); - } - return `The Range header is missing both start and end values. At least ` + `one of those values is needed. The Range header provided was ` + `"${normalizedRangeHeader}"`; - }, - 'no-range-header': () => { - return `No Range header was found in the Request provided.`; - }, - 'range-not-satisfiable': ({ - size, - start, - end - }) => { - return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`; - }, - 'attempt-to-cache-non-get-request': ({ - url, - method - }) => { - return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`; - }, - 'cache-put-with-no-response': ({ - url - }) => { - return `There was an attempt to cache '${url}' but the response was not ` + `defined.`; - }, - 'no-response': ({ - url, - error - }) => { - let message = `The strategy could not generate a response for '${url}'.`; - if (error) { - message += ` The underlying error is ${error}.`; - } - return message; - }, - 'bad-precaching-response': ({ - url, - status - }) => { - return `The precaching request for '${url}' failed` + (status ? ` with an HTTP status of ${status}.` : `.`); - }, - 'non-precached-url': ({ - url - }) => { - return `createHandlerBoundToURL('${url}') was called, but that URL is ` + `not precached. Please pass in a URL that is precached instead.`; - }, - 'add-to-cache-list-conflicting-integrities': ({ - url - }) => { - return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${url} with different integrity values. Please remove one of them.`; - }, - 'missing-precache-entry': ({ - cacheName, - url - }) => { - return `Unable to find a precached response in ${cacheName} for ${url}.`; - }, - 'cross-origin-copy-response': ({ - origin - }) => { - return `workbox-core.copyResponse() can only be used with same-origin ` + `responses. It was passed a response with origin ${origin}.`; - }, - 'opaque-streams-source': ({ - type - }) => { - const message = `One of the workbox-streams sources resulted in an ` + `'${type}' response.`; - if (type === 'opaqueredirect') { - return `${message} Please do not use a navigation request that results ` + `in a redirect as a source.`; - } - return `${message} Please ensure your sources are CORS-enabled.`; - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const generatorFunction = (code, details = {}) => { - const message = messages$1[code]; - if (!message) { - throw new Error(`Unable to find message for code '${code}'.`); - } - return message(details); - }; - const messageGenerator = generatorFunction; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Workbox errors should be thrown with this class. - * This allows use to ensure the type easily in tests, - * helps developers identify errors from workbox - * easily and allows use to optimise error - * messages correctly. - * - * @private - */ - class WorkboxError extends Error { - /** - * - * @param {string} errorCode The error code that - * identifies this particular error. - * @param {Object=} details Any relevant arguments - * that will help developers identify issues should - * be added as a key on the context object. - */ - constructor(errorCode, details) { - const message = messageGenerator(errorCode, details); - super(message); - this.name = errorCode; - this.details = details; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /* - * This method throws if the supplied value is not an array. - * The destructed values are required to produce a meaningful error for users. - * The destructed and restructured object is so it's clear what is - * needed. - */ - const isArray = (value, details) => { - if (!Array.isArray(value)) { - throw new WorkboxError('not-an-array', details); - } - }; - const hasMethod = (object, expectedMethod, details) => { - const type = typeof object[expectedMethod]; - if (type !== 'function') { - details['expectedMethod'] = expectedMethod; - throw new WorkboxError('missing-a-method', details); - } - }; - const isType = (object, expectedType, details) => { - if (typeof object !== expectedType) { - details['expectedType'] = expectedType; - throw new WorkboxError('incorrect-type', details); - } - }; - const isInstance = (object, - // Need the general type to do the check later. - // eslint-disable-next-line @typescript-eslint/ban-types - expectedClass, details) => { - if (!(object instanceof expectedClass)) { - details['expectedClassName'] = expectedClass.name; - throw new WorkboxError('incorrect-class', details); - } - }; - const isOneOf = (value, validValues, details) => { - if (!validValues.includes(value)) { - details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`; - throw new WorkboxError('invalid-value', details); - } - }; - const isArrayOfClass = (value, - // Need general type to do check later. - expectedClass, - // eslint-disable-line - details) => { - const error = new WorkboxError('not-array-of-class', details); - if (!Array.isArray(value)) { - throw error; - } - for (const item of value) { - if (!(item instanceof expectedClass)) { - throw error; - } - } - }; - const finalAssertExports = { - hasMethod, - isArray, - isInstance, - isOneOf, - isType, - isArrayOfClass - }; - - // @ts-ignore - try { - self['workbox:routing:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The default HTTP method, 'GET', used when there's no specific method - * configured for a route. - * - * @type {string} - * - * @private - */ - const defaultMethod = 'GET'; - /** - * The list of valid HTTP methods associated with requests that could be routed. - * - * @type {Array<string>} - * - * @private - */ - const validMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {function()|Object} handler Either a function, or an object with a - * 'handle' method. - * @return {Object} An object with a handle method. - * - * @private - */ - const normalizeHandler = handler => { - if (handler && typeof handler === 'object') { - { - finalAssertExports.hasMethod(handler, 'handle', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'handler' - }); - } - return handler; - } else { - { - finalAssertExports.isType(handler, 'function', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'handler' - }); - } - return { - handle: handler - }; - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A `Route` consists of a pair of callback functions, "match" and "handler". - * The "match" callback determine if a route should be used to "handle" a - * request by returning a non-falsy value if it can. The "handler" callback - * is called when there is a match and should return a Promise that resolves - * to a `Response`. - * - * @memberof workbox-routing - */ - class Route { - /** - * Constructor for Route class. - * - * @param {workbox-routing~matchCallback} match - * A callback function that determines whether the route matches a given - * `fetch` event by returning a non-falsy value. - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resolving to a Response. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - */ - constructor(match, handler, method = defaultMethod) { - { - finalAssertExports.isType(match, 'function', { - moduleName: 'workbox-routing', - className: 'Route', - funcName: 'constructor', - paramName: 'match' - }); - if (method) { - finalAssertExports.isOneOf(method, validMethods, { - paramName: 'method' - }); - } - } - // These values are referenced directly by Router so cannot be - // altered by minificaton. - this.handler = normalizeHandler(handler); - this.match = match; - this.method = method; - } - /** - * - * @param {workbox-routing-handlerCallback} handler A callback - * function that returns a Promise resolving to a Response - */ - setCatchHandler(handler) { - this.catchHandler = normalizeHandler(handler); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * RegExpRoute makes it easy to create a regular expression based - * {@link workbox-routing.Route}. - * - * For same-origin requests the RegExp only needs to match part of the URL. For - * requests against third-party servers, you must define a RegExp that matches - * the start of the URL. - * - * @memberof workbox-routing - * @extends workbox-routing.Route - */ - class RegExpRoute extends Route { - /** - * If the regular expression contains - * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references}, - * the captured values will be passed to the - * {@link workbox-routing~handlerCallback} `params` - * argument. - * - * @param {RegExp} regExp The regular expression to match against URLs. - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - */ - constructor(regExp, handler, method) { - { - finalAssertExports.isInstance(regExp, RegExp, { - moduleName: 'workbox-routing', - className: 'RegExpRoute', - funcName: 'constructor', - paramName: 'pattern' - }); - } - const match = ({ - url - }) => { - const result = regExp.exec(url.href); - // Return immediately if there's no match. - if (!result) { - return; - } - // Require that the match start at the first character in the URL string - // if it's a cross-origin request. - // See https://github.com/GoogleChrome/workbox/issues/281 for the context - // behind this behavior. - if (url.origin !== location.origin && result.index !== 0) { - { - logger.debug(`The regular expression '${regExp.toString()}' only partially matched ` + `against the cross-origin URL '${url.toString()}'. RegExpRoute's will only ` + `handle cross-origin requests if they match the entire URL.`); - } - return; - } - // If the route matches, but there aren't any capture groups defined, then - // this will return [], which is truthy and therefore sufficient to - // indicate a match. - // If there are capture groups, then it will return their values. - return result.slice(1); - }; - super(match, handler, method); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const getFriendlyURL = url => { - const urlObj = new URL(String(url), location.href); - // See https://github.com/GoogleChrome/workbox/issues/2323 - // We want to include everything, except for the origin if it's same-origin. - return urlObj.href.replace(new RegExp(`^${location.origin}`), ''); - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The Router can be used to process a `FetchEvent` using one or more - * {@link workbox-routing.Route}, responding with a `Response` if - * a matching route exists. - * - * If no route matches a given a request, the Router will use a "default" - * handler if one is defined. - * - * Should the matching Route throw an error, the Router will use a "catch" - * handler if one is defined to gracefully deal with issues and respond with a - * Request. - * - * If a request matches multiple routes, the **earliest** registered route will - * be used to respond to the request. - * - * @memberof workbox-routing - */ - class Router { - /** - * Initializes a new Router. - */ - constructor() { - this._routes = new Map(); - this._defaultHandlerMap = new Map(); - } - /** - * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP - * method name ('GET', etc.) to an array of all the corresponding `Route` - * instances that are registered. - */ - get routes() { - return this._routes; - } - /** - * Adds a fetch event listener to respond to events when a route matches - * the event's request. - */ - addFetchListener() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('fetch', event => { - const { - request - } = event; - const responsePromise = this.handleRequest({ - request, - event - }); - if (responsePromise) { - event.respondWith(responsePromise); - } - }); - } - /** - * Adds a message event listener for URLs to cache from the window. - * This is useful to cache resources loaded on the page prior to when the - * service worker started controlling it. - * - * The format of the message data sent from the window should be as follows. - * Where the `urlsToCache` array may consist of URL strings or an array of - * URL string + `requestInit` object (the same as you'd pass to `fetch()`). - * - * ``` - * { - * type: 'CACHE_URLS', - * payload: { - * urlsToCache: [ - * './script1.js', - * './script2.js', - * ['./script3.js', {mode: 'no-cors'}], - * ], - * }, - * } - * ``` - */ - addCacheListener() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('message', event => { - // event.data is type 'any' - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (event.data && event.data.type === 'CACHE_URLS') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { - payload - } = event.data; - { - logger.debug(`Caching URLs from the window`, payload.urlsToCache); - } - const requestPromises = Promise.all(payload.urlsToCache.map(entry => { - if (typeof entry === 'string') { - entry = [entry]; - } - const request = new Request(...entry); - return this.handleRequest({ - request, - event - }); - // TODO(philipwalton): TypeScript errors without this typecast for - // some reason (probably a bug). The real type here should work but - // doesn't: `Array<Promise<Response> | undefined>`. - })); // TypeScript - event.waitUntil(requestPromises); - // If a MessageChannel was used, reply to the message on success. - if (event.ports && event.ports[0]) { - void requestPromises.then(() => event.ports[0].postMessage(true)); - } - } - }); - } - /** - * Apply the routing rules to a FetchEvent object to get a Response from an - * appropriate Route's handler. - * - * @param {Object} options - * @param {Request} options.request The request to handle. - * @param {ExtendableEvent} options.event The event that triggered the - * request. - * @return {Promise<Response>|undefined} A promise is returned if a - * registered route can handle the request. If there is no matching - * route and there's no `defaultHandler`, `undefined` is returned. - */ - handleRequest({ - request, - event - }) { - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'handleRequest', - paramName: 'options.request' - }); - } - const url = new URL(request.url, location.href); - if (!url.protocol.startsWith('http')) { - { - logger.debug(`Workbox Router only supports URLs that start with 'http'.`); - } - return; - } - const sameOrigin = url.origin === location.origin; - const { - params, - route - } = this.findMatchingRoute({ - event, - request, - sameOrigin, - url - }); - let handler = route && route.handler; - const debugMessages = []; - { - if (handler) { - debugMessages.push([`Found a route to handle this request:`, route]); - if (params) { - debugMessages.push([`Passing the following params to the route's handler:`, params]); - } - } - } - // If we don't have a handler because there was no matching route, then - // fall back to defaultHandler if that's defined. - const method = request.method; - if (!handler && this._defaultHandlerMap.has(method)) { - { - debugMessages.push(`Failed to find a matching route. Falling ` + `back to the default handler for ${method}.`); - } - handler = this._defaultHandlerMap.get(method); - } - if (!handler) { - { - // No handler so Workbox will do nothing. If logs is set of debug - // i.e. verbose, we should print out this information. - logger.debug(`No route found for: ${getFriendlyURL(url)}`); - } - return; - } - { - // We have a handler, meaning Workbox is going to handle the route. - // print the routing details to the console. - logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`); - debugMessages.forEach(msg => { - if (Array.isArray(msg)) { - logger.log(...msg); - } else { - logger.log(msg); - } - }); - logger.groupEnd(); - } - // Wrap in try and catch in case the handle method throws a synchronous - // error. It should still callback to the catch handler. - let responsePromise; - try { - responsePromise = handler.handle({ - url, - request, - event, - params - }); - } catch (err) { - responsePromise = Promise.reject(err); - } - // Get route's catch handler, if it exists - const catchHandler = route && route.catchHandler; - if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) { - responsePromise = responsePromise.catch(async err => { - // If there's a route catch handler, process that first - if (catchHandler) { - { - // Still include URL here as it will be async from the console group - // and may not make sense without the URL - logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`); - logger.error(`Error thrown by:`, route); - logger.error(err); - logger.groupEnd(); - } - try { - return await catchHandler.handle({ - url, - request, - event, - params - }); - } catch (catchErr) { - if (catchErr instanceof Error) { - err = catchErr; - } - } - } - if (this._catchHandler) { - { - // Still include URL here as it will be async from the console group - // and may not make sense without the URL - logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`); - logger.error(`Error thrown by:`, route); - logger.error(err); - logger.groupEnd(); - } - return this._catchHandler.handle({ - url, - request, - event - }); - } - throw err; - }); - } - return responsePromise; - } - /** - * Checks a request and URL (and optionally an event) against the list of - * registered routes, and if there's a match, returns the corresponding - * route along with any params generated by the match. - * - * @param {Object} options - * @param {URL} options.url - * @param {boolean} options.sameOrigin The result of comparing `url.origin` - * against the current origin. - * @param {Request} options.request The request to match. - * @param {Event} options.event The corresponding event. - * @return {Object} An object with `route` and `params` properties. - * They are populated if a matching route was found or `undefined` - * otherwise. - */ - findMatchingRoute({ - url, - sameOrigin, - request, - event - }) { - const routes = this._routes.get(request.method) || []; - for (const route of routes) { - let params; - // route.match returns type any, not possible to change right now. - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const matchResult = route.match({ - url, - sameOrigin, - request, - event - }); - if (matchResult) { - { - // Warn developers that using an async matchCallback is almost always - // not the right thing to do. - if (matchResult instanceof Promise) { - logger.warn(`While routing ${getFriendlyURL(url)}, an async ` + `matchCallback function was used. Please convert the ` + `following route to use a synchronous matchCallback function:`, route); - } - } - // See https://github.com/GoogleChrome/workbox/issues/2079 - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - params = matchResult; - if (Array.isArray(params) && params.length === 0) { - // Instead of passing an empty array in as params, use undefined. - params = undefined; - } else if (matchResult.constructor === Object && - // eslint-disable-line - Object.keys(matchResult).length === 0) { - // Instead of passing an empty object in as params, use undefined. - params = undefined; - } else if (typeof matchResult === 'boolean') { - // For the boolean value true (rather than just something truth-y), - // don't set params. - // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353 - params = undefined; - } - // Return early if have a match. - return { - route, - params - }; - } - } - // If no match was found above, return and empty object. - return {}; - } - /** - * Define a default `handler` that's called when no routes explicitly - * match the incoming request. - * - * Each HTTP method ('GET', 'POST', etc.) gets its own default handler. - * - * Without a default handler, unmatched requests will go against the - * network as if there were no service worker present. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {string} [method='GET'] The HTTP method to associate with this - * default handler. Each method has its own default. - */ - setDefaultHandler(handler, method = defaultMethod) { - this._defaultHandlerMap.set(method, normalizeHandler(handler)); - } - /** - * If a Route throws an error while handling a request, this `handler` - * will be called and given a chance to provide a response. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - */ - setCatchHandler(handler) { - this._catchHandler = normalizeHandler(handler); - } - /** - * Registers a route with the router. - * - * @param {workbox-routing.Route} route The route to register. - */ - registerRoute(route) { - { - finalAssertExports.isType(route, 'object', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.hasMethod(route, 'match', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.isType(route.handler, 'object', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route' - }); - finalAssertExports.hasMethod(route.handler, 'handle', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route.handler' - }); - finalAssertExports.isType(route.method, 'string', { - moduleName: 'workbox-routing', - className: 'Router', - funcName: 'registerRoute', - paramName: 'route.method' - }); - } - if (!this._routes.has(route.method)) { - this._routes.set(route.method, []); - } - // Give precedence to all of the earlier routes by adding this additional - // route to the end of the array. - this._routes.get(route.method).push(route); - } - /** - * Unregisters a route with the router. - * - * @param {workbox-routing.Route} route The route to unregister. - */ - unregisterRoute(route) { - if (!this._routes.has(route.method)) { - throw new WorkboxError('unregister-route-but-not-found-with-method', { - method: route.method - }); - } - const routeIndex = this._routes.get(route.method).indexOf(route); - if (routeIndex > -1) { - this._routes.get(route.method).splice(routeIndex, 1); - } else { - throw new WorkboxError('unregister-route-route-not-registered'); - } - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let defaultRouter; - /** - * Creates a new, singleton Router instance if one does not exist. If one - * does already exist, that instance is returned. - * - * @private - * @return {Router} - */ - const getOrCreateDefaultRouter = () => { - if (!defaultRouter) { - defaultRouter = new Router(); - // The helpers that use the default Router assume these listeners exist. - defaultRouter.addFetchListener(); - defaultRouter.addCacheListener(); - } - return defaultRouter; - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Easily register a RegExp, string, or function with a caching - * strategy to a singleton Router instance. - * - * This method will generate a Route for you if needed and - * call {@link workbox-routing.Router#registerRoute}. - * - * @param {RegExp|string|workbox-routing.Route~matchCallback|workbox-routing.Route} capture - * If the capture param is a `Route`, all other arguments will be ignored. - * @param {workbox-routing~handlerCallback} [handler] A callback - * function that returns a Promise resulting in a Response. This parameter - * is required if `capture` is not a `Route` object. - * @param {string} [method='GET'] The HTTP method to match the Route - * against. - * @return {workbox-routing.Route} The generated `Route`. - * - * @memberof workbox-routing - */ - function registerRoute(capture, handler, method) { - let route; - if (typeof capture === 'string') { - const captureUrl = new URL(capture, location.href); - { - if (!(capture.startsWith('/') || capture.startsWith('http'))) { - throw new WorkboxError('invalid-string', { - moduleName: 'workbox-routing', - funcName: 'registerRoute', - paramName: 'capture' - }); - } - // We want to check if Express-style wildcards are in the pathname only. - // TODO: Remove this log message in v4. - const valueToCheck = capture.startsWith('http') ? captureUrl.pathname : capture; - // See https://github.com/pillarjs/path-to-regexp#parameters - const wildcards = '[*:?+]'; - if (new RegExp(`${wildcards}`).exec(valueToCheck)) { - logger.debug(`The '$capture' parameter contains an Express-style wildcard ` + `character (${wildcards}). Strings are now always interpreted as ` + `exact matches; use a RegExp for partial or wildcard matches.`); - } - } - const matchCallback = ({ - url - }) => { - { - if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) { - logger.debug(`${capture} only partially matches the cross-origin URL ` + `${url.toString()}. This route will only handle cross-origin requests ` + `if they match the entire URL.`); - } - } - return url.href === captureUrl.href; - }; - // If `capture` is a string then `handler` and `method` must be present. - route = new Route(matchCallback, handler, method); - } else if (capture instanceof RegExp) { - // If `capture` is a `RegExp` then `handler` and `method` must be present. - route = new RegExpRoute(capture, handler, method); - } else if (typeof capture === 'function') { - // If `capture` is a function then `handler` and `method` must be present. - route = new Route(capture, handler, method); - } else if (capture instanceof Route) { - route = capture; - } else { - throw new WorkboxError('unsupported-route-type', { - moduleName: 'workbox-routing', - funcName: 'registerRoute', - paramName: 'capture' - }); - } - const defaultRouter = getOrCreateDefaultRouter(); - defaultRouter.registerRoute(route); - return route; - } - - // @ts-ignore - try { - self['workbox:cacheable-response:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * This class allows you to set up rules determining what - * status codes and/or headers need to be present in order for a - * [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) - * to be considered cacheable. - * - * @memberof workbox-cacheable-response - */ - class CacheableResponse { - /** - * To construct a new CacheableResponse instance you must provide at least - * one of the `config` properties. - * - * If both `statuses` and `headers` are specified, then both conditions must - * be met for the `Response` to be considered cacheable. - * - * @param {Object} config - * @param {Array<number>} [config.statuses] One or more status codes that a - * `Response` can have and be considered cacheable. - * @param {Object<string,string>} [config.headers] A mapping of header names - * and expected values that a `Response` can have and be considered cacheable. - * If multiple headers are provided, only one needs to be present. - */ - constructor(config = {}) { - { - if (!(config.statuses || config.headers)) { - throw new WorkboxError('statuses-or-headers-required', { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor' - }); - } - if (config.statuses) { - finalAssertExports.isArray(config.statuses, { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor', - paramName: 'config.statuses' - }); - } - if (config.headers) { - finalAssertExports.isType(config.headers, 'object', { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'constructor', - paramName: 'config.headers' - }); - } - } - this._statuses = config.statuses; - this._headers = config.headers; - } - /** - * Checks a response to see whether it's cacheable or not, based on this - * object's configuration. - * - * @param {Response} response The response whose cacheability is being - * checked. - * @return {boolean} `true` if the `Response` is cacheable, and `false` - * otherwise. - */ - isResponseCacheable(response) { - { - finalAssertExports.isInstance(response, Response, { - moduleName: 'workbox-cacheable-response', - className: 'CacheableResponse', - funcName: 'isResponseCacheable', - paramName: 'response' - }); - } - let cacheable = true; - if (this._statuses) { - cacheable = this._statuses.includes(response.status); - } - if (this._headers && cacheable) { - cacheable = Object.keys(this._headers).some(headerName => { - return response.headers.get(headerName) === this._headers[headerName]; - }); - } - { - if (!cacheable) { - logger.groupCollapsed(`The request for ` + `'${getFriendlyURL(response.url)}' returned a response that does ` + `not meet the criteria for being cached.`); - logger.groupCollapsed(`View cacheability criteria here.`); - logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses)); - logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2)); - logger.groupEnd(); - const logFriendlyHeaders = {}; - response.headers.forEach((value, key) => { - logFriendlyHeaders[key] = value; - }); - logger.groupCollapsed(`View response status and headers here.`); - logger.log(`Response status: ${response.status}`); - logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2)); - logger.groupEnd(); - logger.groupCollapsed(`View full response details here.`); - logger.log(response.headers); - logger.log(response); - logger.groupEnd(); - logger.groupEnd(); - } - } - return cacheable; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A class implementing the `cacheWillUpdate` lifecycle callback. This makes it - * easier to add in cacheability checks to requests made via Workbox's built-in - * strategies. - * - * @memberof workbox-cacheable-response - */ - class CacheableResponsePlugin { - /** - * To construct a new CacheableResponsePlugin instance you must provide at - * least one of the `config` properties. - * - * If both `statuses` and `headers` are specified, then both conditions must - * be met for the `Response` to be considered cacheable. - * - * @param {Object} config - * @param {Array<number>} [config.statuses] One or more status codes that a - * `Response` can have and be considered cacheable. - * @param {Object<string,string>} [config.headers] A mapping of header names - * and expected values that a `Response` can have and be considered cacheable. - * If multiple headers are provided, only one needs to be present. - */ - constructor(config) { - /** - * @param {Object} options - * @param {Response} options.response - * @return {Response|null} - * @private - */ - this.cacheWillUpdate = async ({ - response - }) => { - if (this._cacheableResponse.isResponseCacheable(response)) { - return response; - } - return null; - }; - this._cacheableResponse = new CacheableResponse(config); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const _cacheNameDetails = { - googleAnalytics: 'googleAnalytics', - precache: 'precache-v2', - prefix: 'workbox', - runtime: 'runtime', - suffix: typeof registration !== 'undefined' ? registration.scope : '' - }; - const _createCacheName = cacheName => { - return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value && value.length > 0).join('-'); - }; - const eachCacheNameDetail = fn => { - for (const key of Object.keys(_cacheNameDetails)) { - fn(key); - } - }; - const cacheNames = { - updateDetails: details => { - eachCacheNameDetail(key => { - if (typeof details[key] === 'string') { - _cacheNameDetails[key] = details[key]; - } - }); - }, - getGoogleAnalyticsName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics); - }, - getPrecacheName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.precache); - }, - getPrefix: () => { - return _cacheNameDetails.prefix; - }, - getRuntimeName: userCacheName => { - return userCacheName || _createCacheName(_cacheNameDetails.runtime); - }, - getSuffix: () => { - return _cacheNameDetails.suffix; - } - }; - - /* - Copyright 2020 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - function stripParams(fullURL, ignoreParams) { - const strippedURL = new URL(fullURL); - for (const param of ignoreParams) { - strippedURL.searchParams.delete(param); - } - return strippedURL.href; - } - /** - * Matches an item in the cache, ignoring specific URL params. This is similar - * to the `ignoreSearch` option, but it allows you to ignore just specific - * params (while continuing to match on the others). - * - * @private - * @param {Cache} cache - * @param {Request} request - * @param {Object} matchOptions - * @param {Array<string>} ignoreParams - * @return {Promise<Response|undefined>} - */ - async function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) { - const strippedRequestURL = stripParams(request.url, ignoreParams); - // If the request doesn't include any ignored params, match as normal. - if (request.url === strippedRequestURL) { - return cache.match(request, matchOptions); - } - // Otherwise, match by comparing keys - const keysOptions = Object.assign(Object.assign({}, matchOptions), { - ignoreSearch: true - }); - const cacheKeys = await cache.keys(request, keysOptions); - for (const cacheKey of cacheKeys) { - const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams); - if (strippedRequestURL === strippedCacheKeyURL) { - return cache.match(cacheKey, matchOptions); - } - } - return; - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * The Deferred class composes Promises in a way that allows for them to be - * resolved or rejected from outside the constructor. In most cases promises - * should be used directly, but Deferreds can be necessary when the logic to - * resolve a promise must be separate. - * - * @private - */ - class Deferred { - /** - * Creates a promise and exposes its resolve and reject functions as methods. - */ - constructor() { - this.promise = new Promise((resolve, reject) => { - this.resolve = resolve; - this.reject = reject; - }); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - // Callbacks to be executed whenever there's a quota error. - // Can't change Function type right now. - // eslint-disable-next-line @typescript-eslint/ban-types - const quotaErrorCallbacks = new Set(); - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Runs all of the callback functions, one at a time sequentially, in the order - * in which they were registered. - * - * @memberof workbox-core - * @private - */ - async function executeQuotaErrorCallbacks() { - { - logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`); - } - for (const callback of quotaErrorCallbacks) { - await callback(); - { - logger.log(callback, 'is complete.'); - } - } - { - logger.log('Finished running callbacks.'); - } - } - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Returns a promise that resolves and the passed number of milliseconds. - * This utility is an async/await-friendly version of `setTimeout`. - * - * @param {number} ms - * @return {Promise} - * @private - */ - function timeout(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); - } - - // @ts-ignore - try { - self['workbox:strategies:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - function toRequest(input) { - return typeof input === 'string' ? new Request(input) : input; - } - /** - * A class created every time a Strategy instance instance calls - * {@link workbox-strategies.Strategy~handle} or - * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and - * cache actions around plugin callbacks and keeps track of when the strategy - * is "done" (i.e. all added `event.waitUntil()` promises have resolved). - * - * @memberof workbox-strategies - */ - class StrategyHandler { - /** - * Creates a new instance associated with the passed strategy and event - * that's handling the request. - * - * The constructor also initializes the state that will be passed to each of - * the plugins handling this request. - * - * @param {workbox-strategies.Strategy} strategy - * @param {Object} options - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] The return value from the - * {@link workbox-routing~matchCallback} (if applicable). - */ - constructor(strategy, options) { - this._cacheKeys = {}; - /** - * The request the strategy is performing (passed to the strategy's - * `handle()` or `handleAll()` method). - * @name request - * @instance - * @type {Request} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * The event associated with this request. - * @name event - * @instance - * @type {ExtendableEvent} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * A `URL` instance of `request.url` (if passed to the strategy's - * `handle()` or `handleAll()` method). - * Note: the `url` param will be present if the strategy was invoked - * from a workbox `Route` object. - * @name url - * @instance - * @type {URL|undefined} - * @memberof workbox-strategies.StrategyHandler - */ - /** - * A `param` value (if passed to the strategy's - * `handle()` or `handleAll()` method). - * Note: the `param` param will be present if the strategy was invoked - * from a workbox `Route` object and the - * {@link workbox-routing~matchCallback} returned - * a truthy value (it will be that value). - * @name params - * @instance - * @type {*|undefined} - * @memberof workbox-strategies.StrategyHandler - */ - { - finalAssertExports.isInstance(options.event, ExtendableEvent, { - moduleName: 'workbox-strategies', - className: 'StrategyHandler', - funcName: 'constructor', - paramName: 'options.event' - }); - } - Object.assign(this, options); - this.event = options.event; - this._strategy = strategy; - this._handlerDeferred = new Deferred(); - this._extendLifetimePromises = []; - // Copy the plugins list (since it's mutable on the strategy), - // so any mutations don't affect this handler instance. - this._plugins = [...strategy.plugins]; - this._pluginStateMap = new Map(); - for (const plugin of this._plugins) { - this._pluginStateMap.set(plugin, {}); - } - this.event.waitUntil(this._handlerDeferred.promise); - } - /** - * Fetches a given request (and invokes any applicable plugin callback - * methods) using the `fetchOptions` (for non-navigation requests) and - * `plugins` defined on the `Strategy` object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - `requestWillFetch()` - * - `fetchDidSucceed()` - * - `fetchDidFail()` - * - * @param {Request|string} input The URL or request to fetch. - * @return {Promise<Response>} - */ - async fetch(input) { - const { - event - } = this; - let request = toRequest(input); - if (request.mode === 'navigate' && event instanceof FetchEvent && event.preloadResponse) { - const possiblePreloadResponse = await event.preloadResponse; - if (possiblePreloadResponse) { - { - logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`); - } - return possiblePreloadResponse; - } - } - // If there is a fetchDidFail plugin, we need to save a clone of the - // original request before it's either modified by a requestWillFetch - // plugin or before the original request's body is consumed via fetch(). - const originalRequest = this.hasCallback('fetchDidFail') ? request.clone() : null; - try { - for (const cb of this.iterateCallbacks('requestWillFetch')) { - request = await cb({ - request: request.clone(), - event - }); - } - } catch (err) { - if (err instanceof Error) { - throw new WorkboxError('plugin-error-request-will-fetch', { - thrownErrorMessage: err.message - }); - } - } - // The request can be altered by plugins with `requestWillFetch` making - // the original request (most likely from a `fetch` event) different - // from the Request we make. Pass both to `fetchDidFail` to aid debugging. - const pluginFilteredRequest = request.clone(); - try { - let fetchResponse; - // See https://github.com/GoogleChrome/workbox/issues/1796 - fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions); - if ("development" !== 'production') { - logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`); - } - for (const callback of this.iterateCallbacks('fetchDidSucceed')) { - fetchResponse = await callback({ - event, - request: pluginFilteredRequest, - response: fetchResponse - }); - } - return fetchResponse; - } catch (error) { - { - logger.log(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error); - } - // `originalRequest` will only exist if a `fetchDidFail` callback - // is being used (see above). - if (originalRequest) { - await this.runCallbacks('fetchDidFail', { - error: error, - event, - originalRequest: originalRequest.clone(), - request: pluginFilteredRequest.clone() - }); - } - throw error; - } - } - /** - * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on - * the response generated by `this.fetch()`. - * - * The call to `this.cachePut()` automatically invokes `this.waitUntil()`, - * so you do not have to manually call `waitUntil()` on the event. - * - * @param {Request|string} input The request or URL to fetch and cache. - * @return {Promise<Response>} - */ - async fetchAndCachePut(input) { - const response = await this.fetch(input); - const responseClone = response.clone(); - void this.waitUntil(this.cachePut(input, responseClone)); - return response; - } - /** - * Matches a request from the cache (and invokes any applicable plugin - * callback methods) using the `cacheName`, `matchOptions`, and `plugins` - * defined on the strategy object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - cacheKeyWillBeUsed() - * - cachedResponseWillBeUsed() - * - * @param {Request|string} key The Request or URL to use as the cache key. - * @return {Promise<Response|undefined>} A matching response, if found. - */ - async cacheMatch(key) { - const request = toRequest(key); - let cachedResponse; - const { - cacheName, - matchOptions - } = this._strategy; - const effectiveRequest = await this.getCacheKey(request, 'read'); - const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), { - cacheName - }); - cachedResponse = await caches.match(effectiveRequest, multiMatchOptions); - { - if (cachedResponse) { - logger.debug(`Found a cached response in '${cacheName}'.`); - } else { - logger.debug(`No cached response found in '${cacheName}'.`); - } - } - for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) { - cachedResponse = (await callback({ - cacheName, - matchOptions, - cachedResponse, - request: effectiveRequest, - event: this.event - })) || undefined; - } - return cachedResponse; - } - /** - * Puts a request/response pair in the cache (and invokes any applicable - * plugin callback methods) using the `cacheName` and `plugins` defined on - * the strategy object. - * - * The following plugin lifecycle methods are invoked when using this method: - * - cacheKeyWillBeUsed() - * - cacheWillUpdate() - * - cacheDidUpdate() - * - * @param {Request|string} key The request or URL to use as the cache key. - * @param {Response} response The response to cache. - * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response - * not be cached, and `true` otherwise. - */ - async cachePut(key, response) { - const request = toRequest(key); - // Run in the next task to avoid blocking other cache reads. - // https://github.com/w3c/ServiceWorker/issues/1397 - await timeout(0); - const effectiveRequest = await this.getCacheKey(request, 'write'); - { - if (effectiveRequest.method && effectiveRequest.method !== 'GET') { - throw new WorkboxError('attempt-to-cache-non-get-request', { - url: getFriendlyURL(effectiveRequest.url), - method: effectiveRequest.method - }); - } - // See https://github.com/GoogleChrome/workbox/issues/2818 - const vary = response.headers.get('Vary'); - if (vary) { - logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` + `has a 'Vary: ${vary}' header. ` + `Consider setting the {ignoreVary: true} option on your strategy ` + `to ensure cache matching and deletion works as expected.`); - } - } - if (!response) { - { - logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`); - } - throw new WorkboxError('cache-put-with-no-response', { - url: getFriendlyURL(effectiveRequest.url) - }); - } - const responseToCache = await this._ensureResponseSafeToCache(response); - if (!responseToCache) { - { - logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` + `will not be cached.`, responseToCache); - } - return false; - } - const { - cacheName, - matchOptions - } = this._strategy; - const cache = await self.caches.open(cacheName); - const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate'); - const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams( - // TODO(philipwalton): the `__WB_REVISION__` param is a precaching - // feature. Consider into ways to only add this behavior if using - // precaching. - cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions) : null; - { - logger.debug(`Updating the '${cacheName}' cache with a new Response ` + `for ${getFriendlyURL(effectiveRequest.url)}.`); - } - try { - await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache); - } catch (error) { - if (error instanceof Error) { - // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError - if (error.name === 'QuotaExceededError') { - await executeQuotaErrorCallbacks(); - } - throw error; - } - } - for (const callback of this.iterateCallbacks('cacheDidUpdate')) { - await callback({ - cacheName, - oldResponse, - newResponse: responseToCache.clone(), - request: effectiveRequest, - event: this.event - }); - } - return true; - } - /** - * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and - * executes any of those callbacks found in sequence. The final `Request` - * object returned by the last plugin is treated as the cache key for cache - * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have - * been registered, the passed request is returned unmodified - * - * @param {Request} request - * @param {string} mode - * @return {Promise<Request>} - */ - async getCacheKey(request, mode) { - const key = `${request.url} | ${mode}`; - if (!this._cacheKeys[key]) { - let effectiveRequest = request; - for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) { - effectiveRequest = toRequest(await callback({ - mode, - request: effectiveRequest, - event: this.event, - // params has a type any can't change right now. - params: this.params // eslint-disable-line - })); - } - this._cacheKeys[key] = effectiveRequest; - } - return this._cacheKeys[key]; - } - /** - * Returns true if the strategy has at least one plugin with the given - * callback. - * - * @param {string} name The name of the callback to check for. - * @return {boolean} - */ - hasCallback(name) { - for (const plugin of this._strategy.plugins) { - if (name in plugin) { - return true; - } - } - return false; - } - /** - * Runs all plugin callbacks matching the given name, in order, passing the - * given param object (merged ith the current plugin state) as the only - * argument. - * - * Note: since this method runs all plugins, it's not suitable for cases - * where the return value of a callback needs to be applied prior to calling - * the next callback. See - * {@link workbox-strategies.StrategyHandler#iterateCallbacks} - * below for how to handle that case. - * - * @param {string} name The name of the callback to run within each plugin. - * @param {Object} param The object to pass as the first (and only) param - * when executing each callback. This object will be merged with the - * current plugin state prior to callback execution. - */ - async runCallbacks(name, param) { - for (const callback of this.iterateCallbacks(name)) { - // TODO(philipwalton): not sure why `any` is needed. It seems like - // this should work with `as WorkboxPluginCallbackParam[C]`. - await callback(param); - } - } - /** - * Accepts a callback and returns an iterable of matching plugin callbacks, - * where each callback is wrapped with the current handler state (i.e. when - * you call each callback, whatever object parameter you pass it will - * be merged with the plugin's current state). - * - * @param {string} name The name fo the callback to run - * @return {Array<Function>} - */ - *iterateCallbacks(name) { - for (const plugin of this._strategy.plugins) { - if (typeof plugin[name] === 'function') { - const state = this._pluginStateMap.get(plugin); - const statefulCallback = param => { - const statefulParam = Object.assign(Object.assign({}, param), { - state - }); - // TODO(philipwalton): not sure why `any` is needed. It seems like - // this should work with `as WorkboxPluginCallbackParam[C]`. - return plugin[name](statefulParam); - }; - yield statefulCallback; - } - } - } - /** - * Adds a promise to the - * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises} - * of the event event associated with the request being handled (usually a - * `FetchEvent`). - * - * Note: you can await - * {@link workbox-strategies.StrategyHandler~doneWaiting} - * to know when all added promises have settled. - * - * @param {Promise} promise A promise to add to the extend lifetime promises - * of the event that triggered the request. - */ - waitUntil(promise) { - this._extendLifetimePromises.push(promise); - return promise; - } - /** - * Returns a promise that resolves once all promises passed to - * {@link workbox-strategies.StrategyHandler~waitUntil} - * have settled. - * - * Note: any work done after `doneWaiting()` settles should be manually - * passed to an event's `waitUntil()` method (not this handler's - * `waitUntil()` method), otherwise the service worker thread my be killed - * prior to your work completing. - */ - async doneWaiting() { - let promise; - while (promise = this._extendLifetimePromises.shift()) { - await promise; - } - } - /** - * Stops running the strategy and immediately resolves any pending - * `waitUntil()` promises. - */ - destroy() { - this._handlerDeferred.resolve(null); - } - /** - * This method will call cacheWillUpdate on the available plugins (or use - * status === 200) to determine if the Response is safe and valid to cache. - * - * @param {Request} options.request - * @param {Response} options.response - * @return {Promise<Response|undefined>} - * - * @private - */ - async _ensureResponseSafeToCache(response) { - let responseToCache = response; - let pluginsUsed = false; - for (const callback of this.iterateCallbacks('cacheWillUpdate')) { - responseToCache = (await callback({ - request: this.request, - response: responseToCache, - event: this.event - })) || undefined; - pluginsUsed = true; - if (!responseToCache) { - break; - } - } - if (!pluginsUsed) { - if (responseToCache && responseToCache.status !== 200) { - responseToCache = undefined; - } - { - if (responseToCache) { - if (responseToCache.status !== 200) { - if (responseToCache.status === 0) { - logger.warn(`The response for '${this.request.url}' ` + `is an opaque response. The caching strategy that you're ` + `using will not cache opaque responses by default.`); - } else { - logger.debug(`The response for '${this.request.url}' ` + `returned a status code of '${response.status}' and won't ` + `be cached as a result.`); - } - } - } - } - } - return responseToCache; - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An abstract base class that all other strategy classes must extend from: - * - * @memberof workbox-strategies - */ - class Strategy { - /** - * Creates a new instance of the strategy and sets all documented option - * properties as public instance properties. - * - * Note: if a custom strategy class extends the base Strategy class and does - * not need more than these properties, it does not need to define its own - * constructor. - * - * @param {Object} [options] - * @param {string} [options.cacheName] Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796) - * `fetch()` requests made by this strategy. - * @param {Object} [options.matchOptions] The - * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - */ - constructor(options = {}) { - /** - * Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * - * @type {string} - */ - this.cacheName = cacheNames.getRuntimeName(options.cacheName); - /** - * The list - * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * used by this strategy. - * - * @type {Array<Object>} - */ - this.plugins = options.plugins || []; - /** - * Values passed along to the - * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters} - * of all fetch() requests made by this strategy. - * - * @type {Object} - */ - this.fetchOptions = options.fetchOptions; - /** - * The - * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - * - * @type {Object} - */ - this.matchOptions = options.matchOptions; - } - /** - * Perform a request strategy and returns a `Promise` that will resolve with - * a `Response`, invoking all relevant plugin callbacks. - * - * When a strategy instance is registered with a Workbox - * {@link workbox-routing.Route}, this method is automatically - * called when the route matches. - * - * Alternatively, this method can be used in a standalone `FetchEvent` - * listener by passing it to `event.respondWith()`. - * - * @param {FetchEvent|Object} options A `FetchEvent` or an object with the - * properties listed below. - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] - */ - handle(options) { - const [responseDone] = this.handleAll(options); - return responseDone; - } - /** - * Similar to {@link workbox-strategies.Strategy~handle}, but - * instead of just returning a `Promise` that resolves to a `Response` it - * it will return an tuple of `[response, done]` promises, where the former - * (`response`) is equivalent to what `handle()` returns, and the latter is a - * Promise that will resolve once any promises that were added to - * `event.waitUntil()` as part of performing the strategy have completed. - * - * You can await the `done` promise to ensure any extra work performed by - * the strategy (usually caching responses) completes successfully. - * - * @param {FetchEvent|Object} options A `FetchEvent` or an object with the - * properties listed below. - * @param {Request|string} options.request A request to run this strategy for. - * @param {ExtendableEvent} options.event The event associated with the - * request. - * @param {URL} [options.url] - * @param {*} [options.params] - * @return {Array<Promise>} A tuple of [response, done] - * promises that can be used to determine when the response resolves as - * well as when the handler has completed all its work. - */ - handleAll(options) { - // Allow for flexible options to be passed. - if (options instanceof FetchEvent) { - options = { - event: options, - request: options.request - }; - } - const event = options.event; - const request = typeof options.request === 'string' ? new Request(options.request) : options.request; - const params = 'params' in options ? options.params : undefined; - const handler = new StrategyHandler(this, { - event, - request, - params - }); - const responseDone = this._getResponse(handler, request, event); - const handlerDone = this._awaitComplete(responseDone, handler, request, event); - // Return an array of promises, suitable for use with Promise.all(). - return [responseDone, handlerDone]; - } - async _getResponse(handler, request, event) { - await handler.runCallbacks('handlerWillStart', { - event, - request - }); - let response = undefined; - try { - response = await this._handle(request, handler); - // The "official" Strategy subclasses all throw this error automatically, - // but in case a third-party Strategy doesn't, ensure that we have a - // consistent failure when there's no response or an error response. - if (!response || response.type === 'error') { - throw new WorkboxError('no-response', { - url: request.url - }); - } - } catch (error) { - if (error instanceof Error) { - for (const callback of handler.iterateCallbacks('handlerDidError')) { - response = await callback({ - error, - event, - request - }); - if (response) { - break; - } - } - } - if (!response) { - throw error; - } else { - logger.log(`While responding to '${getFriendlyURL(request.url)}', ` + `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` + `a handlerDidError plugin.`); - } - } - for (const callback of handler.iterateCallbacks('handlerWillRespond')) { - response = await callback({ - event, - request, - response - }); - } - return response; - } - async _awaitComplete(responseDone, handler, request, event) { - let response; - let error; - try { - response = await responseDone; - } catch (error) { - // Ignore errors, as response errors should be caught via the `response` - // promise above. The `done` promise will only throw for errors in - // promises passed to `handler.waitUntil()`. - } - try { - await handler.runCallbacks('handlerDidRespond', { - event, - request, - response - }); - await handler.doneWaiting(); - } catch (waitUntilError) { - if (waitUntilError instanceof Error) { - error = waitUntilError; - } - } - await handler.runCallbacks('handlerDidComplete', { - event, - request, - response, - error: error - }); - handler.destroy(); - if (error) { - throw error; - } - } - } - /** - * Classes extending the `Strategy` based class should implement this method, - * and leverage the {@link workbox-strategies.StrategyHandler} - * arg to perform all fetching and cache logic, which will ensure all relevant - * cache, cache options, fetch options and plugins are used (per the current - * strategy instance). - * - * @name _handle - * @instance - * @abstract - * @function - * @param {Request} request - * @param {workbox-strategies.StrategyHandler} handler - * @return {Promise<Response>} - * - * @memberof workbox-strategies.Strategy - */ - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const messages = { - strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`, - printFinalResponse: response => { - if (response) { - logger.groupCollapsed(`View the final response here.`); - logger.log(response || '[No response returned]'); - logger.groupEnd(); - } - } - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network) - * request strategy. - * - * A cache first strategy is useful for assets that have been revisioned, - * such as URLs like `/styles/example.a8f5f1.css`, since they - * can be cached for long periods of time. - * - * If the network request fails, and there is no cache match, this will throw - * a `WorkboxError` exception. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-strategies - */ - class CacheFirst extends Strategy { - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - const logs = []; - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-strategies', - className: this.constructor.name, - funcName: 'makeRequest', - paramName: 'request' - }); - } - let response = await handler.cacheMatch(request); - let error = undefined; - if (!response) { - { - logs.push(`No response found in the '${this.cacheName}' cache. ` + `Will respond with a network request.`); - } - try { - response = await handler.fetchAndCachePut(request); - } catch (err) { - if (err instanceof Error) { - error = err; - } - } - { - if (response) { - logs.push(`Got response from network.`); - } else { - logs.push(`Unable to get a response from the network.`); - } - } - } else { - { - logs.push(`Found a cached response in the '${this.cacheName}' cache.`); - } - } - { - logger.groupCollapsed(messages.strategyStart(this.constructor.name, request)); - for (const log of logs) { - logger.log(log); - } - messages.printFinalResponse(response); - logger.groupEnd(); - } - if (!response) { - throw new WorkboxError('no-response', { - url: request.url, - error - }); - } - return response; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * An implementation of a - * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only) - * request strategy. - * - * This class is useful if you want to take advantage of any - * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/). - * - * If the network request fails, this will throw a `WorkboxError` exception. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-strategies - */ - class NetworkOnly extends Strategy { - /** - * @param {Object} [options] - * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) - * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796) - * `fetch()` requests made by this strategy. - * @param {number} [options.networkTimeoutSeconds] If set, any network requests - * that fail to respond within the timeout will result in a network error. - */ - constructor(options = {}) { - super(options); - this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0; - } - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - { - finalAssertExports.isInstance(request, Request, { - moduleName: 'workbox-strategies', - className: this.constructor.name, - funcName: '_handle', - paramName: 'request' - }); - } - let error = undefined; - let response; - try { - const promises = [handler.fetch(request)]; - if (this._networkTimeoutSeconds) { - const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000); - promises.push(timeoutPromise); - } - response = await Promise.race(promises); - if (!response) { - throw new Error(`Timed out the network response after ` + `${this._networkTimeoutSeconds} seconds.`); - } - } catch (err) { - if (err instanceof Error) { - error = err; - } - } - { - logger.groupCollapsed(messages.strategyStart(this.constructor.name, request)); - if (response) { - logger.log(`Got response from network.`); - } else { - logger.log(`Unable to get a response from the network.`); - } - messages.printFinalResponse(response); - logger.groupEnd(); - } - if (!response) { - throw new WorkboxError('no-response', { - url: request.url, - error - }); - } - return response; - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Claim any currently available clients once the service worker - * becomes active. This is normally used in conjunction with `skipWaiting()`. - * - * @memberof workbox-core - */ - function clientsClaim() { - self.addEventListener('activate', () => self.clients.claim()); - } - - /* - Copyright 2020 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A utility method that makes it easier to use `event.waitUntil` with - * async functions and return the result. - * - * @param {ExtendableEvent} event - * @param {Function} asyncFn - * @return {Function} - * @private - */ - function waitUntil(event, asyncFn) { - const returnPromise = asyncFn(); - event.waitUntil(returnPromise); - return returnPromise; - } - - // @ts-ignore - try { - self['workbox:precaching:7.2.0'] && _(); - } catch (e) {} - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - // Name of the search parameter used to store revision info. - const REVISION_SEARCH_PARAM = '__WB_REVISION__'; - /** - * Converts a manifest entry into a versioned URL suitable for precaching. - * - * @param {Object|string} entry - * @return {string} A URL with versioning info. - * - * @private - * @memberof workbox-precaching - */ - function createCacheKey(entry) { - if (!entry) { - throw new WorkboxError('add-to-cache-list-unexpected-type', { - entry - }); - } - // If a precache manifest entry is a string, it's assumed to be a versioned - // URL, like '/app.abcd1234.js'. Return as-is. - if (typeof entry === 'string') { - const urlObject = new URL(entry, location.href); - return { - cacheKey: urlObject.href, - url: urlObject.href - }; - } - const { - revision, - url - } = entry; - if (!url) { - throw new WorkboxError('add-to-cache-list-unexpected-type', { - entry - }); - } - // If there's just a URL and no revision, then it's also assumed to be a - // versioned URL. - if (!revision) { - const urlObject = new URL(url, location.href); - return { - cacheKey: urlObject.href, - url: urlObject.href - }; - } - // Otherwise, construct a properly versioned URL using the custom Workbox - // search parameter along with the revision info. - const cacheKeyURL = new URL(url, location.href); - const originalURL = new URL(url, location.href); - cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision); - return { - cacheKey: cacheKeyURL.href, - url: originalURL.href - }; - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A plugin, designed to be used with PrecacheController, to determine the - * of assets that were updated (or not updated) during the install event. - * - * @private - */ - class PrecacheInstallReportPlugin { - constructor() { - this.updatedURLs = []; - this.notUpdatedURLs = []; - this.handlerWillStart = async ({ - request, - state - }) => { - // TODO: `state` should never be undefined... - if (state) { - state.originalRequest = request; - } - }; - this.cachedResponseWillBeUsed = async ({ - event, - state, - cachedResponse - }) => { - if (event.type === 'install') { - if (state && state.originalRequest && state.originalRequest instanceof Request) { - // TODO: `state` should never be undefined... - const url = state.originalRequest.url; - if (cachedResponse) { - this.notUpdatedURLs.push(url); - } else { - this.updatedURLs.push(url); - } - } - } - return cachedResponse; - }; - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A plugin, designed to be used with PrecacheController, to translate URLs into - * the corresponding cache key, based on the current revision info. - * - * @private - */ - class PrecacheCacheKeyPlugin { - constructor({ - precacheController - }) { - this.cacheKeyWillBeUsed = async ({ - request, - params - }) => { - // Params is type any, can't change right now. - /* eslint-disable */ - const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) || this._precacheController.getCacheKeyForURL(request.url); - /* eslint-enable */ - return cacheKey ? new Request(cacheKey, { - headers: request.headers - }) : request; - }; - this._precacheController = precacheController; - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {string} groupTitle - * @param {Array<string>} deletedURLs - * - * @private - */ - const logGroup = (groupTitle, deletedURLs) => { - logger.groupCollapsed(groupTitle); - for (const url of deletedURLs) { - logger.log(url); - } - logger.groupEnd(); - }; - /** - * @param {Array<string>} deletedURLs - * - * @private - * @memberof workbox-precaching - */ - function printCleanupDetails(deletedURLs) { - const deletionCount = deletedURLs.length; - if (deletionCount > 0) { - logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`); - logGroup('Deleted Cache Requests', deletedURLs); - logger.groupEnd(); - } - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * @param {string} groupTitle - * @param {Array<string>} urls - * - * @private - */ - function _nestedGroup(groupTitle, urls) { - if (urls.length === 0) { - return; - } - logger.groupCollapsed(groupTitle); - for (const url of urls) { - logger.log(url); - } - logger.groupEnd(); - } - /** - * @param {Array<string>} urlsToPrecache - * @param {Array<string>} urlsAlreadyPrecached - * - * @private - * @memberof workbox-precaching - */ - function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) { - const precachedCount = urlsToPrecache.length; - const alreadyPrecachedCount = urlsAlreadyPrecached.length; - if (precachedCount || alreadyPrecachedCount) { - let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`; - if (alreadyPrecachedCount > 0) { - message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`; - } - logger.groupCollapsed(message); - _nestedGroup(`View newly precached URLs.`, urlsToPrecache); - _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached); - logger.groupEnd(); - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let supportStatus; - /** - * A utility function that determines whether the current browser supports - * constructing a new `Response` from a `response.body` stream. - * - * @return {boolean} `true`, if the current browser can successfully - * construct a `Response` from a `response.body` stream, `false` otherwise. - * - * @private - */ - function canConstructResponseFromBodyStream() { - if (supportStatus === undefined) { - const testResponse = new Response(''); - if ('body' in testResponse) { - try { - new Response(testResponse.body); - supportStatus = true; - } catch (error) { - supportStatus = false; - } - } - supportStatus = false; - } - return supportStatus; - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Allows developers to copy a response and modify its `headers`, `status`, - * or `statusText` values (the values settable via a - * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax} - * object in the constructor). - * To modify these values, pass a function as the second argument. That - * function will be invoked with a single object with the response properties - * `{headers, status, statusText}`. The return value of this function will - * be used as the `ResponseInit` for the new `Response`. To change the values - * either modify the passed parameter(s) and return it, or return a totally - * new object. - * - * This method is intentionally limited to same-origin responses, regardless of - * whether CORS was used or not. - * - * @param {Response} response - * @param {Function} modifier - * @memberof workbox-core - */ - async function copyResponse(response, modifier) { - let origin = null; - // If response.url isn't set, assume it's cross-origin and keep origin null. - if (response.url) { - const responseURL = new URL(response.url); - origin = responseURL.origin; - } - if (origin !== self.location.origin) { - throw new WorkboxError('cross-origin-copy-response', { - origin - }); - } - const clonedResponse = response.clone(); - // Create a fresh `ResponseInit` object by cloning the headers. - const responseInit = { - headers: new Headers(clonedResponse.headers), - status: clonedResponse.status, - statusText: clonedResponse.statusText - }; - // Apply any user modifications. - const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit; - // Create the new response from the body stream and `ResponseInit` - // modifications. Note: not all browsers support the Response.body stream, - // so fall back to reading the entire body into memory as a blob. - const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob(); - return new Response(body, modifiedResponseInit); - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A {@link workbox-strategies.Strategy} implementation - * specifically designed to work with - * {@link workbox-precaching.PrecacheController} - * to both cache and fetch precached assets. - * - * Note: an instance of this class is created automatically when creating a - * `PrecacheController`; it's generally not necessary to create this yourself. - * - * @extends workbox-strategies.Strategy - * @memberof workbox-precaching - */ - class PrecacheStrategy extends Strategy { - /** - * - * @param {Object} [options] - * @param {string} [options.cacheName] Cache name to store and retrieve - * requests. Defaults to the cache names provided by - * {@link workbox-core.cacheNames}. - * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins} - * to use in conjunction with this caching strategy. - * @param {Object} [options.fetchOptions] Values passed along to the - * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init} - * of all fetch() requests made by this strategy. - * @param {Object} [options.matchOptions] The - * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions} - * for any `cache.match()` or `cache.put()` calls made by this strategy. - * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to - * get the response from the network if there's a precache miss. - */ - constructor(options = {}) { - options.cacheName = cacheNames.getPrecacheName(options.cacheName); - super(options); - this._fallbackToNetwork = options.fallbackToNetwork === false ? false : true; - // Redirected responses cannot be used to satisfy a navigation request, so - // any redirected response must be "copied" rather than cloned, so the new - // response doesn't contain the `redirected` flag. See: - // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1 - this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin); - } - /** - * @private - * @param {Request|string} request A request to run this strategy for. - * @param {workbox-strategies.StrategyHandler} handler The event that - * triggered the request. - * @return {Promise<Response>} - */ - async _handle(request, handler) { - const response = await handler.cacheMatch(request); - if (response) { - return response; - } - // If this is an `install` event for an entry that isn't already cached, - // then populate the cache. - if (handler.event && handler.event.type === 'install') { - return await this._handleInstall(request, handler); - } - // Getting here means something went wrong. An entry that should have been - // precached wasn't found in the cache. - return await this._handleFetch(request, handler); - } - async _handleFetch(request, handler) { - let response; - const params = handler.params || {}; - // Fall back to the network if we're configured to do so. - if (this._fallbackToNetwork) { - { - logger.warn(`The precached response for ` + `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` + `found. Falling back to the network.`); - } - const integrityInManifest = params.integrity; - const integrityInRequest = request.integrity; - const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest; - // Do not add integrity if the original request is no-cors - // See https://github.com/GoogleChrome/workbox/issues/3096 - response = await handler.fetch(new Request(request, { - integrity: request.mode !== 'no-cors' ? integrityInRequest || integrityInManifest : undefined - })); - // It's only "safe" to repair the cache if we're using SRI to guarantee - // that the response matches the precache manifest's expectations, - // and there's either a) no integrity property in the incoming request - // or b) there is an integrity, and it matches the precache manifest. - // See https://github.com/GoogleChrome/workbox/issues/2858 - // Also if the original request users no-cors we don't use integrity. - // See https://github.com/GoogleChrome/workbox/issues/3096 - if (integrityInManifest && noIntegrityConflict && request.mode !== 'no-cors') { - this._useDefaultCacheabilityPluginIfNeeded(); - const wasCached = await handler.cachePut(request, response.clone()); - { - if (wasCached) { - logger.log(`A response for ${getFriendlyURL(request.url)} ` + `was used to "repair" the precache.`); - } - } - } - } else { - // This shouldn't normally happen, but there are edge cases: - // https://github.com/GoogleChrome/workbox/issues/1441 - throw new WorkboxError('missing-precache-entry', { - cacheName: this.cacheName, - url: request.url - }); - } - { - const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read')); - // Workbox is going to handle the route. - // print the routing details to the console. - logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL(request.url)); - logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`); - logger.groupCollapsed(`View request details here.`); - logger.log(request); - logger.groupEnd(); - logger.groupCollapsed(`View response details here.`); - logger.log(response); - logger.groupEnd(); - logger.groupEnd(); - } - return response; - } - async _handleInstall(request, handler) { - this._useDefaultCacheabilityPluginIfNeeded(); - const response = await handler.fetch(request); - // Make sure we defer cachePut() until after we know the response - // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737 - const wasCached = await handler.cachePut(request, response.clone()); - if (!wasCached) { - // Throwing here will lead to the `install` handler failing, which - // we want to do if *any* of the responses aren't safe to cache. - throw new WorkboxError('bad-precaching-response', { - url: request.url, - status: response.status - }); - } - return response; - } - /** - * This method is complex, as there a number of things to account for: - * - * The `plugins` array can be set at construction, and/or it might be added to - * to at any time before the strategy is used. - * - * At the time the strategy is used (i.e. during an `install` event), there - * needs to be at least one plugin that implements `cacheWillUpdate` in the - * array, other than `copyRedirectedCacheableResponsesPlugin`. - * - * - If this method is called and there are no suitable `cacheWillUpdate` - * plugins, we need to add `defaultPrecacheCacheabilityPlugin`. - * - * - If this method is called and there is exactly one `cacheWillUpdate`, then - * we don't have to do anything (this might be a previously added - * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin). - * - * - If this method is called and there is more than one `cacheWillUpdate`, - * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so, - * we need to remove it. (This situation is unlikely, but it could happen if - * the strategy is used multiple times, the first without a `cacheWillUpdate`, - * and then later on after manually adding a custom `cacheWillUpdate`.) - * - * See https://github.com/GoogleChrome/workbox/issues/2737 for more context. - * - * @private - */ - _useDefaultCacheabilityPluginIfNeeded() { - let defaultPluginIndex = null; - let cacheWillUpdatePluginCount = 0; - for (const [index, plugin] of this.plugins.entries()) { - // Ignore the copy redirected plugin when determining what to do. - if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) { - continue; - } - // Save the default plugin's index, in case it needs to be removed. - if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) { - defaultPluginIndex = index; - } - if (plugin.cacheWillUpdate) { - cacheWillUpdatePluginCount++; - } - } - if (cacheWillUpdatePluginCount === 0) { - this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin); - } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) { - // Only remove the default plugin; multiple custom plugins are allowed. - this.plugins.splice(defaultPluginIndex, 1); - } - // Nothing needs to be done if cacheWillUpdatePluginCount is 1 - } - } - PrecacheStrategy.defaultPrecacheCacheabilityPlugin = { - async cacheWillUpdate({ - response - }) { - if (!response || response.status >= 400) { - return null; - } - return response; - } - }; - PrecacheStrategy.copyRedirectedCacheableResponsesPlugin = { - async cacheWillUpdate({ - response - }) { - return response.redirected ? await copyResponse(response) : response; - } - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Performs efficient precaching of assets. - * - * @memberof workbox-precaching - */ - class PrecacheController { - /** - * Create a new PrecacheController. - * - * @param {Object} [options] - * @param {string} [options.cacheName] The cache to use for precaching. - * @param {string} [options.plugins] Plugins to use when precaching as well - * as responding to fetch events for precached assets. - * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to - * get the response from the network if there's a precache miss. - */ - constructor({ - cacheName, - plugins = [], - fallbackToNetwork = true - } = {}) { - this._urlsToCacheKeys = new Map(); - this._urlsToCacheModes = new Map(); - this._cacheKeysToIntegrities = new Map(); - this._strategy = new PrecacheStrategy({ - cacheName: cacheNames.getPrecacheName(cacheName), - plugins: [...plugins, new PrecacheCacheKeyPlugin({ - precacheController: this - })], - fallbackToNetwork - }); - // Bind the install and activate methods to the instance. - this.install = this.install.bind(this); - this.activate = this.activate.bind(this); - } - /** - * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and - * used to cache assets and respond to fetch events. - */ - get strategy() { - return this._strategy; - } - /** - * Adds items to the precache list, removing any duplicates and - * stores the files in the - * {@link workbox-core.cacheNames|"precache cache"} when the service - * worker installs. - * - * This method can be called multiple times. - * - * @param {Array<Object|string>} [entries=[]] Array of entries to precache. - */ - precache(entries) { - this.addToCacheList(entries); - if (!this._installAndActiveListenersAdded) { - self.addEventListener('install', this.install); - self.addEventListener('activate', this.activate); - this._installAndActiveListenersAdded = true; - } - } - /** - * This method will add items to the precache list, removing duplicates - * and ensuring the information is valid. - * - * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries - * Array of entries to precache. - */ - addToCacheList(entries) { - { - finalAssertExports.isArray(entries, { - moduleName: 'workbox-precaching', - className: 'PrecacheController', - funcName: 'addToCacheList', - paramName: 'entries' - }); - } - const urlsToWarnAbout = []; - for (const entry of entries) { - // See https://github.com/GoogleChrome/workbox/issues/2259 - if (typeof entry === 'string') { - urlsToWarnAbout.push(entry); - } else if (entry && entry.revision === undefined) { - urlsToWarnAbout.push(entry.url); - } - const { - cacheKey, - url - } = createCacheKey(entry); - const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default'; - if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) { - throw new WorkboxError('add-to-cache-list-conflicting-entries', { - firstEntry: this._urlsToCacheKeys.get(url), - secondEntry: cacheKey - }); - } - if (typeof entry !== 'string' && entry.integrity) { - if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) { - throw new WorkboxError('add-to-cache-list-conflicting-integrities', { - url - }); - } - this._cacheKeysToIntegrities.set(cacheKey, entry.integrity); - } - this._urlsToCacheKeys.set(url, cacheKey); - this._urlsToCacheModes.set(url, cacheMode); - if (urlsToWarnAbout.length > 0) { - const warningMessage = `Workbox is precaching URLs without revision ` + `info: ${urlsToWarnAbout.join(', ')}\nThis is generally NOT safe. ` + `Learn more at https://bit.ly/wb-precache`; - { - logger.warn(warningMessage); - } - } - } - } - /** - * Precaches new and updated assets. Call this method from the service worker - * install event. - * - * Note: this method calls `event.waitUntil()` for you, so you do not need - * to call it yourself in your event handlers. - * - * @param {ExtendableEvent} event - * @return {Promise<workbox-precaching.InstallResult>} - */ - install(event) { - // waitUntil returns Promise<any> - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return waitUntil(event, async () => { - const installReportPlugin = new PrecacheInstallReportPlugin(); - this.strategy.plugins.push(installReportPlugin); - // Cache entries one at a time. - // See https://github.com/GoogleChrome/workbox/issues/2528 - for (const [url, cacheKey] of this._urlsToCacheKeys) { - const integrity = this._cacheKeysToIntegrities.get(cacheKey); - const cacheMode = this._urlsToCacheModes.get(url); - const request = new Request(url, { - integrity, - cache: cacheMode, - credentials: 'same-origin' - }); - await Promise.all(this.strategy.handleAll({ - params: { - cacheKey - }, - request, - event - })); - } - const { - updatedURLs, - notUpdatedURLs - } = installReportPlugin; - { - printInstallDetails(updatedURLs, notUpdatedURLs); - } - return { - updatedURLs, - notUpdatedURLs - }; - }); - } - /** - * Deletes assets that are no longer present in the current precache manifest. - * Call this method from the service worker activate event. - * - * Note: this method calls `event.waitUntil()` for you, so you do not need - * to call it yourself in your event handlers. - * - * @param {ExtendableEvent} event - * @return {Promise<workbox-precaching.CleanupResult>} - */ - activate(event) { - // waitUntil returns Promise<any> - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return waitUntil(event, async () => { - const cache = await self.caches.open(this.strategy.cacheName); - const currentlyCachedRequests = await cache.keys(); - const expectedCacheKeys = new Set(this._urlsToCacheKeys.values()); - const deletedURLs = []; - for (const request of currentlyCachedRequests) { - if (!expectedCacheKeys.has(request.url)) { - await cache.delete(request); - deletedURLs.push(request.url); - } - } - { - printCleanupDetails(deletedURLs); - } - return { - deletedURLs - }; - }); - } - /** - * Returns a mapping of a precached URL to the corresponding cache key, taking - * into account the revision information for the URL. - * - * @return {Map<string, string>} A URL to cache key mapping. - */ - getURLsToCacheKeys() { - return this._urlsToCacheKeys; - } - /** - * Returns a list of all the URLs that have been precached by the current - * service worker. - * - * @return {Array<string>} The precached URLs. - */ - getCachedURLs() { - return [...this._urlsToCacheKeys.keys()]; - } - /** - * Returns the cache key used for storing a given URL. If that URL is - * unversioned, like `/index.html', then the cache key will be the original - * URL with a search parameter appended to it. - * - * @param {string} url A URL whose cache key you want to look up. - * @return {string} The versioned URL that corresponds to a cache key - * for the original URL, or undefined if that URL isn't precached. - */ - getCacheKeyForURL(url) { - const urlObject = new URL(url, location.href); - return this._urlsToCacheKeys.get(urlObject.href); - } - /** - * @param {string} url A cache key whose SRI you want to look up. - * @return {string} The subresource integrity associated with the cache key, - * or undefined if it's not set. - */ - getIntegrityForCacheKey(cacheKey) { - return this._cacheKeysToIntegrities.get(cacheKey); - } - /** - * This acts as a drop-in replacement for - * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) - * with the following differences: - * - * - It knows what the name of the precache is, and only checks in that cache. - * - It allows you to pass in an "original" URL without versioning parameters, - * and it will automatically look up the correct cache key for the currently - * active revision of that URL. - * - * E.g., `matchPrecache('index.html')` will find the correct precached - * response for the currently active service worker, even if the actual cache - * key is `'/index.html?__WB_REVISION__=1234abcd'`. - * - * @param {string|Request} request The key (without revisioning parameters) - * to look up in the precache. - * @return {Promise<Response|undefined>} - */ - async matchPrecache(request) { - const url = request instanceof Request ? request.url : request; - const cacheKey = this.getCacheKeyForURL(url); - if (cacheKey) { - const cache = await self.caches.open(this.strategy.cacheName); - return cache.match(cacheKey); - } - return undefined; - } - /** - * Returns a function that looks up `url` in the precache (taking into - * account revision information), and returns the corresponding `Response`. - * - * @param {string} url The precached URL which will be used to lookup the - * `Response`. - * @return {workbox-routing~handlerCallback} - */ - createHandlerBoundToURL(url) { - const cacheKey = this.getCacheKeyForURL(url); - if (!cacheKey) { - throw new WorkboxError('non-precached-url', { - url - }); - } - return options => { - options.request = new Request(url); - options.params = Object.assign({ - cacheKey - }, options.params); - return this.strategy.handle(options); - }; - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - let precacheController; - /** - * @return {PrecacheController} - * @private - */ - const getOrCreatePrecacheController = () => { - if (!precacheController) { - precacheController = new PrecacheController(); - } - return precacheController; - }; - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Removes any URL search parameters that should be ignored. - * - * @param {URL} urlObject The original URL. - * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against - * each search parameter name. Matches mean that the search parameter should be - * ignored. - * @return {URL} The URL with any ignored search parameters removed. - * - * @private - * @memberof workbox-precaching - */ - function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) { - // Convert the iterable into an array at the start of the loop to make sure - // deletion doesn't mess up iteration. - for (const paramName of [...urlObject.searchParams.keys()]) { - if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) { - urlObject.searchParams.delete(paramName); - } - } - return urlObject; - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Generator function that yields possible variations on the original URL to - * check, one at a time. - * - * @param {string} url - * @param {Object} options - * - * @private - * @memberof workbox-precaching - */ - function* generateURLVariations(url, { - ignoreURLParametersMatching = [/^utm_/, /^fbclid$/], - directoryIndex = 'index.html', - cleanURLs = true, - urlManipulation - } = {}) { - const urlObject = new URL(url, location.href); - urlObject.hash = ''; - yield urlObject.href; - const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching); - yield urlWithoutIgnoredParams.href; - if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) { - const directoryURL = new URL(urlWithoutIgnoredParams.href); - directoryURL.pathname += directoryIndex; - yield directoryURL.href; - } - if (cleanURLs) { - const cleanURL = new URL(urlWithoutIgnoredParams.href); - cleanURL.pathname += '.html'; - yield cleanURL.href; - } - if (urlManipulation) { - const additionalURLs = urlManipulation({ - url: urlObject - }); - for (const urlToAttempt of additionalURLs) { - yield urlToAttempt.href; - } - } - } - - /* - Copyright 2020 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * A subclass of {@link workbox-routing.Route} that takes a - * {@link workbox-precaching.PrecacheController} - * instance and uses it to match incoming requests and handle fetching - * responses from the precache. - * - * @memberof workbox-precaching - * @extends workbox-routing.Route - */ - class PrecacheRoute extends Route { - /** - * @param {PrecacheController} precacheController A `PrecacheController` - * instance used to both match requests and respond to fetch events. - * @param {Object} [options] Options to control how requests are matched - * against the list of precached URLs. - * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will - * check cache entries for a URLs ending with '/' to see if there is a hit when - * appending the `directoryIndex` value. - * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An - * array of regex's to remove search params when looking for a cache match. - * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will - * check the cache for the URL with a `.html` added to the end of the end. - * @param {workbox-precaching~urlManipulation} [options.urlManipulation] - * This is a function that should take a URL and return an array of - * alternative URLs that should be checked for precache matches. - */ - constructor(precacheController, options) { - const match = ({ - request - }) => { - const urlsToCacheKeys = precacheController.getURLsToCacheKeys(); - for (const possibleURL of generateURLVariations(request.url, options)) { - const cacheKey = urlsToCacheKeys.get(possibleURL); - if (cacheKey) { - const integrity = precacheController.getIntegrityForCacheKey(cacheKey); - return { - cacheKey, - integrity - }; - } - } - { - logger.debug(`Precaching did not find a match for ` + getFriendlyURL(request.url)); - } - return; - }; - super(match, precacheController.strategy); - } - } - - /* - Copyright 2019 Google LLC - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Add a `fetch` listener to the service worker that will - * respond to - * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests} - * with precached assets. - * - * Requests for assets that aren't precached, the `FetchEvent` will not be - * responded to, allowing the event to fall through to other `fetch` event - * listeners. - * - * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute} - * options. - * - * @memberof workbox-precaching - */ - function addRoute(options) { - const precacheController = getOrCreatePrecacheController(); - const precacheRoute = new PrecacheRoute(precacheController, options); - registerRoute(precacheRoute); - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Adds items to the precache list, removing any duplicates and - * stores the files in the - * {@link workbox-core.cacheNames|"precache cache"} when the service - * worker installs. - * - * This method can be called multiple times. - * - * Please note: This method **will not** serve any of the cached files for you. - * It only precaches files. To respond to a network request you call - * {@link workbox-precaching.addRoute}. - * - * If you have a single array of files to precache, you can just call - * {@link workbox-precaching.precacheAndRoute}. - * - * @param {Array<Object|string>} [entries=[]] Array of entries to precache. - * - * @memberof workbox-precaching - */ - function precache(entries) { - const precacheController = getOrCreatePrecacheController(); - precacheController.precache(entries); - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * This method will add entries to the precache list and add a route to - * respond to fetch events. - * - * This is a convenience method that will call - * {@link workbox-precaching.precache} and - * {@link workbox-precaching.addRoute} in a single call. - * - * @param {Array<Object|string>} entries Array of entries to precache. - * @param {Object} [options] See the - * {@link workbox-precaching.PrecacheRoute} options. - * - * @memberof workbox-precaching - */ - function precacheAndRoute(entries, options) { - precache(entries); - addRoute(options); - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - const SUBSTRING_TO_FIND = '-precache-'; - /** - * Cleans up incompatible precaches that were created by older versions of - * Workbox, by a service worker registered under the current scope. - * - * This is meant to be called as part of the `activate` event. - * - * This should be safe to use as long as you don't include `substringToFind` - * (defaulting to `-precache-`) in your non-precache cache names. - * - * @param {string} currentPrecacheName The cache name currently in use for - * precaching. This cache won't be deleted. - * @param {string} [substringToFind='-precache-'] Cache names which include this - * substring will be deleted (excluding `currentPrecacheName`). - * @return {Array<string>} A list of all the cache names that were deleted. - * - * @private - * @memberof workbox-precaching - */ - const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => { - const cacheNames = await self.caches.keys(); - const cacheNamesToDelete = cacheNames.filter(cacheName => { - return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName; - }); - await Promise.all(cacheNamesToDelete.map(cacheName => self.caches.delete(cacheName))); - return cacheNamesToDelete; - }; - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Adds an `activate` event listener which will clean up incompatible - * precaches that were created by older versions of Workbox. - * - * @memberof workbox-precaching - */ - function cleanupOutdatedCaches() { - // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705 - self.addEventListener('activate', event => { - const cacheName = cacheNames.getPrecacheName(); - event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => { - { - if (cachesDeleted.length > 0) { - logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted); - } - } - })); - }); - } - - /* - Copyright 2018 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * NavigationRoute makes it easy to create a - * {@link workbox-routing.Route} that matches for browser - * [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}. - * - * It will only match incoming Requests whose - * {@link https://fetch.spec.whatwg.org/#concept-request-mode|mode} - * is set to `navigate`. - * - * You can optionally only apply this route to a subset of navigation requests - * by using one or both of the `denylist` and `allowlist` parameters. - * - * @memberof workbox-routing - * @extends workbox-routing.Route - */ - class NavigationRoute extends Route { - /** - * If both `denylist` and `allowlist` are provided, the `denylist` will - * take precedence and the request will not match this route. - * - * The regular expressions in `allowlist` and `denylist` - * are matched against the concatenated - * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname} - * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search} - * portions of the requested URL. - * - * *Note*: These RegExps may be evaluated against every destination URL during - * a navigation. Avoid using - * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077), - * or else your users may see delays when navigating your site. - * - * @param {workbox-routing~handlerCallback} handler A callback - * function that returns a Promise resulting in a Response. - * @param {Object} options - * @param {Array<RegExp>} [options.denylist] If any of these patterns match, - * the route will not handle the request (even if a allowlist RegExp matches). - * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns - * match the URL's pathname and search parameter, the route will handle the - * request (assuming the denylist doesn't match). - */ - constructor(handler, { - allowlist = [/./], - denylist = [] - } = {}) { - { - finalAssertExports.isArrayOfClass(allowlist, RegExp, { - moduleName: 'workbox-routing', - className: 'NavigationRoute', - funcName: 'constructor', - paramName: 'options.allowlist' - }); - finalAssertExports.isArrayOfClass(denylist, RegExp, { - moduleName: 'workbox-routing', - className: 'NavigationRoute', - funcName: 'constructor', - paramName: 'options.denylist' - }); - } - super(options => this._match(options), handler); - this._allowlist = allowlist; - this._denylist = denylist; - } - /** - * Routes match handler. - * - * @param {Object} options - * @param {URL} options.url - * @param {Request} options.request - * @return {boolean} - * - * @private - */ - _match({ - url, - request - }) { - if (request && request.mode !== 'navigate') { - return false; - } - const pathnameAndSearch = url.pathname + url.search; - for (const regExp of this._denylist) { - if (regExp.test(pathnameAndSearch)) { - { - logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL matches this denylist pattern: ` + `${regExp.toString()}`); - } - return false; - } - } - if (this._allowlist.some(regExp => regExp.test(pathnameAndSearch))) { - { - logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`); - } - return true; - } - { - logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL being navigated to doesn't ` + `match the allowlist.`); - } - return false; - } - } - - /* - Copyright 2019 Google LLC - - Use of this source code is governed by an MIT-style - license that can be found in the LICENSE file or at - https://opensource.org/licenses/MIT. - */ - /** - * Helper function that calls - * {@link PrecacheController#createHandlerBoundToURL} on the default - * {@link PrecacheController} instance. - * - * If you are creating your own {@link PrecacheController}, then call the - * {@link PrecacheController#createHandlerBoundToURL} on that instance, - * instead of using this function. - * - * @param {string} url The precached URL which will be used to lookup the - * `Response`. - * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the - * response from the network if there's a precache miss. - * @return {workbox-routing~handlerCallback} - * - * @memberof workbox-precaching - */ - function createHandlerBoundToURL(url) { - const precacheController = getOrCreatePrecacheController(); - return precacheController.createHandlerBoundToURL(url); - } - - exports.CacheFirst = CacheFirst; - exports.CacheableResponsePlugin = CacheableResponsePlugin; - exports.NavigationRoute = NavigationRoute; - exports.NetworkOnly = NetworkOnly; - exports.cleanupOutdatedCaches = cleanupOutdatedCaches; - exports.clientsClaim = clientsClaim; - exports.createHandlerBoundToURL = createHandlerBoundToURL; - exports.precacheAndRoute = precacheAndRoute; - exports.registerRoute = registerRoute; - -})); -//# sourceMappingURL=workbox-be0f3d84.js.map diff --git a/dev-dist/workbox-be0f3d84.js.map b/dev-dist/workbox-be0f3d84.js.map deleted file mode 100644 index ed898b6..0000000 --- a/dev-dist/workbox-be0f3d84.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"workbox-be0f3d84.js","sources":["node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_version.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/logger.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/models/messages/messages.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/models/messages/messageGenerator.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/WorkboxError.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/assert.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/_version.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/utils/constants.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/utils/normalizeHandler.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/Route.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/RegExpRoute.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/getFriendlyURL.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/Router.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/utils/getOrCreateDefaultRouter.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/registerRoute.js","node_modules/.pnpm/workbox-cacheable-response@7.3.0/node_modules/workbox-cacheable-response/_version.js","node_modules/.pnpm/workbox-cacheable-response@7.3.0/node_modules/workbox-cacheable-response/CacheableResponse.js","node_modules/.pnpm/workbox-cacheable-response@7.3.0/node_modules/workbox-cacheable-response/CacheableResponsePlugin.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/cacheNames.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/cacheMatchIgnoreParams.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/Deferred.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/models/quotaErrorCallbacks.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/executeQuotaErrorCallbacks.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/timeout.js","node_modules/.pnpm/workbox-strategies@7.3.0/node_modules/workbox-strategies/_version.js","node_modules/.pnpm/workbox-strategies@7.3.0/node_modules/workbox-strategies/StrategyHandler.js","node_modules/.pnpm/workbox-strategies@7.3.0/node_modules/workbox-strategies/Strategy.js","node_modules/.pnpm/workbox-strategies@7.3.0/node_modules/workbox-strategies/utils/messages.js","node_modules/.pnpm/workbox-strategies@7.3.0/node_modules/workbox-strategies/CacheFirst.js","node_modules/.pnpm/workbox-strategies@7.3.0/node_modules/workbox-strategies/NetworkOnly.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/clientsClaim.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/waitUntil.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/_version.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/createCacheKey.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/PrecacheInstallReportPlugin.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/PrecacheCacheKeyPlugin.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/printCleanupDetails.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/printInstallDetails.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/_private/canConstructResponseFromBodyStream.js","node_modules/.pnpm/workbox-core@7.3.0/node_modules/workbox-core/copyResponse.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/PrecacheStrategy.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/PrecacheController.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/getOrCreatePrecacheController.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/removeIgnoredSearchParams.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/generateURLVariations.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/PrecacheRoute.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/addRoute.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/precache.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/precacheAndRoute.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/utils/deleteOutdatedCaches.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/cleanupOutdatedCaches.js","node_modules/.pnpm/workbox-routing@7.3.0/node_modules/workbox-routing/NavigationRoute.js","node_modules/.pnpm/workbox-precaching@7.3.0/node_modules/workbox-precaching/createHandlerBoundToURL.js"],"sourcesContent":["\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:core:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst logger = (process.env.NODE_ENV === 'production'\n ? null\n : (() => {\n // Don't overwrite this value if it's already set.\n // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923\n if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) {\n self.__WB_DISABLE_DEV_LOGS = false;\n }\n let inGroup = false;\n const methodToColorMap = {\n debug: `#7f8c8d`,\n log: `#2ecc71`,\n warn: `#f39c12`,\n error: `#c0392b`,\n groupCollapsed: `#3498db`,\n groupEnd: null, // No colored prefix on groupEnd\n };\n const print = function (method, args) {\n if (self.__WB_DISABLE_DEV_LOGS) {\n return;\n }\n if (method === 'groupCollapsed') {\n // Safari doesn't print all console.groupCollapsed() arguments:\n // https://bugs.webkit.org/show_bug.cgi?id=182754\n if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {\n console[method](...args);\n return;\n }\n }\n const styles = [\n `background: ${methodToColorMap[method]}`,\n `border-radius: 0.5em`,\n `color: white`,\n `font-weight: bold`,\n `padding: 2px 0.5em`,\n ];\n // When in a group, the workbox prefix is not displayed.\n const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];\n console[method](...logPrefix, ...args);\n if (method === 'groupCollapsed') {\n inGroup = true;\n }\n if (method === 'groupEnd') {\n inGroup = false;\n }\n };\n // eslint-disable-next-line @typescript-eslint/ban-types\n const api = {};\n const loggerMethods = Object.keys(methodToColorMap);\n for (const key of loggerMethods) {\n const method = key;\n api[method] = (...args) => {\n print(method, args);\n };\n }\n return api;\n })());\nexport { logger };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../../_version.js';\nexport const messages = {\n 'invalid-value': ({ paramName, validValueDescription, value }) => {\n if (!paramName || !validValueDescription) {\n throw new Error(`Unexpected input to 'invalid-value' error.`);\n }\n return (`The '${paramName}' parameter was given a value with an ` +\n `unexpected value. ${validValueDescription} Received a value of ` +\n `${JSON.stringify(value)}.`);\n },\n 'not-an-array': ({ moduleName, className, funcName, paramName }) => {\n if (!moduleName || !className || !funcName || !paramName) {\n throw new Error(`Unexpected input to 'not-an-array' error.`);\n }\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${className}.${funcName}()' must be an array.`);\n },\n 'incorrect-type': ({ expectedType, paramName, moduleName, className, funcName, }) => {\n if (!expectedType || !paramName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'incorrect-type' error.`);\n }\n const classNameStr = className ? `${className}.` : '';\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${classNameStr}` +\n `${funcName}()' must be of type ${expectedType}.`);\n },\n 'incorrect-class': ({ expectedClassName, paramName, moduleName, className, funcName, isReturnValueProblem, }) => {\n if (!expectedClassName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'incorrect-class' error.`);\n }\n const classNameStr = className ? `${className}.` : '';\n if (isReturnValueProblem) {\n return (`The return value from ` +\n `'${moduleName}.${classNameStr}${funcName}()' ` +\n `must be an instance of class ${expectedClassName}.`);\n }\n return (`The parameter '${paramName}' passed into ` +\n `'${moduleName}.${classNameStr}${funcName}()' ` +\n `must be an instance of class ${expectedClassName}.`);\n },\n 'missing-a-method': ({ expectedMethod, paramName, moduleName, className, funcName, }) => {\n if (!expectedMethod ||\n !paramName ||\n !moduleName ||\n !className ||\n !funcName) {\n throw new Error(`Unexpected input to 'missing-a-method' error.`);\n }\n return (`${moduleName}.${className}.${funcName}() expected the ` +\n `'${paramName}' parameter to expose a '${expectedMethod}' method.`);\n },\n 'add-to-cache-list-unexpected-type': ({ entry }) => {\n return (`An unexpected entry was passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` +\n `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` +\n `strings with one or more characters, objects with a url property or ` +\n `Request objects.`);\n },\n 'add-to-cache-list-conflicting-entries': ({ firstEntry, secondEntry }) => {\n if (!firstEntry || !secondEntry) {\n throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`);\n }\n return (`Two of the entries passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` +\n `${firstEntry} but different revision details. Workbox is ` +\n `unable to cache and version the asset correctly. Please remove one ` +\n `of the entries.`);\n },\n 'plugin-error-request-will-fetch': ({ thrownErrorMessage }) => {\n if (!thrownErrorMessage) {\n throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`);\n }\n return (`An error was thrown by a plugins 'requestWillFetch()' method. ` +\n `The thrown error message was: '${thrownErrorMessage}'.`);\n },\n 'invalid-cache-name': ({ cacheNameId, value }) => {\n if (!cacheNameId) {\n throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`);\n }\n return (`You must provide a name containing at least one character for ` +\n `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` +\n `'${JSON.stringify(value)}'`);\n },\n 'unregister-route-but-not-found-with-method': ({ method }) => {\n if (!method) {\n throw new Error(`Unexpected input to ` +\n `'unregister-route-but-not-found-with-method' error.`);\n }\n return (`The route you're trying to unregister was not previously ` +\n `registered for the method type '${method}'.`);\n },\n 'unregister-route-route-not-registered': () => {\n return (`The route you're trying to unregister was not previously ` +\n `registered.`);\n },\n 'queue-replay-failed': ({ name }) => {\n return `Replaying the background sync queue '${name}' failed.`;\n },\n 'duplicate-queue-name': ({ name }) => {\n return (`The Queue name '${name}' is already being used. ` +\n `All instances of backgroundSync.Queue must be given unique names.`);\n },\n 'expired-test-without-max-age': ({ methodName, paramName }) => {\n return (`The '${methodName}()' method can only be used when the ` +\n `'${paramName}' is used in the constructor.`);\n },\n 'unsupported-route-type': ({ moduleName, className, funcName, paramName }) => {\n return (`The supplied '${paramName}' parameter was an unsupported type. ` +\n `Please check the docs for ${moduleName}.${className}.${funcName} for ` +\n `valid input types.`);\n },\n 'not-array-of-class': ({ value, expectedClass, moduleName, className, funcName, paramName, }) => {\n return (`The supplied '${paramName}' parameter must be an array of ` +\n `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` +\n `Please check the call to ${moduleName}.${className}.${funcName}() ` +\n `to fix the issue.`);\n },\n 'max-entries-or-age-required': ({ moduleName, className, funcName }) => {\n return (`You must define either config.maxEntries or config.maxAgeSeconds` +\n `in ${moduleName}.${className}.${funcName}`);\n },\n 'statuses-or-headers-required': ({ moduleName, className, funcName }) => {\n return (`You must define either config.statuses or config.headers` +\n `in ${moduleName}.${className}.${funcName}`);\n },\n 'invalid-string': ({ moduleName, funcName, paramName }) => {\n if (!paramName || !moduleName || !funcName) {\n throw new Error(`Unexpected input to 'invalid-string' error.`);\n }\n return (`When using strings, the '${paramName}' parameter must start with ` +\n `'http' (for cross-origin matches) or '/' (for same-origin matches). ` +\n `Please see the docs for ${moduleName}.${funcName}() for ` +\n `more info.`);\n },\n 'channel-name-required': () => {\n return (`You must provide a channelName to construct a ` +\n `BroadcastCacheUpdate instance.`);\n },\n 'invalid-responses-are-same-args': () => {\n return (`The arguments passed into responsesAreSame() appear to be ` +\n `invalid. Please ensure valid Responses are used.`);\n },\n 'expire-custom-caches-only': () => {\n return (`You must provide a 'cacheName' property when using the ` +\n `expiration plugin with a runtime caching strategy.`);\n },\n 'unit-must-be-bytes': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`);\n }\n return (`The 'unit' portion of the Range header must be set to 'bytes'. ` +\n `The Range header provided was \"${normalizedRangeHeader}\"`);\n },\n 'single-range-only': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'single-range-only' error.`);\n }\n return (`Multiple ranges are not supported. Please use a single start ` +\n `value, and optional end value. The Range header provided was ` +\n `\"${normalizedRangeHeader}\"`);\n },\n 'invalid-range-values': ({ normalizedRangeHeader }) => {\n if (!normalizedRangeHeader) {\n throw new Error(`Unexpected input to 'invalid-range-values' error.`);\n }\n return (`The Range header is missing both start and end values. At least ` +\n `one of those values is needed. The Range header provided was ` +\n `\"${normalizedRangeHeader}\"`);\n },\n 'no-range-header': () => {\n return `No Range header was found in the Request provided.`;\n },\n 'range-not-satisfiable': ({ size, start, end }) => {\n return (`The start (${start}) and end (${end}) values in the Range are ` +\n `not satisfiable by the cached response, which is ${size} bytes.`);\n },\n 'attempt-to-cache-non-get-request': ({ url, method }) => {\n return (`Unable to cache '${url}' because it is a '${method}' request and ` +\n `only 'GET' requests can be cached.`);\n },\n 'cache-put-with-no-response': ({ url }) => {\n return (`There was an attempt to cache '${url}' but the response was not ` +\n `defined.`);\n },\n 'no-response': ({ url, error }) => {\n let message = `The strategy could not generate a response for '${url}'.`;\n if (error) {\n message += ` The underlying error is ${error}.`;\n }\n return message;\n },\n 'bad-precaching-response': ({ url, status }) => {\n return (`The precaching request for '${url}' failed` +\n (status ? ` with an HTTP status of ${status}.` : `.`));\n },\n 'non-precached-url': ({ url }) => {\n return (`createHandlerBoundToURL('${url}') was called, but that URL is ` +\n `not precached. Please pass in a URL that is precached instead.`);\n },\n 'add-to-cache-list-conflicting-integrities': ({ url }) => {\n return (`Two of the entries passed to ` +\n `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` +\n `${url} with different integrity values. Please remove one of them.`);\n },\n 'missing-precache-entry': ({ cacheName, url }) => {\n return `Unable to find a precached response in ${cacheName} for ${url}.`;\n },\n 'cross-origin-copy-response': ({ origin }) => {\n return (`workbox-core.copyResponse() can only be used with same-origin ` +\n `responses. It was passed a response with origin ${origin}.`);\n },\n 'opaque-streams-source': ({ type }) => {\n const message = `One of the workbox-streams sources resulted in an ` +\n `'${type}' response.`;\n if (type === 'opaqueredirect') {\n return (`${message} Please do not use a navigation request that results ` +\n `in a redirect as a source.`);\n }\n return `${message} Please ensure your sources are CORS-enabled.`;\n },\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { messages } from './messages.js';\nimport '../../_version.js';\nconst fallback = (code, ...args) => {\n let msg = code;\n if (args.length > 0) {\n msg += ` :: ${JSON.stringify(args)}`;\n }\n return msg;\n};\nconst generatorFunction = (code, details = {}) => {\n const message = messages[code];\n if (!message) {\n throw new Error(`Unable to find message for code '${code}'.`);\n }\n return message(details);\n};\nexport const messageGenerator = process.env.NODE_ENV === 'production' ? fallback : generatorFunction;\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { messageGenerator } from '../models/messages/messageGenerator.js';\nimport '../_version.js';\n/**\n * Workbox errors should be thrown with this class.\n * This allows use to ensure the type easily in tests,\n * helps developers identify errors from workbox\n * easily and allows use to optimise error\n * messages correctly.\n *\n * @private\n */\nclass WorkboxError extends Error {\n /**\n *\n * @param {string} errorCode The error code that\n * identifies this particular error.\n * @param {Object=} details Any relevant arguments\n * that will help developers identify issues should\n * be added as a key on the context object.\n */\n constructor(errorCode, details) {\n const message = messageGenerator(errorCode, details);\n super(message);\n this.name = errorCode;\n this.details = details;\n }\n}\nexport { WorkboxError };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { WorkboxError } from '../_private/WorkboxError.js';\nimport '../_version.js';\n/*\n * This method throws if the supplied value is not an array.\n * The destructed values are required to produce a meaningful error for users.\n * The destructed and restructured object is so it's clear what is\n * needed.\n */\nconst isArray = (value, details) => {\n if (!Array.isArray(value)) {\n throw new WorkboxError('not-an-array', details);\n }\n};\nconst hasMethod = (object, expectedMethod, details) => {\n const type = typeof object[expectedMethod];\n if (type !== 'function') {\n details['expectedMethod'] = expectedMethod;\n throw new WorkboxError('missing-a-method', details);\n }\n};\nconst isType = (object, expectedType, details) => {\n if (typeof object !== expectedType) {\n details['expectedType'] = expectedType;\n throw new WorkboxError('incorrect-type', details);\n }\n};\nconst isInstance = (object, \n// Need the general type to do the check later.\n// eslint-disable-next-line @typescript-eslint/ban-types\nexpectedClass, details) => {\n if (!(object instanceof expectedClass)) {\n details['expectedClassName'] = expectedClass.name;\n throw new WorkboxError('incorrect-class', details);\n }\n};\nconst isOneOf = (value, validValues, details) => {\n if (!validValues.includes(value)) {\n details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`;\n throw new WorkboxError('invalid-value', details);\n }\n};\nconst isArrayOfClass = (value, \n// Need general type to do check later.\nexpectedClass, // eslint-disable-line\ndetails) => {\n const error = new WorkboxError('not-array-of-class', details);\n if (!Array.isArray(value)) {\n throw error;\n }\n for (const item of value) {\n if (!(item instanceof expectedClass)) {\n throw error;\n }\n }\n};\nconst finalAssertExports = process.env.NODE_ENV === 'production'\n ? null\n : {\n hasMethod,\n isArray,\n isInstance,\n isOneOf,\n isType,\n isArrayOfClass,\n };\nexport { finalAssertExports as assert };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:routing:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * The default HTTP method, 'GET', used when there's no specific method\n * configured for a route.\n *\n * @type {string}\n *\n * @private\n */\nexport const defaultMethod = 'GET';\n/**\n * The list of valid HTTP methods associated with requests that could be routed.\n *\n * @type {Array<string>}\n *\n * @private\n */\nexport const validMethods = [\n 'DELETE',\n 'GET',\n 'HEAD',\n 'PATCH',\n 'POST',\n 'PUT',\n];\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport '../_version.js';\n/**\n * @param {function()|Object} handler Either a function, or an object with a\n * 'handle' method.\n * @return {Object} An object with a handle method.\n *\n * @private\n */\nexport const normalizeHandler = (handler) => {\n if (handler && typeof handler === 'object') {\n if (process.env.NODE_ENV !== 'production') {\n assert.hasMethod(handler, 'handle', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'handler',\n });\n }\n return handler;\n }\n else {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(handler, 'function', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'handler',\n });\n }\n return { handle: handler };\n }\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { defaultMethod, validMethods } from './utils/constants.js';\nimport { normalizeHandler } from './utils/normalizeHandler.js';\nimport './_version.js';\n/**\n * A `Route` consists of a pair of callback functions, \"match\" and \"handler\".\n * The \"match\" callback determine if a route should be used to \"handle\" a\n * request by returning a non-falsy value if it can. The \"handler\" callback\n * is called when there is a match and should return a Promise that resolves\n * to a `Response`.\n *\n * @memberof workbox-routing\n */\nclass Route {\n /**\n * Constructor for Route class.\n *\n * @param {workbox-routing~matchCallback} match\n * A callback function that determines whether the route matches a given\n * `fetch` event by returning a non-falsy value.\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resolving to a Response.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n */\n constructor(match, handler, method = defaultMethod) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(match, 'function', {\n moduleName: 'workbox-routing',\n className: 'Route',\n funcName: 'constructor',\n paramName: 'match',\n });\n if (method) {\n assert.isOneOf(method, validMethods, { paramName: 'method' });\n }\n }\n // These values are referenced directly by Router so cannot be\n // altered by minificaton.\n this.handler = normalizeHandler(handler);\n this.match = match;\n this.method = method;\n }\n /**\n *\n * @param {workbox-routing-handlerCallback} handler A callback\n * function that returns a Promise resolving to a Response\n */\n setCatchHandler(handler) {\n this.catchHandler = normalizeHandler(handler);\n }\n}\nexport { Route };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { Route } from './Route.js';\nimport './_version.js';\n/**\n * RegExpRoute makes it easy to create a regular expression based\n * {@link workbox-routing.Route}.\n *\n * For same-origin requests the RegExp only needs to match part of the URL. For\n * requests against third-party servers, you must define a RegExp that matches\n * the start of the URL.\n *\n * @memberof workbox-routing\n * @extends workbox-routing.Route\n */\nclass RegExpRoute extends Route {\n /**\n * If the regular expression contains\n * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},\n * the captured values will be passed to the\n * {@link workbox-routing~handlerCallback} `params`\n * argument.\n *\n * @param {RegExp} regExp The regular expression to match against URLs.\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n */\n constructor(regExp, handler, method) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(regExp, RegExp, {\n moduleName: 'workbox-routing',\n className: 'RegExpRoute',\n funcName: 'constructor',\n paramName: 'pattern',\n });\n }\n const match = ({ url }) => {\n const result = regExp.exec(url.href);\n // Return immediately if there's no match.\n if (!result) {\n return;\n }\n // Require that the match start at the first character in the URL string\n // if it's a cross-origin request.\n // See https://github.com/GoogleChrome/workbox/issues/281 for the context\n // behind this behavior.\n if (url.origin !== location.origin && result.index !== 0) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`The regular expression '${regExp.toString()}' only partially matched ` +\n `against the cross-origin URL '${url.toString()}'. RegExpRoute's will only ` +\n `handle cross-origin requests if they match the entire URL.`);\n }\n return;\n }\n // If the route matches, but there aren't any capture groups defined, then\n // this will return [], which is truthy and therefore sufficient to\n // indicate a match.\n // If there are capture groups, then it will return their values.\n return result.slice(1);\n };\n super(match, handler, method);\n }\n}\nexport { RegExpRoute };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst getFriendlyURL = (url) => {\n const urlObj = new URL(String(url), location.href);\n // See https://github.com/GoogleChrome/workbox/issues/2323\n // We want to include everything, except for the origin if it's same-origin.\n return urlObj.href.replace(new RegExp(`^${location.origin}`), '');\n};\nexport { getFriendlyURL };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { defaultMethod } from './utils/constants.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { normalizeHandler } from './utils/normalizeHandler.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport './_version.js';\n/**\n * The Router can be used to process a `FetchEvent` using one or more\n * {@link workbox-routing.Route}, responding with a `Response` if\n * a matching route exists.\n *\n * If no route matches a given a request, the Router will use a \"default\"\n * handler if one is defined.\n *\n * Should the matching Route throw an error, the Router will use a \"catch\"\n * handler if one is defined to gracefully deal with issues and respond with a\n * Request.\n *\n * If a request matches multiple routes, the **earliest** registered route will\n * be used to respond to the request.\n *\n * @memberof workbox-routing\n */\nclass Router {\n /**\n * Initializes a new Router.\n */\n constructor() {\n this._routes = new Map();\n this._defaultHandlerMap = new Map();\n }\n /**\n * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP\n * method name ('GET', etc.) to an array of all the corresponding `Route`\n * instances that are registered.\n */\n get routes() {\n return this._routes;\n }\n /**\n * Adds a fetch event listener to respond to events when a route matches\n * the event's request.\n */\n addFetchListener() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('fetch', ((event) => {\n const { request } = event;\n const responsePromise = this.handleRequest({ request, event });\n if (responsePromise) {\n event.respondWith(responsePromise);\n }\n }));\n }\n /**\n * Adds a message event listener for URLs to cache from the window.\n * This is useful to cache resources loaded on the page prior to when the\n * service worker started controlling it.\n *\n * The format of the message data sent from the window should be as follows.\n * Where the `urlsToCache` array may consist of URL strings or an array of\n * URL string + `requestInit` object (the same as you'd pass to `fetch()`).\n *\n * ```\n * {\n * type: 'CACHE_URLS',\n * payload: {\n * urlsToCache: [\n * './script1.js',\n * './script2.js',\n * ['./script3.js', {mode: 'no-cors'}],\n * ],\n * },\n * }\n * ```\n */\n addCacheListener() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('message', ((event) => {\n // event.data is type 'any'\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (event.data && event.data.type === 'CACHE_URLS') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { payload } = event.data;\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Caching URLs from the window`, payload.urlsToCache);\n }\n const requestPromises = Promise.all(payload.urlsToCache.map((entry) => {\n if (typeof entry === 'string') {\n entry = [entry];\n }\n const request = new Request(...entry);\n return this.handleRequest({ request, event });\n // TODO(philipwalton): TypeScript errors without this typecast for\n // some reason (probably a bug). The real type here should work but\n // doesn't: `Array<Promise<Response> | undefined>`.\n })); // TypeScript\n event.waitUntil(requestPromises);\n // If a MessageChannel was used, reply to the message on success.\n if (event.ports && event.ports[0]) {\n void requestPromises.then(() => event.ports[0].postMessage(true));\n }\n }\n }));\n }\n /**\n * Apply the routing rules to a FetchEvent object to get a Response from an\n * appropriate Route's handler.\n *\n * @param {Object} options\n * @param {Request} options.request The request to handle.\n * @param {ExtendableEvent} options.event The event that triggered the\n * request.\n * @return {Promise<Response>|undefined} A promise is returned if a\n * registered route can handle the request. If there is no matching\n * route and there's no `defaultHandler`, `undefined` is returned.\n */\n handleRequest({ request, event, }) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'handleRequest',\n paramName: 'options.request',\n });\n }\n const url = new URL(request.url, location.href);\n if (!url.protocol.startsWith('http')) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Workbox Router only supports URLs that start with 'http'.`);\n }\n return;\n }\n const sameOrigin = url.origin === location.origin;\n const { params, route } = this.findMatchingRoute({\n event,\n request,\n sameOrigin,\n url,\n });\n let handler = route && route.handler;\n const debugMessages = [];\n if (process.env.NODE_ENV !== 'production') {\n if (handler) {\n debugMessages.push([`Found a route to handle this request:`, route]);\n if (params) {\n debugMessages.push([\n `Passing the following params to the route's handler:`,\n params,\n ]);\n }\n }\n }\n // If we don't have a handler because there was no matching route, then\n // fall back to defaultHandler if that's defined.\n const method = request.method;\n if (!handler && this._defaultHandlerMap.has(method)) {\n if (process.env.NODE_ENV !== 'production') {\n debugMessages.push(`Failed to find a matching route. Falling ` +\n `back to the default handler for ${method}.`);\n }\n handler = this._defaultHandlerMap.get(method);\n }\n if (!handler) {\n if (process.env.NODE_ENV !== 'production') {\n // No handler so Workbox will do nothing. If logs is set of debug\n // i.e. verbose, we should print out this information.\n logger.debug(`No route found for: ${getFriendlyURL(url)}`);\n }\n return;\n }\n if (process.env.NODE_ENV !== 'production') {\n // We have a handler, meaning Workbox is going to handle the route.\n // print the routing details to the console.\n logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);\n debugMessages.forEach((msg) => {\n if (Array.isArray(msg)) {\n logger.log(...msg);\n }\n else {\n logger.log(msg);\n }\n });\n logger.groupEnd();\n }\n // Wrap in try and catch in case the handle method throws a synchronous\n // error. It should still callback to the catch handler.\n let responsePromise;\n try {\n responsePromise = handler.handle({ url, request, event, params });\n }\n catch (err) {\n responsePromise = Promise.reject(err);\n }\n // Get route's catch handler, if it exists\n const catchHandler = route && route.catchHandler;\n if (responsePromise instanceof Promise &&\n (this._catchHandler || catchHandler)) {\n responsePromise = responsePromise.catch(async (err) => {\n // If there's a route catch handler, process that first\n if (catchHandler) {\n if (process.env.NODE_ENV !== 'production') {\n // Still include URL here as it will be async from the console group\n // and may not make sense without the URL\n logger.groupCollapsed(`Error thrown when responding to: ` +\n ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);\n logger.error(`Error thrown by:`, route);\n logger.error(err);\n logger.groupEnd();\n }\n try {\n return await catchHandler.handle({ url, request, event, params });\n }\n catch (catchErr) {\n if (catchErr instanceof Error) {\n err = catchErr;\n }\n }\n }\n if (this._catchHandler) {\n if (process.env.NODE_ENV !== 'production') {\n // Still include URL here as it will be async from the console group\n // and may not make sense without the URL\n logger.groupCollapsed(`Error thrown when responding to: ` +\n ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);\n logger.error(`Error thrown by:`, route);\n logger.error(err);\n logger.groupEnd();\n }\n return this._catchHandler.handle({ url, request, event });\n }\n throw err;\n });\n }\n return responsePromise;\n }\n /**\n * Checks a request and URL (and optionally an event) against the list of\n * registered routes, and if there's a match, returns the corresponding\n * route along with any params generated by the match.\n *\n * @param {Object} options\n * @param {URL} options.url\n * @param {boolean} options.sameOrigin The result of comparing `url.origin`\n * against the current origin.\n * @param {Request} options.request The request to match.\n * @param {Event} options.event The corresponding event.\n * @return {Object} An object with `route` and `params` properties.\n * They are populated if a matching route was found or `undefined`\n * otherwise.\n */\n findMatchingRoute({ url, sameOrigin, request, event, }) {\n const routes = this._routes.get(request.method) || [];\n for (const route of routes) {\n let params;\n // route.match returns type any, not possible to change right now.\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const matchResult = route.match({ url, sameOrigin, request, event });\n if (matchResult) {\n if (process.env.NODE_ENV !== 'production') {\n // Warn developers that using an async matchCallback is almost always\n // not the right thing to do.\n if (matchResult instanceof Promise) {\n logger.warn(`While routing ${getFriendlyURL(url)}, an async ` +\n `matchCallback function was used. Please convert the ` +\n `following route to use a synchronous matchCallback function:`, route);\n }\n }\n // See https://github.com/GoogleChrome/workbox/issues/2079\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n params = matchResult;\n if (Array.isArray(params) && params.length === 0) {\n // Instead of passing an empty array in as params, use undefined.\n params = undefined;\n }\n else if (matchResult.constructor === Object && // eslint-disable-line\n Object.keys(matchResult).length === 0) {\n // Instead of passing an empty object in as params, use undefined.\n params = undefined;\n }\n else if (typeof matchResult === 'boolean') {\n // For the boolean value true (rather than just something truth-y),\n // don't set params.\n // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353\n params = undefined;\n }\n // Return early if have a match.\n return { route, params };\n }\n }\n // If no match was found above, return and empty object.\n return {};\n }\n /**\n * Define a default `handler` that's called when no routes explicitly\n * match the incoming request.\n *\n * Each HTTP method ('GET', 'POST', etc.) gets its own default handler.\n *\n * Without a default handler, unmatched requests will go against the\n * network as if there were no service worker present.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {string} [method='GET'] The HTTP method to associate with this\n * default handler. Each method has its own default.\n */\n setDefaultHandler(handler, method = defaultMethod) {\n this._defaultHandlerMap.set(method, normalizeHandler(handler));\n }\n /**\n * If a Route throws an error while handling a request, this `handler`\n * will be called and given a chance to provide a response.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n */\n setCatchHandler(handler) {\n this._catchHandler = normalizeHandler(handler);\n }\n /**\n * Registers a route with the router.\n *\n * @param {workbox-routing.Route} route The route to register.\n */\n registerRoute(route) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isType(route, 'object', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.hasMethod(route, 'match', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.isType(route.handler, 'object', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route',\n });\n assert.hasMethod(route.handler, 'handle', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route.handler',\n });\n assert.isType(route.method, 'string', {\n moduleName: 'workbox-routing',\n className: 'Router',\n funcName: 'registerRoute',\n paramName: 'route.method',\n });\n }\n if (!this._routes.has(route.method)) {\n this._routes.set(route.method, []);\n }\n // Give precedence to all of the earlier routes by adding this additional\n // route to the end of the array.\n this._routes.get(route.method).push(route);\n }\n /**\n * Unregisters a route with the router.\n *\n * @param {workbox-routing.Route} route The route to unregister.\n */\n unregisterRoute(route) {\n if (!this._routes.has(route.method)) {\n throw new WorkboxError('unregister-route-but-not-found-with-method', {\n method: route.method,\n });\n }\n const routeIndex = this._routes.get(route.method).indexOf(route);\n if (routeIndex > -1) {\n this._routes.get(route.method).splice(routeIndex, 1);\n }\n else {\n throw new WorkboxError('unregister-route-route-not-registered');\n }\n }\n}\nexport { Router };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { Router } from '../Router.js';\nimport '../_version.js';\nlet defaultRouter;\n/**\n * Creates a new, singleton Router instance if one does not exist. If one\n * does already exist, that instance is returned.\n *\n * @private\n * @return {Router}\n */\nexport const getOrCreateDefaultRouter = () => {\n if (!defaultRouter) {\n defaultRouter = new Router();\n // The helpers that use the default Router assume these listeners exist.\n defaultRouter.addFetchListener();\n defaultRouter.addCacheListener();\n }\n return defaultRouter;\n};\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Route } from './Route.js';\nimport { RegExpRoute } from './RegExpRoute.js';\nimport { getOrCreateDefaultRouter } from './utils/getOrCreateDefaultRouter.js';\nimport './_version.js';\n/**\n * Easily register a RegExp, string, or function with a caching\n * strategy to a singleton Router instance.\n *\n * This method will generate a Route for you if needed and\n * call {@link workbox-routing.Router#registerRoute}.\n *\n * @param {RegExp|string|workbox-routing.Route~matchCallback|workbox-routing.Route} capture\n * If the capture param is a `Route`, all other arguments will be ignored.\n * @param {workbox-routing~handlerCallback} [handler] A callback\n * function that returns a Promise resulting in a Response. This parameter\n * is required if `capture` is not a `Route` object.\n * @param {string} [method='GET'] The HTTP method to match the Route\n * against.\n * @return {workbox-routing.Route} The generated `Route`.\n *\n * @memberof workbox-routing\n */\nfunction registerRoute(capture, handler, method) {\n let route;\n if (typeof capture === 'string') {\n const captureUrl = new URL(capture, location.href);\n if (process.env.NODE_ENV !== 'production') {\n if (!(capture.startsWith('/') || capture.startsWith('http'))) {\n throw new WorkboxError('invalid-string', {\n moduleName: 'workbox-routing',\n funcName: 'registerRoute',\n paramName: 'capture',\n });\n }\n // We want to check if Express-style wildcards are in the pathname only.\n // TODO: Remove this log message in v4.\n const valueToCheck = capture.startsWith('http')\n ? captureUrl.pathname\n : capture;\n // See https://github.com/pillarjs/path-to-regexp#parameters\n const wildcards = '[*:?+]';\n if (new RegExp(`${wildcards}`).exec(valueToCheck)) {\n logger.debug(`The '$capture' parameter contains an Express-style wildcard ` +\n `character (${wildcards}). Strings are now always interpreted as ` +\n `exact matches; use a RegExp for partial or wildcard matches.`);\n }\n }\n const matchCallback = ({ url }) => {\n if (process.env.NODE_ENV !== 'production') {\n if (url.pathname === captureUrl.pathname &&\n url.origin !== captureUrl.origin) {\n logger.debug(`${capture} only partially matches the cross-origin URL ` +\n `${url.toString()}. This route will only handle cross-origin requests ` +\n `if they match the entire URL.`);\n }\n }\n return url.href === captureUrl.href;\n };\n // If `capture` is a string then `handler` and `method` must be present.\n route = new Route(matchCallback, handler, method);\n }\n else if (capture instanceof RegExp) {\n // If `capture` is a `RegExp` then `handler` and `method` must be present.\n route = new RegExpRoute(capture, handler, method);\n }\n else if (typeof capture === 'function') {\n // If `capture` is a function then `handler` and `method` must be present.\n route = new Route(capture, handler, method);\n }\n else if (capture instanceof Route) {\n route = capture;\n }\n else {\n throw new WorkboxError('unsupported-route-type', {\n moduleName: 'workbox-routing',\n funcName: 'registerRoute',\n paramName: 'capture',\n });\n }\n const defaultRouter = getOrCreateDefaultRouter();\n defaultRouter.registerRoute(route);\n return route;\n}\nexport { registerRoute };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:cacheable-response:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport './_version.js';\n/**\n * This class allows you to set up rules determining what\n * status codes and/or headers need to be present in order for a\n * [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)\n * to be considered cacheable.\n *\n * @memberof workbox-cacheable-response\n */\nclass CacheableResponse {\n /**\n * To construct a new CacheableResponse instance you must provide at least\n * one of the `config` properties.\n *\n * If both `statuses` and `headers` are specified, then both conditions must\n * be met for the `Response` to be considered cacheable.\n *\n * @param {Object} config\n * @param {Array<number>} [config.statuses] One or more status codes that a\n * `Response` can have and be considered cacheable.\n * @param {Object<string,string>} [config.headers] A mapping of header names\n * and expected values that a `Response` can have and be considered cacheable.\n * If multiple headers are provided, only one needs to be present.\n */\n constructor(config = {}) {\n if (process.env.NODE_ENV !== 'production') {\n if (!(config.statuses || config.headers)) {\n throw new WorkboxError('statuses-or-headers-required', {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n });\n }\n if (config.statuses) {\n assert.isArray(config.statuses, {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n paramName: 'config.statuses',\n });\n }\n if (config.headers) {\n assert.isType(config.headers, 'object', {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'constructor',\n paramName: 'config.headers',\n });\n }\n }\n this._statuses = config.statuses;\n this._headers = config.headers;\n }\n /**\n * Checks a response to see whether it's cacheable or not, based on this\n * object's configuration.\n *\n * @param {Response} response The response whose cacheability is being\n * checked.\n * @return {boolean} `true` if the `Response` is cacheable, and `false`\n * otherwise.\n */\n isResponseCacheable(response) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(response, Response, {\n moduleName: 'workbox-cacheable-response',\n className: 'CacheableResponse',\n funcName: 'isResponseCacheable',\n paramName: 'response',\n });\n }\n let cacheable = true;\n if (this._statuses) {\n cacheable = this._statuses.includes(response.status);\n }\n if (this._headers && cacheable) {\n cacheable = Object.keys(this._headers).some((headerName) => {\n return response.headers.get(headerName) === this._headers[headerName];\n });\n }\n if (process.env.NODE_ENV !== 'production') {\n if (!cacheable) {\n logger.groupCollapsed(`The request for ` +\n `'${getFriendlyURL(response.url)}' returned a response that does ` +\n `not meet the criteria for being cached.`);\n logger.groupCollapsed(`View cacheability criteria here.`);\n logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses));\n logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2));\n logger.groupEnd();\n const logFriendlyHeaders = {};\n response.headers.forEach((value, key) => {\n logFriendlyHeaders[key] = value;\n });\n logger.groupCollapsed(`View response status and headers here.`);\n logger.log(`Response status: ${response.status}`);\n logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2));\n logger.groupEnd();\n logger.groupCollapsed(`View full response details here.`);\n logger.log(response.headers);\n logger.log(response);\n logger.groupEnd();\n logger.groupEnd();\n }\n }\n return cacheable;\n }\n}\nexport { CacheableResponse };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { CacheableResponse, } from './CacheableResponse.js';\nimport './_version.js';\n/**\n * A class implementing the `cacheWillUpdate` lifecycle callback. This makes it\n * easier to add in cacheability checks to requests made via Workbox's built-in\n * strategies.\n *\n * @memberof workbox-cacheable-response\n */\nclass CacheableResponsePlugin {\n /**\n * To construct a new CacheableResponsePlugin instance you must provide at\n * least one of the `config` properties.\n *\n * If both `statuses` and `headers` are specified, then both conditions must\n * be met for the `Response` to be considered cacheable.\n *\n * @param {Object} config\n * @param {Array<number>} [config.statuses] One or more status codes that a\n * `Response` can have and be considered cacheable.\n * @param {Object<string,string>} [config.headers] A mapping of header names\n * and expected values that a `Response` can have and be considered cacheable.\n * If multiple headers are provided, only one needs to be present.\n */\n constructor(config) {\n /**\n * @param {Object} options\n * @param {Response} options.response\n * @return {Response|null}\n * @private\n */\n this.cacheWillUpdate = async ({ response }) => {\n if (this._cacheableResponse.isResponseCacheable(response)) {\n return response;\n }\n return null;\n };\n this._cacheableResponse = new CacheableResponse(config);\n }\n}\nexport { CacheableResponsePlugin };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst _cacheNameDetails = {\n googleAnalytics: 'googleAnalytics',\n precache: 'precache-v2',\n prefix: 'workbox',\n runtime: 'runtime',\n suffix: typeof registration !== 'undefined' ? registration.scope : '',\n};\nconst _createCacheName = (cacheName) => {\n return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix]\n .filter((value) => value && value.length > 0)\n .join('-');\n};\nconst eachCacheNameDetail = (fn) => {\n for (const key of Object.keys(_cacheNameDetails)) {\n fn(key);\n }\n};\nexport const cacheNames = {\n updateDetails: (details) => {\n eachCacheNameDetail((key) => {\n if (typeof details[key] === 'string') {\n _cacheNameDetails[key] = details[key];\n }\n });\n },\n getGoogleAnalyticsName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);\n },\n getPrecacheName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.precache);\n },\n getPrefix: () => {\n return _cacheNameDetails.prefix;\n },\n getRuntimeName: (userCacheName) => {\n return userCacheName || _createCacheName(_cacheNameDetails.runtime);\n },\n getSuffix: () => {\n return _cacheNameDetails.suffix;\n },\n};\n","/*\n Copyright 2020 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nfunction stripParams(fullURL, ignoreParams) {\n const strippedURL = new URL(fullURL);\n for (const param of ignoreParams) {\n strippedURL.searchParams.delete(param);\n }\n return strippedURL.href;\n}\n/**\n * Matches an item in the cache, ignoring specific URL params. This is similar\n * to the `ignoreSearch` option, but it allows you to ignore just specific\n * params (while continuing to match on the others).\n *\n * @private\n * @param {Cache} cache\n * @param {Request} request\n * @param {Object} matchOptions\n * @param {Array<string>} ignoreParams\n * @return {Promise<Response|undefined>}\n */\nasync function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) {\n const strippedRequestURL = stripParams(request.url, ignoreParams);\n // If the request doesn't include any ignored params, match as normal.\n if (request.url === strippedRequestURL) {\n return cache.match(request, matchOptions);\n }\n // Otherwise, match by comparing keys\n const keysOptions = Object.assign(Object.assign({}, matchOptions), { ignoreSearch: true });\n const cacheKeys = await cache.keys(request, keysOptions);\n for (const cacheKey of cacheKeys) {\n const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams);\n if (strippedRequestURL === strippedCacheKeyURL) {\n return cache.match(cacheKey, matchOptions);\n }\n }\n return;\n}\nexport { cacheMatchIgnoreParams };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * The Deferred class composes Promises in a way that allows for them to be\n * resolved or rejected from outside the constructor. In most cases promises\n * should be used directly, but Deferreds can be necessary when the logic to\n * resolve a promise must be separate.\n *\n * @private\n */\nclass Deferred {\n /**\n * Creates a promise and exposes its resolve and reject functions as methods.\n */\n constructor() {\n this.promise = new Promise((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\nexport { Deferred };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n// Callbacks to be executed whenever there's a quota error.\n// Can't change Function type right now.\n// eslint-disable-next-line @typescript-eslint/ban-types\nconst quotaErrorCallbacks = new Set();\nexport { quotaErrorCallbacks };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from '../_private/logger.js';\nimport { quotaErrorCallbacks } from '../models/quotaErrorCallbacks.js';\nimport '../_version.js';\n/**\n * Runs all of the callback functions, one at a time sequentially, in the order\n * in which they were registered.\n *\n * @memberof workbox-core\n * @private\n */\nasync function executeQuotaErrorCallbacks() {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`About to run ${quotaErrorCallbacks.size} ` +\n `callbacks to clean up caches.`);\n }\n for (const callback of quotaErrorCallbacks) {\n await callback();\n if (process.env.NODE_ENV !== 'production') {\n logger.log(callback, 'is complete.');\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.log('Finished running callbacks.');\n }\n}\nexport { executeQuotaErrorCallbacks };\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * Returns a promise that resolves and the passed number of milliseconds.\n * This utility is an async/await-friendly version of `setTimeout`.\n *\n * @param {number} ms\n * @return {Promise}\n * @private\n */\nexport function timeout(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:strategies:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { cacheMatchIgnoreParams } from 'workbox-core/_private/cacheMatchIgnoreParams.js';\nimport { Deferred } from 'workbox-core/_private/Deferred.js';\nimport { executeQuotaErrorCallbacks } from 'workbox-core/_private/executeQuotaErrorCallbacks.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { timeout } from 'workbox-core/_private/timeout.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport './_version.js';\nfunction toRequest(input) {\n return typeof input === 'string' ? new Request(input) : input;\n}\n/**\n * A class created every time a Strategy instance instance calls\n * {@link workbox-strategies.Strategy~handle} or\n * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and\n * cache actions around plugin callbacks and keeps track of when the strategy\n * is \"done\" (i.e. all added `event.waitUntil()` promises have resolved).\n *\n * @memberof workbox-strategies\n */\nclass StrategyHandler {\n /**\n * Creates a new instance associated with the passed strategy and event\n * that's handling the request.\n *\n * The constructor also initializes the state that will be passed to each of\n * the plugins handling this request.\n *\n * @param {workbox-strategies.Strategy} strategy\n * @param {Object} options\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params] The return value from the\n * {@link workbox-routing~matchCallback} (if applicable).\n */\n constructor(strategy, options) {\n this._cacheKeys = {};\n /**\n * The request the strategy is performing (passed to the strategy's\n * `handle()` or `handleAll()` method).\n * @name request\n * @instance\n * @type {Request}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * The event associated with this request.\n * @name event\n * @instance\n * @type {ExtendableEvent}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * A `URL` instance of `request.url` (if passed to the strategy's\n * `handle()` or `handleAll()` method).\n * Note: the `url` param will be present if the strategy was invoked\n * from a workbox `Route` object.\n * @name url\n * @instance\n * @type {URL|undefined}\n * @memberof workbox-strategies.StrategyHandler\n */\n /**\n * A `param` value (if passed to the strategy's\n * `handle()` or `handleAll()` method).\n * Note: the `param` param will be present if the strategy was invoked\n * from a workbox `Route` object and the\n * {@link workbox-routing~matchCallback} returned\n * a truthy value (it will be that value).\n * @name params\n * @instance\n * @type {*|undefined}\n * @memberof workbox-strategies.StrategyHandler\n */\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(options.event, ExtendableEvent, {\n moduleName: 'workbox-strategies',\n className: 'StrategyHandler',\n funcName: 'constructor',\n paramName: 'options.event',\n });\n }\n Object.assign(this, options);\n this.event = options.event;\n this._strategy = strategy;\n this._handlerDeferred = new Deferred();\n this._extendLifetimePromises = [];\n // Copy the plugins list (since it's mutable on the strategy),\n // so any mutations don't affect this handler instance.\n this._plugins = [...strategy.plugins];\n this._pluginStateMap = new Map();\n for (const plugin of this._plugins) {\n this._pluginStateMap.set(plugin, {});\n }\n this.event.waitUntil(this._handlerDeferred.promise);\n }\n /**\n * Fetches a given request (and invokes any applicable plugin callback\n * methods) using the `fetchOptions` (for non-navigation requests) and\n * `plugins` defined on the `Strategy` object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - `requestWillFetch()`\n * - `fetchDidSucceed()`\n * - `fetchDidFail()`\n *\n * @param {Request|string} input The URL or request to fetch.\n * @return {Promise<Response>}\n */\n async fetch(input) {\n const { event } = this;\n let request = toRequest(input);\n if (request.mode === 'navigate' &&\n event instanceof FetchEvent &&\n event.preloadResponse) {\n const possiblePreloadResponse = (await event.preloadResponse);\n if (possiblePreloadResponse) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Using a preloaded navigation response for ` +\n `'${getFriendlyURL(request.url)}'`);\n }\n return possiblePreloadResponse;\n }\n }\n // If there is a fetchDidFail plugin, we need to save a clone of the\n // original request before it's either modified by a requestWillFetch\n // plugin or before the original request's body is consumed via fetch().\n const originalRequest = this.hasCallback('fetchDidFail')\n ? request.clone()\n : null;\n try {\n for (const cb of this.iterateCallbacks('requestWillFetch')) {\n request = await cb({ request: request.clone(), event });\n }\n }\n catch (err) {\n if (err instanceof Error) {\n throw new WorkboxError('plugin-error-request-will-fetch', {\n thrownErrorMessage: err.message,\n });\n }\n }\n // The request can be altered by plugins with `requestWillFetch` making\n // the original request (most likely from a `fetch` event) different\n // from the Request we make. Pass both to `fetchDidFail` to aid debugging.\n const pluginFilteredRequest = request.clone();\n try {\n let fetchResponse;\n // See https://github.com/GoogleChrome/workbox/issues/1796\n fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions);\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Network request for ` +\n `'${getFriendlyURL(request.url)}' returned a response with ` +\n `status '${fetchResponse.status}'.`);\n }\n for (const callback of this.iterateCallbacks('fetchDidSucceed')) {\n fetchResponse = await callback({\n event,\n request: pluginFilteredRequest,\n response: fetchResponse,\n });\n }\n return fetchResponse;\n }\n catch (error) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Network request for ` +\n `'${getFriendlyURL(request.url)}' threw an error.`, error);\n }\n // `originalRequest` will only exist if a `fetchDidFail` callback\n // is being used (see above).\n if (originalRequest) {\n await this.runCallbacks('fetchDidFail', {\n error: error,\n event,\n originalRequest: originalRequest.clone(),\n request: pluginFilteredRequest.clone(),\n });\n }\n throw error;\n }\n }\n /**\n * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on\n * the response generated by `this.fetch()`.\n *\n * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,\n * so you do not have to manually call `waitUntil()` on the event.\n *\n * @param {Request|string} input The request or URL to fetch and cache.\n * @return {Promise<Response>}\n */\n async fetchAndCachePut(input) {\n const response = await this.fetch(input);\n const responseClone = response.clone();\n void this.waitUntil(this.cachePut(input, responseClone));\n return response;\n }\n /**\n * Matches a request from the cache (and invokes any applicable plugin\n * callback methods) using the `cacheName`, `matchOptions`, and `plugins`\n * defined on the strategy object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - cacheKeyWillBeUsed()\n * - cachedResponseWillBeUsed()\n *\n * @param {Request|string} key The Request or URL to use as the cache key.\n * @return {Promise<Response|undefined>} A matching response, if found.\n */\n async cacheMatch(key) {\n const request = toRequest(key);\n let cachedResponse;\n const { cacheName, matchOptions } = this._strategy;\n const effectiveRequest = await this.getCacheKey(request, 'read');\n const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), { cacheName });\n cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);\n if (process.env.NODE_ENV !== 'production') {\n if (cachedResponse) {\n logger.debug(`Found a cached response in '${cacheName}'.`);\n }\n else {\n logger.debug(`No cached response found in '${cacheName}'.`);\n }\n }\n for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {\n cachedResponse =\n (await callback({\n cacheName,\n matchOptions,\n cachedResponse,\n request: effectiveRequest,\n event: this.event,\n })) || undefined;\n }\n return cachedResponse;\n }\n /**\n * Puts a request/response pair in the cache (and invokes any applicable\n * plugin callback methods) using the `cacheName` and `plugins` defined on\n * the strategy object.\n *\n * The following plugin lifecycle methods are invoked when using this method:\n * - cacheKeyWillBeUsed()\n * - cacheWillUpdate()\n * - cacheDidUpdate()\n *\n * @param {Request|string} key The request or URL to use as the cache key.\n * @param {Response} response The response to cache.\n * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response\n * not be cached, and `true` otherwise.\n */\n async cachePut(key, response) {\n const request = toRequest(key);\n // Run in the next task to avoid blocking other cache reads.\n // https://github.com/w3c/ServiceWorker/issues/1397\n await timeout(0);\n const effectiveRequest = await this.getCacheKey(request, 'write');\n if (process.env.NODE_ENV !== 'production') {\n if (effectiveRequest.method && effectiveRequest.method !== 'GET') {\n throw new WorkboxError('attempt-to-cache-non-get-request', {\n url: getFriendlyURL(effectiveRequest.url),\n method: effectiveRequest.method,\n });\n }\n // See https://github.com/GoogleChrome/workbox/issues/2818\n const vary = response.headers.get('Vary');\n if (vary) {\n logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` +\n `has a 'Vary: ${vary}' header. ` +\n `Consider setting the {ignoreVary: true} option on your strategy ` +\n `to ensure cache matching and deletion works as expected.`);\n }\n }\n if (!response) {\n if (process.env.NODE_ENV !== 'production') {\n logger.error(`Cannot cache non-existent response for ` +\n `'${getFriendlyURL(effectiveRequest.url)}'.`);\n }\n throw new WorkboxError('cache-put-with-no-response', {\n url: getFriendlyURL(effectiveRequest.url),\n });\n }\n const responseToCache = await this._ensureResponseSafeToCache(response);\n if (!responseToCache) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` +\n `will not be cached.`, responseToCache);\n }\n return false;\n }\n const { cacheName, matchOptions } = this._strategy;\n const cache = await self.caches.open(cacheName);\n const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');\n const oldResponse = hasCacheUpdateCallback\n ? await cacheMatchIgnoreParams(\n // TODO(philipwalton): the `__WB_REVISION__` param is a precaching\n // feature. Consider into ways to only add this behavior if using\n // precaching.\n cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions)\n : null;\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Updating the '${cacheName}' cache with a new Response ` +\n `for ${getFriendlyURL(effectiveRequest.url)}.`);\n }\n try {\n await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);\n }\n catch (error) {\n if (error instanceof Error) {\n // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError\n if (error.name === 'QuotaExceededError') {\n await executeQuotaErrorCallbacks();\n }\n throw error;\n }\n }\n for (const callback of this.iterateCallbacks('cacheDidUpdate')) {\n await callback({\n cacheName,\n oldResponse,\n newResponse: responseToCache.clone(),\n request: effectiveRequest,\n event: this.event,\n });\n }\n return true;\n }\n /**\n * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and\n * executes any of those callbacks found in sequence. The final `Request`\n * object returned by the last plugin is treated as the cache key for cache\n * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have\n * been registered, the passed request is returned unmodified\n *\n * @param {Request} request\n * @param {string} mode\n * @return {Promise<Request>}\n */\n async getCacheKey(request, mode) {\n const key = `${request.url} | ${mode}`;\n if (!this._cacheKeys[key]) {\n let effectiveRequest = request;\n for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {\n effectiveRequest = toRequest(await callback({\n mode,\n request: effectiveRequest,\n event: this.event,\n // params has a type any can't change right now.\n params: this.params, // eslint-disable-line\n }));\n }\n this._cacheKeys[key] = effectiveRequest;\n }\n return this._cacheKeys[key];\n }\n /**\n * Returns true if the strategy has at least one plugin with the given\n * callback.\n *\n * @param {string} name The name of the callback to check for.\n * @return {boolean}\n */\n hasCallback(name) {\n for (const plugin of this._strategy.plugins) {\n if (name in plugin) {\n return true;\n }\n }\n return false;\n }\n /**\n * Runs all plugin callbacks matching the given name, in order, passing the\n * given param object (merged ith the current plugin state) as the only\n * argument.\n *\n * Note: since this method runs all plugins, it's not suitable for cases\n * where the return value of a callback needs to be applied prior to calling\n * the next callback. See\n * {@link workbox-strategies.StrategyHandler#iterateCallbacks}\n * below for how to handle that case.\n *\n * @param {string} name The name of the callback to run within each plugin.\n * @param {Object} param The object to pass as the first (and only) param\n * when executing each callback. This object will be merged with the\n * current plugin state prior to callback execution.\n */\n async runCallbacks(name, param) {\n for (const callback of this.iterateCallbacks(name)) {\n // TODO(philipwalton): not sure why `any` is needed. It seems like\n // this should work with `as WorkboxPluginCallbackParam[C]`.\n await callback(param);\n }\n }\n /**\n * Accepts a callback and returns an iterable of matching plugin callbacks,\n * where each callback is wrapped with the current handler state (i.e. when\n * you call each callback, whatever object parameter you pass it will\n * be merged with the plugin's current state).\n *\n * @param {string} name The name fo the callback to run\n * @return {Array<Function>}\n */\n *iterateCallbacks(name) {\n for (const plugin of this._strategy.plugins) {\n if (typeof plugin[name] === 'function') {\n const state = this._pluginStateMap.get(plugin);\n const statefulCallback = (param) => {\n const statefulParam = Object.assign(Object.assign({}, param), { state });\n // TODO(philipwalton): not sure why `any` is needed. It seems like\n // this should work with `as WorkboxPluginCallbackParam[C]`.\n return plugin[name](statefulParam);\n };\n yield statefulCallback;\n }\n }\n }\n /**\n * Adds a promise to the\n * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}\n * of the event event associated with the request being handled (usually a\n * `FetchEvent`).\n *\n * Note: you can await\n * {@link workbox-strategies.StrategyHandler~doneWaiting}\n * to know when all added promises have settled.\n *\n * @param {Promise} promise A promise to add to the extend lifetime promises\n * of the event that triggered the request.\n */\n waitUntil(promise) {\n this._extendLifetimePromises.push(promise);\n return promise;\n }\n /**\n * Returns a promise that resolves once all promises passed to\n * {@link workbox-strategies.StrategyHandler~waitUntil}\n * have settled.\n *\n * Note: any work done after `doneWaiting()` settles should be manually\n * passed to an event's `waitUntil()` method (not this handler's\n * `waitUntil()` method), otherwise the service worker thread my be killed\n * prior to your work completing.\n */\n async doneWaiting() {\n let promise;\n while ((promise = this._extendLifetimePromises.shift())) {\n await promise;\n }\n }\n /**\n * Stops running the strategy and immediately resolves any pending\n * `waitUntil()` promises.\n */\n destroy() {\n this._handlerDeferred.resolve(null);\n }\n /**\n * This method will call cacheWillUpdate on the available plugins (or use\n * status === 200) to determine if the Response is safe and valid to cache.\n *\n * @param {Request} options.request\n * @param {Response} options.response\n * @return {Promise<Response|undefined>}\n *\n * @private\n */\n async _ensureResponseSafeToCache(response) {\n let responseToCache = response;\n let pluginsUsed = false;\n for (const callback of this.iterateCallbacks('cacheWillUpdate')) {\n responseToCache =\n (await callback({\n request: this.request,\n response: responseToCache,\n event: this.event,\n })) || undefined;\n pluginsUsed = true;\n if (!responseToCache) {\n break;\n }\n }\n if (!pluginsUsed) {\n if (responseToCache && responseToCache.status !== 200) {\n responseToCache = undefined;\n }\n if (process.env.NODE_ENV !== 'production') {\n if (responseToCache) {\n if (responseToCache.status !== 200) {\n if (responseToCache.status === 0) {\n logger.warn(`The response for '${this.request.url}' ` +\n `is an opaque response. The caching strategy that you're ` +\n `using will not cache opaque responses by default.`);\n }\n else {\n logger.debug(`The response for '${this.request.url}' ` +\n `returned a status code of '${response.status}' and won't ` +\n `be cached as a result.`);\n }\n }\n }\n }\n }\n return responseToCache;\n }\n}\nexport { StrategyHandler };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { StrategyHandler } from './StrategyHandler.js';\nimport './_version.js';\n/**\n * An abstract base class that all other strategy classes must extend from:\n *\n * @memberof workbox-strategies\n */\nclass Strategy {\n /**\n * Creates a new instance of the strategy and sets all documented option\n * properties as public instance properties.\n *\n * Note: if a custom strategy class extends the base Strategy class and does\n * not need more than these properties, it does not need to define its own\n * constructor.\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {Object} [options.matchOptions] The\n * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n */\n constructor(options = {}) {\n /**\n * Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n *\n * @type {string}\n */\n this.cacheName = cacheNames.getRuntimeName(options.cacheName);\n /**\n * The list\n * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * used by this strategy.\n *\n * @type {Array<Object>}\n */\n this.plugins = options.plugins || [];\n /**\n * Values passed along to the\n * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}\n * of all fetch() requests made by this strategy.\n *\n * @type {Object}\n */\n this.fetchOptions = options.fetchOptions;\n /**\n * The\n * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n *\n * @type {Object}\n */\n this.matchOptions = options.matchOptions;\n }\n /**\n * Perform a request strategy and returns a `Promise` that will resolve with\n * a `Response`, invoking all relevant plugin callbacks.\n *\n * When a strategy instance is registered with a Workbox\n * {@link workbox-routing.Route}, this method is automatically\n * called when the route matches.\n *\n * Alternatively, this method can be used in a standalone `FetchEvent`\n * listener by passing it to `event.respondWith()`.\n *\n * @param {FetchEvent|Object} options A `FetchEvent` or an object with the\n * properties listed below.\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n */\n handle(options) {\n const [responseDone] = this.handleAll(options);\n return responseDone;\n }\n /**\n * Similar to {@link workbox-strategies.Strategy~handle}, but\n * instead of just returning a `Promise` that resolves to a `Response` it\n * it will return an tuple of `[response, done]` promises, where the former\n * (`response`) is equivalent to what `handle()` returns, and the latter is a\n * Promise that will resolve once any promises that were added to\n * `event.waitUntil()` as part of performing the strategy have completed.\n *\n * You can await the `done` promise to ensure any extra work performed by\n * the strategy (usually caching responses) completes successfully.\n *\n * @param {FetchEvent|Object} options A `FetchEvent` or an object with the\n * properties listed below.\n * @param {Request|string} options.request A request to run this strategy for.\n * @param {ExtendableEvent} options.event The event associated with the\n * request.\n * @param {URL} [options.url]\n * @param {*} [options.params]\n * @return {Array<Promise>} A tuple of [response, done]\n * promises that can be used to determine when the response resolves as\n * well as when the handler has completed all its work.\n */\n handleAll(options) {\n // Allow for flexible options to be passed.\n if (options instanceof FetchEvent) {\n options = {\n event: options,\n request: options.request,\n };\n }\n const event = options.event;\n const request = typeof options.request === 'string'\n ? new Request(options.request)\n : options.request;\n const params = 'params' in options ? options.params : undefined;\n const handler = new StrategyHandler(this, { event, request, params });\n const responseDone = this._getResponse(handler, request, event);\n const handlerDone = this._awaitComplete(responseDone, handler, request, event);\n // Return an array of promises, suitable for use with Promise.all().\n return [responseDone, handlerDone];\n }\n async _getResponse(handler, request, event) {\n await handler.runCallbacks('handlerWillStart', { event, request });\n let response = undefined;\n try {\n response = await this._handle(request, handler);\n // The \"official\" Strategy subclasses all throw this error automatically,\n // but in case a third-party Strategy doesn't, ensure that we have a\n // consistent failure when there's no response or an error response.\n if (!response || response.type === 'error') {\n throw new WorkboxError('no-response', { url: request.url });\n }\n }\n catch (error) {\n if (error instanceof Error) {\n for (const callback of handler.iterateCallbacks('handlerDidError')) {\n response = await callback({ error, event, request });\n if (response) {\n break;\n }\n }\n }\n if (!response) {\n throw error;\n }\n else if (process.env.NODE_ENV !== 'production') {\n logger.log(`While responding to '${getFriendlyURL(request.url)}', ` +\n `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` +\n `a handlerDidError plugin.`);\n }\n }\n for (const callback of handler.iterateCallbacks('handlerWillRespond')) {\n response = await callback({ event, request, response });\n }\n return response;\n }\n async _awaitComplete(responseDone, handler, request, event) {\n let response;\n let error;\n try {\n response = await responseDone;\n }\n catch (error) {\n // Ignore errors, as response errors should be caught via the `response`\n // promise above. The `done` promise will only throw for errors in\n // promises passed to `handler.waitUntil()`.\n }\n try {\n await handler.runCallbacks('handlerDidRespond', {\n event,\n request,\n response,\n });\n await handler.doneWaiting();\n }\n catch (waitUntilError) {\n if (waitUntilError instanceof Error) {\n error = waitUntilError;\n }\n }\n await handler.runCallbacks('handlerDidComplete', {\n event,\n request,\n response,\n error: error,\n });\n handler.destroy();\n if (error) {\n throw error;\n }\n }\n}\nexport { Strategy };\n/**\n * Classes extending the `Strategy` based class should implement this method,\n * and leverage the {@link workbox-strategies.StrategyHandler}\n * arg to perform all fetching and cache logic, which will ensure all relevant\n * cache, cache options, fetch options and plugins are used (per the current\n * strategy instance).\n *\n * @name _handle\n * @instance\n * @abstract\n * @function\n * @param {Request} request\n * @param {workbox-strategies.StrategyHandler} handler\n * @return {Promise<Response>}\n *\n * @memberof workbox-strategies.Strategy\n */\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport '../_version.js';\nexport const messages = {\n strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,\n printFinalResponse: (response) => {\n if (response) {\n logger.groupCollapsed(`View the final response here.`);\n logger.log(response || '[No response returned]');\n logger.groupEnd();\n }\n },\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from './Strategy.js';\nimport { messages } from './utils/messages.js';\nimport './_version.js';\n/**\n * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network)\n * request strategy.\n *\n * A cache first strategy is useful for assets that have been revisioned,\n * such as URLs like `/styles/example.a8f5f1.css`, since they\n * can be cached for long periods of time.\n *\n * If the network request fails, and there is no cache match, this will throw\n * a `WorkboxError` exception.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-strategies\n */\nclass CacheFirst extends Strategy {\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n const logs = [];\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: 'makeRequest',\n paramName: 'request',\n });\n }\n let response = await handler.cacheMatch(request);\n let error = undefined;\n if (!response) {\n if (process.env.NODE_ENV !== 'production') {\n logs.push(`No response found in the '${this.cacheName}' cache. ` +\n `Will respond with a network request.`);\n }\n try {\n response = await handler.fetchAndCachePut(request);\n }\n catch (err) {\n if (err instanceof Error) {\n error = err;\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n if (response) {\n logs.push(`Got response from network.`);\n }\n else {\n logs.push(`Unable to get a response from the network.`);\n }\n }\n }\n else {\n if (process.env.NODE_ENV !== 'production') {\n logs.push(`Found a cached response in the '${this.cacheName}' cache.`);\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));\n for (const log of logs) {\n logger.log(log);\n }\n messages.printFinalResponse(response);\n logger.groupEnd();\n }\n if (!response) {\n throw new WorkboxError('no-response', { url: request.url, error });\n }\n return response;\n }\n}\nexport { CacheFirst };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { timeout } from 'workbox-core/_private/timeout.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from './Strategy.js';\nimport { messages } from './utils/messages.js';\nimport './_version.js';\n/**\n * An implementation of a\n * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)\n * request strategy.\n *\n * This class is useful if you want to take advantage of any\n * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).\n *\n * If the network request fails, this will throw a `WorkboxError` exception.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-strategies\n */\nclass NetworkOnly extends Strategy {\n /**\n * @param {Object} [options]\n * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)\n * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)\n * `fetch()` requests made by this strategy.\n * @param {number} [options.networkTimeoutSeconds] If set, any network requests\n * that fail to respond within the timeout will result in a network error.\n */\n constructor(options = {}) {\n super(options);\n this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;\n }\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isInstance(request, Request, {\n moduleName: 'workbox-strategies',\n className: this.constructor.name,\n funcName: '_handle',\n paramName: 'request',\n });\n }\n let error = undefined;\n let response;\n try {\n const promises = [\n handler.fetch(request),\n ];\n if (this._networkTimeoutSeconds) {\n const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);\n promises.push(timeoutPromise);\n }\n response = await Promise.race(promises);\n if (!response) {\n throw new Error(`Timed out the network response after ` +\n `${this._networkTimeoutSeconds} seconds.`);\n }\n }\n catch (err) {\n if (err instanceof Error) {\n error = err;\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));\n if (response) {\n logger.log(`Got response from network.`);\n }\n else {\n logger.log(`Unable to get a response from the network.`);\n }\n messages.printFinalResponse(response);\n logger.groupEnd();\n }\n if (!response) {\n throw new WorkboxError('no-response', { url: request.url, error });\n }\n return response;\n }\n}\nexport { NetworkOnly };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport './_version.js';\n/**\n * Claim any currently available clients once the service worker\n * becomes active. This is normally used in conjunction with `skipWaiting()`.\n *\n * @memberof workbox-core\n */\nfunction clientsClaim() {\n self.addEventListener('activate', () => self.clients.claim());\n}\nexport { clientsClaim };\n","/*\n Copyright 2020 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A utility method that makes it easier to use `event.waitUntil` with\n * async functions and return the result.\n *\n * @param {ExtendableEvent} event\n * @param {Function} asyncFn\n * @return {Function}\n * @private\n */\nfunction waitUntil(event, asyncFn) {\n const returnPromise = asyncFn();\n event.waitUntil(returnPromise);\n return returnPromise;\n}\nexport { waitUntil };\n","\"use strict\";\n// @ts-ignore\ntry {\n self['workbox:precaching:7.2.0'] && _();\n}\ncatch (e) { }\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport '../_version.js';\n// Name of the search parameter used to store revision info.\nconst REVISION_SEARCH_PARAM = '__WB_REVISION__';\n/**\n * Converts a manifest entry into a versioned URL suitable for precaching.\n *\n * @param {Object|string} entry\n * @return {string} A URL with versioning info.\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function createCacheKey(entry) {\n if (!entry) {\n throw new WorkboxError('add-to-cache-list-unexpected-type', { entry });\n }\n // If a precache manifest entry is a string, it's assumed to be a versioned\n // URL, like '/app.abcd1234.js'. Return as-is.\n if (typeof entry === 'string') {\n const urlObject = new URL(entry, location.href);\n return {\n cacheKey: urlObject.href,\n url: urlObject.href,\n };\n }\n const { revision, url } = entry;\n if (!url) {\n throw new WorkboxError('add-to-cache-list-unexpected-type', { entry });\n }\n // If there's just a URL and no revision, then it's also assumed to be a\n // versioned URL.\n if (!revision) {\n const urlObject = new URL(url, location.href);\n return {\n cacheKey: urlObject.href,\n url: urlObject.href,\n };\n }\n // Otherwise, construct a properly versioned URL using the custom Workbox\n // search parameter along with the revision info.\n const cacheKeyURL = new URL(url, location.href);\n const originalURL = new URL(url, location.href);\n cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);\n return {\n cacheKey: cacheKeyURL.href,\n url: originalURL.href,\n };\n}\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A plugin, designed to be used with PrecacheController, to determine the\n * of assets that were updated (or not updated) during the install event.\n *\n * @private\n */\nclass PrecacheInstallReportPlugin {\n constructor() {\n this.updatedURLs = [];\n this.notUpdatedURLs = [];\n this.handlerWillStart = async ({ request, state, }) => {\n // TODO: `state` should never be undefined...\n if (state) {\n state.originalRequest = request;\n }\n };\n this.cachedResponseWillBeUsed = async ({ event, state, cachedResponse, }) => {\n if (event.type === 'install') {\n if (state &&\n state.originalRequest &&\n state.originalRequest instanceof Request) {\n // TODO: `state` should never be undefined...\n const url = state.originalRequest.url;\n if (cachedResponse) {\n this.notUpdatedURLs.push(url);\n }\n else {\n this.updatedURLs.push(url);\n }\n }\n }\n return cachedResponse;\n };\n }\n}\nexport { PrecacheInstallReportPlugin };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * A plugin, designed to be used with PrecacheController, to translate URLs into\n * the corresponding cache key, based on the current revision info.\n *\n * @private\n */\nclass PrecacheCacheKeyPlugin {\n constructor({ precacheController }) {\n this.cacheKeyWillBeUsed = async ({ request, params, }) => {\n // Params is type any, can't change right now.\n /* eslint-disable */\n const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) ||\n this._precacheController.getCacheKeyForURL(request.url);\n /* eslint-enable */\n return cacheKey\n ? new Request(cacheKey, { headers: request.headers })\n : request;\n };\n this._precacheController = precacheController;\n }\n}\nexport { PrecacheCacheKeyPlugin };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport '../_version.js';\n/**\n * @param {string} groupTitle\n * @param {Array<string>} deletedURLs\n *\n * @private\n */\nconst logGroup = (groupTitle, deletedURLs) => {\n logger.groupCollapsed(groupTitle);\n for (const url of deletedURLs) {\n logger.log(url);\n }\n logger.groupEnd();\n};\n/**\n * @param {Array<string>} deletedURLs\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function printCleanupDetails(deletedURLs) {\n const deletionCount = deletedURLs.length;\n if (deletionCount > 0) {\n logger.groupCollapsed(`During precaching cleanup, ` +\n `${deletionCount} cached ` +\n `request${deletionCount === 1 ? ' was' : 's were'} deleted.`);\n logGroup('Deleted Cache Requests', deletedURLs);\n logger.groupEnd();\n }\n}\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport '../_version.js';\n/**\n * @param {string} groupTitle\n * @param {Array<string>} urls\n *\n * @private\n */\nfunction _nestedGroup(groupTitle, urls) {\n if (urls.length === 0) {\n return;\n }\n logger.groupCollapsed(groupTitle);\n for (const url of urls) {\n logger.log(url);\n }\n logger.groupEnd();\n}\n/**\n * @param {Array<string>} urlsToPrecache\n * @param {Array<string>} urlsAlreadyPrecached\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) {\n const precachedCount = urlsToPrecache.length;\n const alreadyPrecachedCount = urlsAlreadyPrecached.length;\n if (precachedCount || alreadyPrecachedCount) {\n let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;\n if (alreadyPrecachedCount > 0) {\n message +=\n ` ${alreadyPrecachedCount} ` +\n `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;\n }\n logger.groupCollapsed(message);\n _nestedGroup(`View newly precached URLs.`, urlsToPrecache);\n _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached);\n logger.groupEnd();\n }\n}\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nlet supportStatus;\n/**\n * A utility function that determines whether the current browser supports\n * constructing a new `Response` from a `response.body` stream.\n *\n * @return {boolean} `true`, if the current browser can successfully\n * construct a `Response` from a `response.body` stream, `false` otherwise.\n *\n * @private\n */\nfunction canConstructResponseFromBodyStream() {\n if (supportStatus === undefined) {\n const testResponse = new Response('');\n if ('body' in testResponse) {\n try {\n new Response(testResponse.body);\n supportStatus = true;\n }\n catch (error) {\n supportStatus = false;\n }\n }\n supportStatus = false;\n }\n return supportStatus;\n}\nexport { canConstructResponseFromBodyStream };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { canConstructResponseFromBodyStream } from './_private/canConstructResponseFromBodyStream.js';\nimport { WorkboxError } from './_private/WorkboxError.js';\nimport './_version.js';\n/**\n * Allows developers to copy a response and modify its `headers`, `status`,\n * or `statusText` values (the values settable via a\n * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax}\n * object in the constructor).\n * To modify these values, pass a function as the second argument. That\n * function will be invoked with a single object with the response properties\n * `{headers, status, statusText}`. The return value of this function will\n * be used as the `ResponseInit` for the new `Response`. To change the values\n * either modify the passed parameter(s) and return it, or return a totally\n * new object.\n *\n * This method is intentionally limited to same-origin responses, regardless of\n * whether CORS was used or not.\n *\n * @param {Response} response\n * @param {Function} modifier\n * @memberof workbox-core\n */\nasync function copyResponse(response, modifier) {\n let origin = null;\n // If response.url isn't set, assume it's cross-origin and keep origin null.\n if (response.url) {\n const responseURL = new URL(response.url);\n origin = responseURL.origin;\n }\n if (origin !== self.location.origin) {\n throw new WorkboxError('cross-origin-copy-response', { origin });\n }\n const clonedResponse = response.clone();\n // Create a fresh `ResponseInit` object by cloning the headers.\n const responseInit = {\n headers: new Headers(clonedResponse.headers),\n status: clonedResponse.status,\n statusText: clonedResponse.statusText,\n };\n // Apply any user modifications.\n const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;\n // Create the new response from the body stream and `ResponseInit`\n // modifications. Note: not all browsers support the Response.body stream,\n // so fall back to reading the entire body into memory as a blob.\n const body = canConstructResponseFromBodyStream()\n ? clonedResponse.body\n : await clonedResponse.blob();\n return new Response(body, modifiedResponseInit);\n}\nexport { copyResponse };\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { copyResponse } from 'workbox-core/copyResponse.js';\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { Strategy } from 'workbox-strategies/Strategy.js';\nimport './_version.js';\n/**\n * A {@link workbox-strategies.Strategy} implementation\n * specifically designed to work with\n * {@link workbox-precaching.PrecacheController}\n * to both cache and fetch precached assets.\n *\n * Note: an instance of this class is created automatically when creating a\n * `PrecacheController`; it's generally not necessary to create this yourself.\n *\n * @extends workbox-strategies.Strategy\n * @memberof workbox-precaching\n */\nclass PrecacheStrategy extends Strategy {\n /**\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] Cache name to store and retrieve\n * requests. Defaults to the cache names provided by\n * {@link workbox-core.cacheNames}.\n * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}\n * to use in conjunction with this caching strategy.\n * @param {Object} [options.fetchOptions] Values passed along to the\n * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init}\n * of all fetch() requests made by this strategy.\n * @param {Object} [options.matchOptions] The\n * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions}\n * for any `cache.match()` or `cache.put()` calls made by this strategy.\n * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to\n * get the response from the network if there's a precache miss.\n */\n constructor(options = {}) {\n options.cacheName = cacheNames.getPrecacheName(options.cacheName);\n super(options);\n this._fallbackToNetwork =\n options.fallbackToNetwork === false ? false : true;\n // Redirected responses cannot be used to satisfy a navigation request, so\n // any redirected response must be \"copied\" rather than cloned, so the new\n // response doesn't contain the `redirected` flag. See:\n // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1\n this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);\n }\n /**\n * @private\n * @param {Request|string} request A request to run this strategy for.\n * @param {workbox-strategies.StrategyHandler} handler The event that\n * triggered the request.\n * @return {Promise<Response>}\n */\n async _handle(request, handler) {\n const response = await handler.cacheMatch(request);\n if (response) {\n return response;\n }\n // If this is an `install` event for an entry that isn't already cached,\n // then populate the cache.\n if (handler.event && handler.event.type === 'install') {\n return await this._handleInstall(request, handler);\n }\n // Getting here means something went wrong. An entry that should have been\n // precached wasn't found in the cache.\n return await this._handleFetch(request, handler);\n }\n async _handleFetch(request, handler) {\n let response;\n const params = (handler.params || {});\n // Fall back to the network if we're configured to do so.\n if (this._fallbackToNetwork) {\n if (process.env.NODE_ENV !== 'production') {\n logger.warn(`The precached response for ` +\n `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` +\n `found. Falling back to the network.`);\n }\n const integrityInManifest = params.integrity;\n const integrityInRequest = request.integrity;\n const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;\n // Do not add integrity if the original request is no-cors\n // See https://github.com/GoogleChrome/workbox/issues/3096\n response = await handler.fetch(new Request(request, {\n integrity: request.mode !== 'no-cors'\n ? integrityInRequest || integrityInManifest\n : undefined,\n }));\n // It's only \"safe\" to repair the cache if we're using SRI to guarantee\n // that the response matches the precache manifest's expectations,\n // and there's either a) no integrity property in the incoming request\n // or b) there is an integrity, and it matches the precache manifest.\n // See https://github.com/GoogleChrome/workbox/issues/2858\n // Also if the original request users no-cors we don't use integrity.\n // See https://github.com/GoogleChrome/workbox/issues/3096\n if (integrityInManifest &&\n noIntegrityConflict &&\n request.mode !== 'no-cors') {\n this._useDefaultCacheabilityPluginIfNeeded();\n const wasCached = await handler.cachePut(request, response.clone());\n if (process.env.NODE_ENV !== 'production') {\n if (wasCached) {\n logger.log(`A response for ${getFriendlyURL(request.url)} ` +\n `was used to \"repair\" the precache.`);\n }\n }\n }\n }\n else {\n // This shouldn't normally happen, but there are edge cases:\n // https://github.com/GoogleChrome/workbox/issues/1441\n throw new WorkboxError('missing-precache-entry', {\n cacheName: this.cacheName,\n url: request.url,\n });\n }\n if (process.env.NODE_ENV !== 'production') {\n const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read'));\n // Workbox is going to handle the route.\n // print the routing details to the console.\n logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL(request.url));\n logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);\n logger.groupCollapsed(`View request details here.`);\n logger.log(request);\n logger.groupEnd();\n logger.groupCollapsed(`View response details here.`);\n logger.log(response);\n logger.groupEnd();\n logger.groupEnd();\n }\n return response;\n }\n async _handleInstall(request, handler) {\n this._useDefaultCacheabilityPluginIfNeeded();\n const response = await handler.fetch(request);\n // Make sure we defer cachePut() until after we know the response\n // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737\n const wasCached = await handler.cachePut(request, response.clone());\n if (!wasCached) {\n // Throwing here will lead to the `install` handler failing, which\n // we want to do if *any* of the responses aren't safe to cache.\n throw new WorkboxError('bad-precaching-response', {\n url: request.url,\n status: response.status,\n });\n }\n return response;\n }\n /**\n * This method is complex, as there a number of things to account for:\n *\n * The `plugins` array can be set at construction, and/or it might be added to\n * to at any time before the strategy is used.\n *\n * At the time the strategy is used (i.e. during an `install` event), there\n * needs to be at least one plugin that implements `cacheWillUpdate` in the\n * array, other than `copyRedirectedCacheableResponsesPlugin`.\n *\n * - If this method is called and there are no suitable `cacheWillUpdate`\n * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.\n *\n * - If this method is called and there is exactly one `cacheWillUpdate`, then\n * we don't have to do anything (this might be a previously added\n * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).\n *\n * - If this method is called and there is more than one `cacheWillUpdate`,\n * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,\n * we need to remove it. (This situation is unlikely, but it could happen if\n * the strategy is used multiple times, the first without a `cacheWillUpdate`,\n * and then later on after manually adding a custom `cacheWillUpdate`.)\n *\n * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.\n *\n * @private\n */\n _useDefaultCacheabilityPluginIfNeeded() {\n let defaultPluginIndex = null;\n let cacheWillUpdatePluginCount = 0;\n for (const [index, plugin] of this.plugins.entries()) {\n // Ignore the copy redirected plugin when determining what to do.\n if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {\n continue;\n }\n // Save the default plugin's index, in case it needs to be removed.\n if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {\n defaultPluginIndex = index;\n }\n if (plugin.cacheWillUpdate) {\n cacheWillUpdatePluginCount++;\n }\n }\n if (cacheWillUpdatePluginCount === 0) {\n this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);\n }\n else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {\n // Only remove the default plugin; multiple custom plugins are allowed.\n this.plugins.splice(defaultPluginIndex, 1);\n }\n // Nothing needs to be done if cacheWillUpdatePluginCount is 1\n }\n}\nPrecacheStrategy.defaultPrecacheCacheabilityPlugin = {\n async cacheWillUpdate({ response }) {\n if (!response || response.status >= 400) {\n return null;\n }\n return response;\n },\n};\nPrecacheStrategy.copyRedirectedCacheableResponsesPlugin = {\n async cacheWillUpdate({ response }) {\n return response.redirected ? await copyResponse(response) : response;\n },\n};\nexport { PrecacheStrategy };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { WorkboxError } from 'workbox-core/_private/WorkboxError.js';\nimport { waitUntil } from 'workbox-core/_private/waitUntil.js';\nimport { createCacheKey } from './utils/createCacheKey.js';\nimport { PrecacheInstallReportPlugin } from './utils/PrecacheInstallReportPlugin.js';\nimport { PrecacheCacheKeyPlugin } from './utils/PrecacheCacheKeyPlugin.js';\nimport { printCleanupDetails } from './utils/printCleanupDetails.js';\nimport { printInstallDetails } from './utils/printInstallDetails.js';\nimport { PrecacheStrategy } from './PrecacheStrategy.js';\nimport './_version.js';\n/**\n * Performs efficient precaching of assets.\n *\n * @memberof workbox-precaching\n */\nclass PrecacheController {\n /**\n * Create a new PrecacheController.\n *\n * @param {Object} [options]\n * @param {string} [options.cacheName] The cache to use for precaching.\n * @param {string} [options.plugins] Plugins to use when precaching as well\n * as responding to fetch events for precached assets.\n * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to\n * get the response from the network if there's a precache miss.\n */\n constructor({ cacheName, plugins = [], fallbackToNetwork = true, } = {}) {\n this._urlsToCacheKeys = new Map();\n this._urlsToCacheModes = new Map();\n this._cacheKeysToIntegrities = new Map();\n this._strategy = new PrecacheStrategy({\n cacheName: cacheNames.getPrecacheName(cacheName),\n plugins: [\n ...plugins,\n new PrecacheCacheKeyPlugin({ precacheController: this }),\n ],\n fallbackToNetwork,\n });\n // Bind the install and activate methods to the instance.\n this.install = this.install.bind(this);\n this.activate = this.activate.bind(this);\n }\n /**\n * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and\n * used to cache assets and respond to fetch events.\n */\n get strategy() {\n return this._strategy;\n }\n /**\n * Adds items to the precache list, removing any duplicates and\n * stores the files in the\n * {@link workbox-core.cacheNames|\"precache cache\"} when the service\n * worker installs.\n *\n * This method can be called multiple times.\n *\n * @param {Array<Object|string>} [entries=[]] Array of entries to precache.\n */\n precache(entries) {\n this.addToCacheList(entries);\n if (!this._installAndActiveListenersAdded) {\n self.addEventListener('install', this.install);\n self.addEventListener('activate', this.activate);\n this._installAndActiveListenersAdded = true;\n }\n }\n /**\n * This method will add items to the precache list, removing duplicates\n * and ensuring the information is valid.\n *\n * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries\n * Array of entries to precache.\n */\n addToCacheList(entries) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isArray(entries, {\n moduleName: 'workbox-precaching',\n className: 'PrecacheController',\n funcName: 'addToCacheList',\n paramName: 'entries',\n });\n }\n const urlsToWarnAbout = [];\n for (const entry of entries) {\n // See https://github.com/GoogleChrome/workbox/issues/2259\n if (typeof entry === 'string') {\n urlsToWarnAbout.push(entry);\n }\n else if (entry && entry.revision === undefined) {\n urlsToWarnAbout.push(entry.url);\n }\n const { cacheKey, url } = createCacheKey(entry);\n const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default';\n if (this._urlsToCacheKeys.has(url) &&\n this._urlsToCacheKeys.get(url) !== cacheKey) {\n throw new WorkboxError('add-to-cache-list-conflicting-entries', {\n firstEntry: this._urlsToCacheKeys.get(url),\n secondEntry: cacheKey,\n });\n }\n if (typeof entry !== 'string' && entry.integrity) {\n if (this._cacheKeysToIntegrities.has(cacheKey) &&\n this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {\n throw new WorkboxError('add-to-cache-list-conflicting-integrities', {\n url,\n });\n }\n this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);\n }\n this._urlsToCacheKeys.set(url, cacheKey);\n this._urlsToCacheModes.set(url, cacheMode);\n if (urlsToWarnAbout.length > 0) {\n const warningMessage = `Workbox is precaching URLs without revision ` +\n `info: ${urlsToWarnAbout.join(', ')}\\nThis is generally NOT safe. ` +\n `Learn more at https://bit.ly/wb-precache`;\n if (process.env.NODE_ENV === 'production') {\n // Use console directly to display this warning without bloating\n // bundle sizes by pulling in all of the logger codebase in prod.\n console.warn(warningMessage);\n }\n else {\n logger.warn(warningMessage);\n }\n }\n }\n }\n /**\n * Precaches new and updated assets. Call this method from the service worker\n * install event.\n *\n * Note: this method calls `event.waitUntil()` for you, so you do not need\n * to call it yourself in your event handlers.\n *\n * @param {ExtendableEvent} event\n * @return {Promise<workbox-precaching.InstallResult>}\n */\n install(event) {\n // waitUntil returns Promise<any>\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return waitUntil(event, async () => {\n const installReportPlugin = new PrecacheInstallReportPlugin();\n this.strategy.plugins.push(installReportPlugin);\n // Cache entries one at a time.\n // See https://github.com/GoogleChrome/workbox/issues/2528\n for (const [url, cacheKey] of this._urlsToCacheKeys) {\n const integrity = this._cacheKeysToIntegrities.get(cacheKey);\n const cacheMode = this._urlsToCacheModes.get(url);\n const request = new Request(url, {\n integrity,\n cache: cacheMode,\n credentials: 'same-origin',\n });\n await Promise.all(this.strategy.handleAll({\n params: { cacheKey },\n request,\n event,\n }));\n }\n const { updatedURLs, notUpdatedURLs } = installReportPlugin;\n if (process.env.NODE_ENV !== 'production') {\n printInstallDetails(updatedURLs, notUpdatedURLs);\n }\n return { updatedURLs, notUpdatedURLs };\n });\n }\n /**\n * Deletes assets that are no longer present in the current precache manifest.\n * Call this method from the service worker activate event.\n *\n * Note: this method calls `event.waitUntil()` for you, so you do not need\n * to call it yourself in your event handlers.\n *\n * @param {ExtendableEvent} event\n * @return {Promise<workbox-precaching.CleanupResult>}\n */\n activate(event) {\n // waitUntil returns Promise<any>\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return waitUntil(event, async () => {\n const cache = await self.caches.open(this.strategy.cacheName);\n const currentlyCachedRequests = await cache.keys();\n const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());\n const deletedURLs = [];\n for (const request of currentlyCachedRequests) {\n if (!expectedCacheKeys.has(request.url)) {\n await cache.delete(request);\n deletedURLs.push(request.url);\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n printCleanupDetails(deletedURLs);\n }\n return { deletedURLs };\n });\n }\n /**\n * Returns a mapping of a precached URL to the corresponding cache key, taking\n * into account the revision information for the URL.\n *\n * @return {Map<string, string>} A URL to cache key mapping.\n */\n getURLsToCacheKeys() {\n return this._urlsToCacheKeys;\n }\n /**\n * Returns a list of all the URLs that have been precached by the current\n * service worker.\n *\n * @return {Array<string>} The precached URLs.\n */\n getCachedURLs() {\n return [...this._urlsToCacheKeys.keys()];\n }\n /**\n * Returns the cache key used for storing a given URL. If that URL is\n * unversioned, like `/index.html', then the cache key will be the original\n * URL with a search parameter appended to it.\n *\n * @param {string} url A URL whose cache key you want to look up.\n * @return {string} The versioned URL that corresponds to a cache key\n * for the original URL, or undefined if that URL isn't precached.\n */\n getCacheKeyForURL(url) {\n const urlObject = new URL(url, location.href);\n return this._urlsToCacheKeys.get(urlObject.href);\n }\n /**\n * @param {string} url A cache key whose SRI you want to look up.\n * @return {string} The subresource integrity associated with the cache key,\n * or undefined if it's not set.\n */\n getIntegrityForCacheKey(cacheKey) {\n return this._cacheKeysToIntegrities.get(cacheKey);\n }\n /**\n * This acts as a drop-in replacement for\n * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)\n * with the following differences:\n *\n * - It knows what the name of the precache is, and only checks in that cache.\n * - It allows you to pass in an \"original\" URL without versioning parameters,\n * and it will automatically look up the correct cache key for the currently\n * active revision of that URL.\n *\n * E.g., `matchPrecache('index.html')` will find the correct precached\n * response for the currently active service worker, even if the actual cache\n * key is `'/index.html?__WB_REVISION__=1234abcd'`.\n *\n * @param {string|Request} request The key (without revisioning parameters)\n * to look up in the precache.\n * @return {Promise<Response|undefined>}\n */\n async matchPrecache(request) {\n const url = request instanceof Request ? request.url : request;\n const cacheKey = this.getCacheKeyForURL(url);\n if (cacheKey) {\n const cache = await self.caches.open(this.strategy.cacheName);\n return cache.match(cacheKey);\n }\n return undefined;\n }\n /**\n * Returns a function that looks up `url` in the precache (taking into\n * account revision information), and returns the corresponding `Response`.\n *\n * @param {string} url The precached URL which will be used to lookup the\n * `Response`.\n * @return {workbox-routing~handlerCallback}\n */\n createHandlerBoundToURL(url) {\n const cacheKey = this.getCacheKeyForURL(url);\n if (!cacheKey) {\n throw new WorkboxError('non-precached-url', { url });\n }\n return (options) => {\n options.request = new Request(url);\n options.params = Object.assign({ cacheKey }, options.params);\n return this.strategy.handle(options);\n };\n }\n}\nexport { PrecacheController };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { PrecacheController } from '../PrecacheController.js';\nimport '../_version.js';\nlet precacheController;\n/**\n * @return {PrecacheController}\n * @private\n */\nexport const getOrCreatePrecacheController = () => {\n if (!precacheController) {\n precacheController = new PrecacheController();\n }\n return precacheController;\n};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\n/**\n * Removes any URL search parameters that should be ignored.\n *\n * @param {URL} urlObject The original URL.\n * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against\n * each search parameter name. Matches mean that the search parameter should be\n * ignored.\n * @return {URL} The URL with any ignored search parameters removed.\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) {\n // Convert the iterable into an array at the start of the loop to make sure\n // deletion doesn't mess up iteration.\n for (const paramName of [...urlObject.searchParams.keys()]) {\n if (ignoreURLParametersMatching.some((regExp) => regExp.test(paramName))) {\n urlObject.searchParams.delete(paramName);\n }\n }\n return urlObject;\n}\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { removeIgnoredSearchParams } from './removeIgnoredSearchParams.js';\nimport '../_version.js';\n/**\n * Generator function that yields possible variations on the original URL to\n * check, one at a time.\n *\n * @param {string} url\n * @param {Object} options\n *\n * @private\n * @memberof workbox-precaching\n */\nexport function* generateURLVariations(url, { ignoreURLParametersMatching = [/^utm_/, /^fbclid$/], directoryIndex = 'index.html', cleanURLs = true, urlManipulation, } = {}) {\n const urlObject = new URL(url, location.href);\n urlObject.hash = '';\n yield urlObject.href;\n const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);\n yield urlWithoutIgnoredParams.href;\n if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {\n const directoryURL = new URL(urlWithoutIgnoredParams.href);\n directoryURL.pathname += directoryIndex;\n yield directoryURL.href;\n }\n if (cleanURLs) {\n const cleanURL = new URL(urlWithoutIgnoredParams.href);\n cleanURL.pathname += '.html';\n yield cleanURL.href;\n }\n if (urlManipulation) {\n const additionalURLs = urlManipulation({ url: urlObject });\n for (const urlToAttempt of additionalURLs) {\n yield urlToAttempt.href;\n }\n }\n}\n","/*\n Copyright 2020 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';\nimport { Route } from 'workbox-routing/Route.js';\nimport { generateURLVariations } from './utils/generateURLVariations.js';\nimport './_version.js';\n/**\n * A subclass of {@link workbox-routing.Route} that takes a\n * {@link workbox-precaching.PrecacheController}\n * instance and uses it to match incoming requests and handle fetching\n * responses from the precache.\n *\n * @memberof workbox-precaching\n * @extends workbox-routing.Route\n */\nclass PrecacheRoute extends Route {\n /**\n * @param {PrecacheController} precacheController A `PrecacheController`\n * instance used to both match requests and respond to fetch events.\n * @param {Object} [options] Options to control how requests are matched\n * against the list of precached URLs.\n * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will\n * check cache entries for a URLs ending with '/' to see if there is a hit when\n * appending the `directoryIndex` value.\n * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An\n * array of regex's to remove search params when looking for a cache match.\n * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will\n * check the cache for the URL with a `.html` added to the end of the end.\n * @param {workbox-precaching~urlManipulation} [options.urlManipulation]\n * This is a function that should take a URL and return an array of\n * alternative URLs that should be checked for precache matches.\n */\n constructor(precacheController, options) {\n const match = ({ request, }) => {\n const urlsToCacheKeys = precacheController.getURLsToCacheKeys();\n for (const possibleURL of generateURLVariations(request.url, options)) {\n const cacheKey = urlsToCacheKeys.get(possibleURL);\n if (cacheKey) {\n const integrity = precacheController.getIntegrityForCacheKey(cacheKey);\n return { cacheKey, integrity };\n }\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`Precaching did not find a match for ` + getFriendlyURL(request.url));\n }\n return;\n };\n super(match, precacheController.strategy);\n }\n}\nexport { PrecacheRoute };\n","/*\n Copyright 2019 Google LLC\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { registerRoute } from 'workbox-routing/registerRoute.js';\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport { PrecacheRoute } from './PrecacheRoute.js';\nimport './_version.js';\n/**\n * Add a `fetch` listener to the service worker that will\n * respond to\n * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}\n * with precached assets.\n *\n * Requests for assets that aren't precached, the `FetchEvent` will not be\n * responded to, allowing the event to fall through to other `fetch` event\n * listeners.\n *\n * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute}\n * options.\n *\n * @memberof workbox-precaching\n */\nfunction addRoute(options) {\n const precacheController = getOrCreatePrecacheController();\n const precacheRoute = new PrecacheRoute(precacheController, options);\n registerRoute(precacheRoute);\n}\nexport { addRoute };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport './_version.js';\n/**\n * Adds items to the precache list, removing any duplicates and\n * stores the files in the\n * {@link workbox-core.cacheNames|\"precache cache\"} when the service\n * worker installs.\n *\n * This method can be called multiple times.\n *\n * Please note: This method **will not** serve any of the cached files for you.\n * It only precaches files. To respond to a network request you call\n * {@link workbox-precaching.addRoute}.\n *\n * If you have a single array of files to precache, you can just call\n * {@link workbox-precaching.precacheAndRoute}.\n *\n * @param {Array<Object|string>} [entries=[]] Array of entries to precache.\n *\n * @memberof workbox-precaching\n */\nfunction precache(entries) {\n const precacheController = getOrCreatePrecacheController();\n precacheController.precache(entries);\n}\nexport { precache };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { addRoute } from './addRoute.js';\nimport { precache } from './precache.js';\nimport './_version.js';\n/**\n * This method will add entries to the precache list and add a route to\n * respond to fetch events.\n *\n * This is a convenience method that will call\n * {@link workbox-precaching.precache} and\n * {@link workbox-precaching.addRoute} in a single call.\n *\n * @param {Array<Object|string>} entries Array of entries to precache.\n * @param {Object} [options] See the\n * {@link workbox-precaching.PrecacheRoute} options.\n *\n * @memberof workbox-precaching\n */\nfunction precacheAndRoute(entries, options) {\n precache(entries);\n addRoute(options);\n}\nexport { precacheAndRoute };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport '../_version.js';\nconst SUBSTRING_TO_FIND = '-precache-';\n/**\n * Cleans up incompatible precaches that were created by older versions of\n * Workbox, by a service worker registered under the current scope.\n *\n * This is meant to be called as part of the `activate` event.\n *\n * This should be safe to use as long as you don't include `substringToFind`\n * (defaulting to `-precache-`) in your non-precache cache names.\n *\n * @param {string} currentPrecacheName The cache name currently in use for\n * precaching. This cache won't be deleted.\n * @param {string} [substringToFind='-precache-'] Cache names which include this\n * substring will be deleted (excluding `currentPrecacheName`).\n * @return {Array<string>} A list of all the cache names that were deleted.\n *\n * @private\n * @memberof workbox-precaching\n */\nconst deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => {\n const cacheNames = await self.caches.keys();\n const cacheNamesToDelete = cacheNames.filter((cacheName) => {\n return (cacheName.includes(substringToFind) &&\n cacheName.includes(self.registration.scope) &&\n cacheName !== currentPrecacheName);\n });\n await Promise.all(cacheNamesToDelete.map((cacheName) => self.caches.delete(cacheName)));\n return cacheNamesToDelete;\n};\nexport { deleteOutdatedCaches };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { cacheNames } from 'workbox-core/_private/cacheNames.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { deleteOutdatedCaches } from './utils/deleteOutdatedCaches.js';\nimport './_version.js';\n/**\n * Adds an `activate` event listener which will clean up incompatible\n * precaches that were created by older versions of Workbox.\n *\n * @memberof workbox-precaching\n */\nfunction cleanupOutdatedCaches() {\n // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705\n self.addEventListener('activate', ((event) => {\n const cacheName = cacheNames.getPrecacheName();\n event.waitUntil(deleteOutdatedCaches(cacheName).then((cachesDeleted) => {\n if (process.env.NODE_ENV !== 'production') {\n if (cachesDeleted.length > 0) {\n logger.log(`The following out-of-date precaches were cleaned up ` +\n `automatically:`, cachesDeleted);\n }\n }\n }));\n }));\n}\nexport { cleanupOutdatedCaches };\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { assert } from 'workbox-core/_private/assert.js';\nimport { logger } from 'workbox-core/_private/logger.js';\nimport { Route } from './Route.js';\nimport './_version.js';\n/**\n * NavigationRoute makes it easy to create a\n * {@link workbox-routing.Route} that matches for browser\n * [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}.\n *\n * It will only match incoming Requests whose\n * {@link https://fetch.spec.whatwg.org/#concept-request-mode|mode}\n * is set to `navigate`.\n *\n * You can optionally only apply this route to a subset of navigation requests\n * by using one or both of the `denylist` and `allowlist` parameters.\n *\n * @memberof workbox-routing\n * @extends workbox-routing.Route\n */\nclass NavigationRoute extends Route {\n /**\n * If both `denylist` and `allowlist` are provided, the `denylist` will\n * take precedence and the request will not match this route.\n *\n * The regular expressions in `allowlist` and `denylist`\n * are matched against the concatenated\n * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname}\n * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search}\n * portions of the requested URL.\n *\n * *Note*: These RegExps may be evaluated against every destination URL during\n * a navigation. Avoid using\n * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077),\n * or else your users may see delays when navigating your site.\n *\n * @param {workbox-routing~handlerCallback} handler A callback\n * function that returns a Promise resulting in a Response.\n * @param {Object} options\n * @param {Array<RegExp>} [options.denylist] If any of these patterns match,\n * the route will not handle the request (even if a allowlist RegExp matches).\n * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns\n * match the URL's pathname and search parameter, the route will handle the\n * request (assuming the denylist doesn't match).\n */\n constructor(handler, { allowlist = [/./], denylist = [] } = {}) {\n if (process.env.NODE_ENV !== 'production') {\n assert.isArrayOfClass(allowlist, RegExp, {\n moduleName: 'workbox-routing',\n className: 'NavigationRoute',\n funcName: 'constructor',\n paramName: 'options.allowlist',\n });\n assert.isArrayOfClass(denylist, RegExp, {\n moduleName: 'workbox-routing',\n className: 'NavigationRoute',\n funcName: 'constructor',\n paramName: 'options.denylist',\n });\n }\n super((options) => this._match(options), handler);\n this._allowlist = allowlist;\n this._denylist = denylist;\n }\n /**\n * Routes match handler.\n *\n * @param {Object} options\n * @param {URL} options.url\n * @param {Request} options.request\n * @return {boolean}\n *\n * @private\n */\n _match({ url, request }) {\n if (request && request.mode !== 'navigate') {\n return false;\n }\n const pathnameAndSearch = url.pathname + url.search;\n for (const regExp of this._denylist) {\n if (regExp.test(pathnameAndSearch)) {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`The navigation route ${pathnameAndSearch} is not ` +\n `being used, since the URL matches this denylist pattern: ` +\n `${regExp.toString()}`);\n }\n return false;\n }\n }\n if (this._allowlist.some((regExp) => regExp.test(pathnameAndSearch))) {\n if (process.env.NODE_ENV !== 'production') {\n logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`);\n }\n return true;\n }\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`The navigation route ${pathnameAndSearch} is not ` +\n `being used, since the URL being navigated to doesn't ` +\n `match the allowlist.`);\n }\n return false;\n }\n}\nexport { NavigationRoute };\n","/*\n Copyright 2019 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\nimport { getOrCreatePrecacheController } from './utils/getOrCreatePrecacheController.js';\nimport './_version.js';\n/**\n * Helper function that calls\n * {@link PrecacheController#createHandlerBoundToURL} on the default\n * {@link PrecacheController} instance.\n *\n * If you are creating your own {@link PrecacheController}, then call the\n * {@link PrecacheController#createHandlerBoundToURL} on that instance,\n * instead of using this function.\n *\n * @param {string} url The precached URL which will be used to lookup the\n * `Response`.\n * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the\n * response from the network if there's a precache miss.\n * @return {workbox-routing~handlerCallback}\n *\n * @memberof workbox-precaching\n */\nfunction createHandlerBoundToURL(url) {\n const precacheController = getOrCreatePrecacheController();\n return precacheController.createHandlerBoundToURL(url);\n}\nexport { createHandlerBoundToURL };\n"],"names":["self","_","e","logger","globalThis","__WB_DISABLE_DEV_LOGS","inGroup","methodToColorMap","debug","log","warn","error","groupCollapsed","groupEnd","print","method","args","test","navigator","userAgent","console","styles","logPrefix","join","api","loggerMethods","Object","keys","key","messages","invalid-value","paramName","validValueDescription","value","Error","JSON","stringify","not-an-array","moduleName","className","funcName","incorrect-type","expectedType","classNameStr","incorrect-class","expectedClassName","isReturnValueProblem","missing-a-method","expectedMethod","add-to-cache-list-unexpected-type","entry","add-to-cache-list-conflicting-entries","firstEntry","secondEntry","plugin-error-request-will-fetch","thrownErrorMessage","invalid-cache-name","cacheNameId","unregister-route-but-not-found-with-method","unregister-route-route-not-registered","queue-replay-failed","name","duplicate-queue-name","expired-test-without-max-age","methodName","unsupported-route-type","not-array-of-class","expectedClass","max-entries-or-age-required","statuses-or-headers-required","invalid-string","channel-name-required","invalid-responses-are-same-args","expire-custom-caches-only","unit-must-be-bytes","normalizedRangeHeader","single-range-only","invalid-range-values","no-range-header","range-not-satisfiable","size","start","end","attempt-to-cache-non-get-request","url","cache-put-with-no-response","no-response","message","bad-precaching-response","status","non-precached-url","add-to-cache-list-conflicting-integrities","missing-precache-entry","cacheName","cross-origin-copy-response","origin","opaque-streams-source","type","generatorFunction","code","details","messageGenerator","WorkboxError","constructor","errorCode","isArray","Array","hasMethod","object","isType","isInstance","isOneOf","validValues","includes","isArrayOfClass","item","finalAssertExports","defaultMethod","validMethods","normalizeHandler","handler","assert","handle","Route","match","setCatchHandler","catchHandler","RegExpRoute","regExp","RegExp","result","exec","href","location","index","toString","slice","getFriendlyURL","urlObj","URL","String","replace","Router","_routes","Map","_defaultHandlerMap","routes","addFetchListener","addEventListener","event","request","responsePromise","handleRequest","respondWith","addCacheListener","data","payload","urlsToCache","requestPromises","Promise","all","map","Request","waitUntil","ports","then","postMessage","protocol","startsWith","sameOrigin","params","route","findMatchingRoute","debugMessages","push","has","get","forEach","msg","err","reject","_catchHandler","catch","catchErr","matchResult","length","undefined","setDefaultHandler","set","registerRoute","unregisterRoute","routeIndex","indexOf","splice","defaultRouter","getOrCreateDefaultRouter","capture","captureUrl","valueToCheck","pathname","wildcards","matchCallback","CacheableResponse","config","statuses","headers","_statuses","_headers","isResponseCacheable","response","Response","cacheable","some","headerName","logFriendlyHeaders","CacheableResponsePlugin","cacheWillUpdate","_cacheableResponse","_cacheNameDetails","googleAnalytics","precache","prefix","runtime","suffix","registration","scope","_createCacheName","filter","eachCacheNameDetail","fn","cacheNames","updateDetails","getGoogleAnalyticsName","userCacheName","getPrecacheName","getPrefix","getRuntimeName","getSuffix","stripParams","fullURL","ignoreParams","strippedURL","param","searchParams","delete","cacheMatchIgnoreParams","cache","matchOptions","strippedRequestURL","keysOptions","assign","ignoreSearch","cacheKeys","cacheKey","strippedCacheKeyURL","Deferred","promise","resolve","quotaErrorCallbacks","Set","executeQuotaErrorCallbacks","callback","timeout","ms","setTimeout","toRequest","input","StrategyHandler","strategy","options","_cacheKeys","ExtendableEvent","_strategy","_handlerDeferred","_extendLifetimePromises","_plugins","plugins","_pluginStateMap","plugin","fetch","mode","FetchEvent","preloadResponse","possiblePreloadResponse","originalRequest","hasCallback","clone","cb","iterateCallbacks","pluginFilteredRequest","fetchResponse","fetchOptions","runCallbacks","fetchAndCachePut","responseClone","cachePut","cacheMatch","cachedResponse","effectiveRequest","getCacheKey","multiMatchOptions","caches","vary","responseToCache","_ensureResponseSafeToCache","open","hasCacheUpdateCallback","oldResponse","put","newResponse","state","statefulCallback","statefulParam","doneWaiting","shift","destroy","pluginsUsed","Strategy","responseDone","handleAll","_getResponse","handlerDone","_awaitComplete","_handle","waitUntilError","strategyStart","strategyName","printFinalResponse","CacheFirst","logs","NetworkOnly","_networkTimeoutSeconds","networkTimeoutSeconds","promises","timeoutPromise","race","clientsClaim","clients","claim","asyncFn","returnPromise","REVISION_SEARCH_PARAM","createCacheKey","urlObject","revision","cacheKeyURL","originalURL","PrecacheInstallReportPlugin","updatedURLs","notUpdatedURLs","handlerWillStart","cachedResponseWillBeUsed","PrecacheCacheKeyPlugin","precacheController","cacheKeyWillBeUsed","_precacheController","getCacheKeyForURL","logGroup","groupTitle","deletedURLs","printCleanupDetails","deletionCount","_nestedGroup","urls","printInstallDetails","urlsToPrecache","urlsAlreadyPrecached","precachedCount","alreadyPrecachedCount","supportStatus","canConstructResponseFromBodyStream","testResponse","body","copyResponse","modifier","responseURL","clonedResponse","responseInit","Headers","statusText","modifiedResponseInit","blob","PrecacheStrategy","_fallbackToNetwork","fallbackToNetwork","copyRedirectedCacheableResponsesPlugin","_handleInstall","_handleFetch","integrityInManifest","integrity","integrityInRequest","noIntegrityConflict","_useDefaultCacheabilityPluginIfNeeded","wasCached","defaultPluginIndex","cacheWillUpdatePluginCount","entries","defaultPrecacheCacheabilityPlugin","redirected","PrecacheController","_urlsToCacheKeys","_urlsToCacheModes","_cacheKeysToIntegrities","install","bind","activate","addToCacheList","_installAndActiveListenersAdded","urlsToWarnAbout","cacheMode","warningMessage","installReportPlugin","credentials","currentlyCachedRequests","expectedCacheKeys","values","getURLsToCacheKeys","getCachedURLs","getIntegrityForCacheKey","matchPrecache","createHandlerBoundToURL","getOrCreatePrecacheController","removeIgnoredSearchParams","ignoreURLParametersMatching","generateURLVariations","directoryIndex","cleanURLs","urlManipulation","hash","urlWithoutIgnoredParams","endsWith","directoryURL","cleanURL","additionalURLs","urlToAttempt","PrecacheRoute","urlsToCacheKeys","possibleURL","addRoute","precacheRoute","precacheAndRoute","SUBSTRING_TO_FIND","deleteOutdatedCaches","currentPrecacheName","substringToFind","cacheNamesToDelete","cleanupOutdatedCaches","cachesDeleted","NavigationRoute","allowlist","denylist","_match","_allowlist","_denylist","pathnameAndSearch","search"],"mappings":"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xH,MAAM,CAAc,YAAA,CAAA,GAC3D,wBAAwB,CAAC,CAAA;IACjC,aAAA;IACJ,WAAA;IACJ,SAAA;IACJ,OAAA;IACJ,KAAA;IACA,IAAA,OAAOsN,eAAe,CAAA;IAC1B,GAAA;IACJ;;ICngBA;IACA;AACA;IACA;IACA;IACA;IACA;IAOA;IACA;IACA;IACA;IACA;IACA,MAAMc,QAAQ,CAAC;IACX;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACItN,EAAAA,WAAWA,CAACsK,OAAO,GAAG,EAAE,EAAE;IACtB;IACR;IACA;IACA;IACA;IACA;IACA;QACQ,IAAI,CAAChL,SAAS,GAAGyI,UAAU,CAACM,cAAc,CAACiC,OAAO,CAAChL,SAAS,CAAC,CAAA;IAC7D;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAACuL,OAAO,GAAGP,OAAO,CAACO,OAAO,IAAI,EAAE,CAAA;IACpC;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAACe,YAAY,GAAGtB,OAAO,CAACsB,YAAY,CAAA;IACxC;IACR;IACA;IACA;IACA;IACA;IACA;IACQ,IAAA,IAAI,CAAC5C,YAAY,GAAGsB,OAAO,CAACtB,YAAY,CAAA;IAC5C,GAAA;IACA;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;MACI7H,MAAMA,CAACmJ,OAAO,EAAE;QACZ,MAAM,CAACiD,YAAY,CAAC,GAAG,IAAI,CAACC,SAAS,CAAClD,OAAO,CAAC,CAAA;IAC9C,IAAA,OAAOiD,YAAY,CAAA;IACvB,GAAA;IACA;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;MACIC,SAASA,CAAClD,OAAO,EAAE;IACf;QACA,IAAIA,OAAO,YAAYY,UAAU,EAAE;I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"} \ No newline at end of file diff --git a/eslint.config.js.bak b/eslint.config.js.bak deleted file mode 100644 index 82c2e20..0000000 --- a/eslint.config.js.bak +++ /dev/null @@ -1,28 +0,0 @@ -import js from '@eslint/js'; -import globals from 'globals'; -import reactHooks from 'eslint-plugin-react-hooks'; -import reactRefresh from 'eslint-plugin-react-refresh'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { ignores: ['dist'] }, - { - extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, - }, - rules: { - ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, - } -); diff --git a/github-profile-README.md b/github-profile-README.md deleted file mode 100644 index df177f2..0000000 --- a/github-profile-README.md +++ /dev/null @@ -1,83 +0,0 @@ -# 👋 Hi, I'm Damjan Savić - Fullstack Developer - -🚀 **Specializing in:** Python | JavaScript | React | Next.js | TypeScript | AI/ML with OLLAMA - -## 🎯 Current Focus -- 🤖 Building AI-powered applications with OLLAMA -- 🏢 Developing ERP integration solutions -- 🛒 Creating scalable e-commerce platforms -- ⚡ Automating business processes with Python - -## 💻 Tech Stack - -### Languages -![Python](https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white) -![JavaScript](https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black) -![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white) - -### Frontend -![React](https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB) -![Next.js](https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white) -![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=for-the-badge&logo=tailwind-css&logoColor=white) - -### Backend -![FastAPI](https://img.shields.io/badge/FastAPI-009688?style=for-the-badge&logo=FastAPI&logoColor=white) -![Django](https://img.shields.io/badge/Django-092E20?style=for-the-badge&logo=django&logoColor=white) -![Node.js](https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white) - -### AI/ML -![OLLAMA](https://img.shields.io/badge/OLLAMA-000000?style=for-the-badge&logo=ai&logoColor=white) -![LangChain](https://img.shields.io/badge/LangChain-121212?style=for-the-badge) - -### Databases -![PostgreSQL](https://img.shields.io/badge/PostgreSQL-316192?style=for-the-badge&logo=postgresql&logoColor=white) -![MongoDB](https://img.shields.io/badge/MongoDB-4EA94B?style=for-the-badge&logo=mongodb&logoColor=white) - -### DevOps -![Docker](https://img.shields.io/badge/Docker-2CA5E0?style=for-the-badge&logo=docker&logoColor=white) -![AWS](https://img.shields.io/badge/AWS-FF9900?style=for-the-badge&logo=amazonaws&logoColor=white) - -## 📊 GitHub Stats -![GitHub Stats](https://github-readme-stats.vercel.app/api?username=damjansavic&show_icons=true&theme=dark&count_private=true) - -![Top Languages](https://github-readme-stats.vercel.app/api/top-langs/?username=damjansavic&layout=compact&theme=dark) - -## 🏆 Featured Projects - -### 🤖 [OLLAMA Chat Interface](https://github.com/damjansavic/ollama-chat) -Local AI chat application with privacy-first approach -- **Tech:** Python, FastAPI, React, OLLAMA -- **Features:** Stream responses, conversation history, multiple models - -### 🛒 [E-Commerce Automation Suite](https://github.com/damjansavic/ecommerce-automation) -Automated inventory and order management system -- **Tech:** Python, Django, React, PostgreSQL -- **Impact:** 60% reduction in manual processing time - -### 📊 [ERP Integration Platform](https://github.com/damjansavic/erp-integration) -Seamless connection between multiple ERP systems -- **Tech:** Python, FastAPI, Docker, RabbitMQ -- **Scale:** Processing 1M+ transactions daily - -## 💡 What I Offer - -- **🚀 Performance:** Applications optimized for speed and scalability -- **🔒 Security:** Privacy-first AI solutions with OLLAMA -- **📈 Business Impact:** Solutions that deliver measurable ROI -- **🛠️ Full Stack:** From database design to user interface - -## 📫 Connect With Me -- 🌐 [Portfolio](https://damjan-savic.com) -- 💼 [LinkedIn](https://linkedin.com/in/damjansavic) -- 📧 [Email](mailto:info@damjan-savic.com) - -## 🎯 Open for Opportunities -I'm interested in projects involving: -- AI/ML integration with business applications -- High-performance web applications -- Process automation and optimization -- E-commerce and ERP solutions - ---- - -*"Building the future, one line of code at a time"* 🚀 diff --git a/img.png b/img.png deleted file mode 100644 index ed94c30686952340cea23bfa24091a16b63d7177..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62368 zcmce-^;;WX_XXPG6n9!2f>YeJK!60dBE?;b1a~Oz!L3+vXmNMf;#S<<-GcP;eqOoX z=l%irmn6^0oSd1-p0)SdYn^aqMH!5DAKtxr^9BP90;;}w1Ml+s`4<Y(>uc9k-R7G& z-`;?M;_B}D$88pgWV2~!J{@gtE05(>9O9ruXV`Ye;daKOvtp6XP(&FD!;jurz$J(h zND4&a_!bn#YlvrygFv@vNJ6qi99p%<`UuMua%&ZypZ|O3ajP;^a5ycCV!80xV?SH+ zGt*j=O+_q=>?M8fC>Tf`2Lj-R|LaXr{w4h-;QtPg=Ffot^CbXx2sJ8E@Sg+!84~c1 zQ>FjkTmU`0>f!$m4c|tPmd*Tse<`CARFd%DWdQMb#EdTgUh-cv9N}mR6#vgz82<mY z=qZ2vkoiT~+*<#Ym%#_Ov*}M|F&k^JM?W-O&jo_}7c5@@6z2866WvCLYgXouQWuPt zs0wqGk?QPt_`;7%p7L@fA-i%R^Z#D2qa|J0Oy8yhIwqFM!h7aQ>rrc)C0`!bvxPv5 zEFX>6c^}rcxHFQk_J<!>9!mf3#nWZZ(8i;(kdpK)-W*lk^$}g<Q9PrFJuZx6q84q` zW`tfjzK1!+@cDL7ES0w-t$Eyu;D9}kgw}dOqW7W_n&s&NLn)r$tv=X^tlX_W;<P`H z-2wGk1E(SQhbw2#*eSZ60{Y$u1aZ4wXKgX`?YG>HO|6aPWqTP;;}t0@l&VDGK*-XG zTZCXE=~$1;S?iv!L(KH`?M?V&3xzvKS97V~#d3)UM;FSw=GO67^yV)!@rQD<#9if+ zu!L`CUN<hS7k`8pl71s6bvW)=Pf}!bsN|P7R8WiyH)ziMs!yN(InIZ3^vAyQ$llk> z@9zufY4_{lvmtxfuX@{orL~fVw6(Vl4y#Xy$Wx?8E%E=28PSh)Niz<$(KPn<tq;5W z4nnH@XFqwq0<~e6G4u_~o}u?Y{I*8jAft72TSFCM3UF6G7vdGNWO7J;_ch+<&t;l+ zJ<lz-C{0E<jqBZ`)MSxx*&UB76zj8dtGzdT$Jp_`J2jOKuokz~(516hou^$v^N|c5 z#Y3%ra?g8?xR@o#&F$CV<Kp;k+g+G?$yW9K;11_g`jY4E;>}i;=CH`!3GRtm)uy29 znFlCrjKtm-IauN%Y^m$H|E6rM(n0s(Oxb`%tI;xJCK@`1y;JP-+YFNJ;H5=;WBYlb z`i3b^`fuwpvPDLnYOTj!+g;>soAmS0PWAPr&<6J}jS8Hd*V{4GAoJhnL_lBuUCEY! zlUxd0&&u6#ZzzQ?zv2v(kTr04le5Xj$0s{6D@z|I_~Fl2d(?213a7+KvHQ;@I^6YI zVdQcVykc^Aa^aqO1Sxx2ejA@^!}Byr`;4`}INdJ7dua1&QL?sgS39U`o*d&(78PYb zBXw_dNSA3b)mE3tk~P>m_m99I%%G&Tw|$ur?0D#I52W_wTXwIBC1%|ME#+j1J?(F8 z{r0=q(Uj6}n-QtpGn#ZGCHQ;=$!<H}6s0mrk{xK|y%I9sFnwF0{u8-;JdI298a}1T zeMoiE_e98$FkZ`%M0>gRL}1aaz&_z0Ac^k~^A^SOQ9_87m5Ch-nVe1$FQEyt>_*C- zLZY>nXvYVZ;EGcde4Dcm9W|Fw&JivFR4<@!UJwiG6Y24(dDsQziT7^4^SM%eNE9vR z$BBN7{mn2)ls8DwJ-AKqlPyf1HOyH>yqfe=>!wWpx-`RA8SXP+j#yNCnm`mwvGHm3 z+o-Y+;(tfrsxys%oObuQy!>#|sTpOJO&I4P$I6@6WZCE4dGi}sLO8is{B=TUs8n&Z z`@~8}*x-BYh$b8q0qh^Ovh%_2-7z+-vTNpyiNoOSeYb#e#2((p0Hp0^eDs+qV)CIM z8gjV}T;Ck~p-c(&x%3tTkM?5W*Πe_>s}?jK_AHmXAHI-^uzbcL-=5m>=?h{Kv# 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 F|1UYFsiyz{ diff --git a/index.html b/index.html deleted file mode 100644 index 134d85a..0000000 --- a/index.html +++ /dev/null @@ -1,119 +0,0 @@ -<!doctype html> -<html lang="de"> -<head> - <meta charset="UTF-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - - <!-- Google Tag Manager wird nach Cookie-Zustimmung geladen --> - - <!-- Favicons --> - <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> - <link rel="icon" type="image/png" sizes="32x32" href="/logo.png" /> - <link rel="icon" type="image/png" sizes="16x16" href="/logo.png" /> - <link rel="apple-touch-icon" href="/logo.png" /> - <link rel="mask-icon" href="/favicon.svg" color="#18181B" /> - <meta name="description" content="Als Fullstack-Entwickler aus Köln spezialisiert sich Damjan Savić auf die Entwicklung skalierbarer Softwarelösungen mit IoT- und KI-Integration." /> - <meta name="keywords" content="Damjan Savić, Damjan Savic, Senior Fullstack Entwickler Köln, Digital Solutions Consultant, Python Experte Köln, React Spezialist, TypeScript Developer, Software Architekt Köln, KI Integration OLLAMA, Machine Learning Entwickler, ERP System Integration, SAP Schnittstellen, E-Commerce Entwicklung, Shopify Integration, WooCommerce Entwickler, Prozessautomatisierung, Workflow Automation, Cloud Native Entwicklung, AWS Developer, Docker Kubernetes, Microservices Architektur, API Development, FastAPI Python, Next.js Entwicklung, Electron Desktop Apps, Progressive Web Apps, Agile Software Development, DevOps Engineer Köln, CI/CD Pipeline, Software Consulting, IT Beratung Köln, Digitale Transformation, B2B Enterprise Solutions, SaaS Entwicklung, Full Stack Engineer Germany" /> - <meta name="author" content="Damjan Savić" /> - <meta name="theme-color" content="#18181B" /> - <meta name="robots" content="index, follow" /> - <meta name="googlebot" content="index, follow" /> - - <!-- Google Search Console Verification --> - <meta name="google-site-verification" content="YOUR_VERIFICATION_CODE" /> - - <!-- PWA Manifest --> - <link rel="manifest" href="/manifest.json" /> - - <!-- Additional SEO Meta Tags --> - <meta name="image" content="https://damjan-savic.com/logo.png" /> - <meta itemprop="name" content="Damjan Savić - Senior Fullstack Entwickler & Digital Solutions Consultant Köln" /> - <meta itemprop="description" content="Damjan Savić entwickelt Enterprise Software, Cloud-native Lösungen und KI-gestützte Systeme. Python, React, TypeScript Experte in Köln." /> - <meta itemprop="image" content="https://damjan-savic.com/logo.png" /> - - <title>Damjan Savić | Fullstack-, IoT- & KI-Entwickler - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - Damjan Savić - -

SENIOR FULLSTACK ENTWICKLER

-

Damjan Savić|

-
- -
- - - \ No newline at end of file diff --git a/list_directory.py b/list_directory.py deleted file mode 100644 index b08853f..0000000 --- a/list_directory.py +++ /dev/null @@ -1,54 +0,0 @@ -import os - -def should_ignore(path): - ignore_paths = [ - 'dist', - 'git', - '.idea', - 'dev-dist', - 'node_modules', - 'package-lock.json', - 'External Libraries', - 'Scratches and Consoles' - ] - - for ignore in ignore_paths: - if ignore in str(path): - return True - return False - -def print_directory_tree(path, prefix=""): - try: - # Verzeichnisname ausgeben - print(f"{prefix}📁 {os.path.basename(path)}") - - # Präfix für Unterelemente - prefix_files = prefix + "├── " - prefix_last = prefix + "└── " - prefix_deeper = prefix + "│ " - - # Alle Einträge sammeln und filtern - entries = [entry for entry in os.scandir(path) if not should_ignore(entry.path)] - - # Sortieren: Ordner zuerst, dann Dateien - entries.sort(key=lambda e: (not e.is_dir(), e.name.lower())) - - # Durch alle Einträge iterieren - for i, entry in enumerate(entries): - is_last = (i == len(entries) - 1) - current_prefix = prefix_last if is_last else prefix_files - - if entry.is_file(): - print(f"{current_prefix}📄 {entry.name}") - elif entry.is_dir(): - print_directory_tree(entry.path, prefix_deeper if not is_last else prefix + " ") - - except Exception as e: - print(f"Fehler beim Lesen des Verzeichnisses {path}: {str(e)}") - -if __name__ == "__main__": - portfolio_path = r"C:\Development\Damjan Savic\Portfolio" - print("\nVerzeichnisstruktur:") - print("="*50) - print_directory_tree(portfolio_path) - print("="*50) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 0311f06..0000000 --- a/package-lock.json +++ /dev/null @@ -1,10018 +0,0 @@ -{ - "name": "portfolio", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "portfolio", - "version": "1.0.0", - "dependencies": { - "@mdx-js/loader": "^3.1.0", - "@mdx-js/mdx": "^3.1.0", - "@mdx-js/react": "^3.1.0", - "@next/mdx": "^15.1.0", - "@supabase/ssr": "^0.8.0", - "@supabase/supabase-js": "^2.39.7", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "framer-motion": "^11.18.2", - "gray-matter": "^4.0.3", - "lucide-react": "^0.475.0", - "next": "^15.1.0", - "next-intl": "^3.26.0", - "next-mdx-remote": "^5.0.0", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-intersection-observer": "^9.8.1", - "tailwind-merge": "^3.0.1", - "tailwindcss-animate": "^1.0.7", - "web-vitals": "^5.1.0" - }, - "devDependencies": { - "@tailwindcss/forms": "^0.5.7", - "@types/mdx": "^2.0.13", - "@types/node": "^22.0.0", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "autoprefixer": "^10.4.18", - "eslint": "^9.9.1", - "eslint-config-next": "^15.1.0", - "postcss": "^8.4.35", - "sharp": "^0.34.3", - "tailwindcss": "^3.4.1", - "typescript": "^5.5.3", - "vitest": "^1.3.1" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.28.6", - "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.28.6.tgz", - "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmmirror.com/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.6", - "resolved": "https://registry.npmmirror.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", - "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/intl-localematcher": "0.6.2", - "decimal.js": "^10.4.3", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": { - "version": "0.6.2", - "resolved": "https://registry.npmmirror.com/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", - "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/fast-memoize": { - "version": "2.2.7", - "resolved": "https://registry.npmmirror.com/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", - "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.11.4", - "resolved": "https://registry.npmmirror.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", - "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "@formatjs/icu-skeleton-parser": "1.8.16", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.16", - "resolved": "https://registry.npmmirror.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", - "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.10", - "resolved": "https://registry.npmmirror.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz", - "integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==", - "license": "MIT", - "dependencies": { - "tslib": "2" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mdx-js/loader": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/@mdx-js/loader/-/loader-3.1.1.tgz", - "integrity": "sha512-0TTacJyZ9mDmY+VefuthVshaNIyCGZHJG2fMnGaDttCt8HmjUF7SizlHJpaCDoGnN635nK1wpzfpx/Xx5S4WnQ==", - "license": "MIT", - "dependencies": { - "@mdx-js/mdx": "^3.0.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "webpack": ">=5" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - } - } - }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/@mdx-js/mdx/-/mdx-3.1.1.tgz", - "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "acorn": "^8.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/@mdx-js/react/-/react-3.1.1.tgz", - "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", - "license": "MIT", - "dependencies": { - "@types/mdx": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@next/env": { - "version": "15.5.9", - "resolved": "https://registry.npmmirror.com/@next/env/-/env-15.5.9.tgz", - "integrity": "sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==", - "license": "MIT" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "15.5.9", - "resolved": "https://registry.npmmirror.com/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.9.tgz", - "integrity": "sha512-kUzXx0iFiXw27cQAViE1yKWnz/nF8JzRmwgMRTMh8qMY90crNsdXJRh2e+R0vBpFR3kk1yvAR7wev7+fCCb79Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "3.3.1" - } - }, - "node_modules/@next/mdx": { - "version": "15.5.9", - "resolved": "https://registry.npmmirror.com/@next/mdx/-/mdx-15.5.9.tgz", - "integrity": "sha512-qG9GUKUMpnyD5vU+wNGFNsVDxuSdmYDaCEsScPNPIiplzfNSS7VZk1G2yQ2tgXz6KjFncdaqJPuDehFqFy/gjQ==", - "license": "MIT", - "dependencies": { - "source-map": "^0.7.0" - }, - "peerDependencies": { - "@mdx-js/loader": ">=0.15.0", - "@mdx-js/react": ">=0.15.0" - }, - "peerDependenciesMeta": { - "@mdx-js/loader": { - "optional": true - }, - "@mdx-js/react": { - "optional": true - } - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.7", - "resolved": "https://registry.npmmirror.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz", - "integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "15.5.7", - "resolved": "https://registry.npmmirror.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz", - "integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.7", - "resolved": "https://registry.npmmirror.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz", - "integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.7", - "resolved": "https://registry.npmmirror.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz", - "integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.7", - "resolved": "https://registry.npmmirror.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz", - "integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.7", - "resolved": "https://registry.npmmirror.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz", - "integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.7", - "resolved": "https://registry.npmmirror.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz", - "integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.7", - "resolved": "https://registry.npmmirror.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz", - "integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmmirror.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.4.0" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", - "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", - "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", - "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", - "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", - "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", - "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", - "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", - "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", - "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", - "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", - "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", - "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", - "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", - "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", - "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", - "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", - "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", - "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", - "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", - "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", - "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", - "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", - "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", - "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", - "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", - "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@supabase/auth-js": { - "version": "2.90.1", - "resolved": "https://registry.npmmirror.com/@supabase/auth-js/-/auth-js-2.90.1.tgz", - "integrity": "sha512-vxb66dgo6h3yyPbR06735Ps+dK3hj0JwS8w9fdQPVZQmocSTlKUW5MfxSy99mN0XqCCuLMQ3jCEiIIUU23e9ng==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/functions-js": { - "version": "2.90.1", - "resolved": "https://registry.npmmirror.com/@supabase/functions-js/-/functions-js-2.90.1.tgz", - "integrity": "sha512-x9mV9dF1Lam9qL3zlpP6mSM5C9iqMPtF5B/tU1Jj/F0ufX5mjDf9ghVBaErVxmrQJRL4+iMKWKY2GnODkpS8tw==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/postgrest-js": { - "version": "2.90.1", - "resolved": "https://registry.npmmirror.com/@supabase/postgrest-js/-/postgrest-js-2.90.1.tgz", - "integrity": "sha512-jh6vqzaYzoFn3raaC0hcFt9h+Bt+uxNRBSdc7PfToQeRGk7PDPoweHsbdiPWREtDVTGKfu+PyPW9e2jbK+BCgQ==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/realtime-js": { - "version": "2.90.1", - "resolved": "https://registry.npmmirror.com/@supabase/realtime-js/-/realtime-js-2.90.1.tgz", - "integrity": "sha512-PWbnEMkcQRuor8jhObp4+Snufkq8C6fBp+MchVp2qBPY1NXk/c3Iv3YyiFYVzo0Dzuw4nAlT4+ahuPggy4r32w==", - "license": "MIT", - "dependencies": { - "@types/phoenix": "^1.6.6", - "@types/ws": "^8.18.1", - "tslib": "2.8.1", - "ws": "^8.18.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/ssr": { - "version": "0.8.0", - "resolved": "https://registry.npmmirror.com/@supabase/ssr/-/ssr-0.8.0.tgz", - "integrity": "sha512-/PKk8kNFSs8QvvJ2vOww1mF5/c5W8y42duYtXvkOSe+yZKRgTTZywYG2l41pjhNomqESZCpZtXuWmYjFRMV+dw==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.2" - }, - "peerDependencies": { - "@supabase/supabase-js": "^2.76.1" - } - }, - "node_modules/@supabase/storage-js": { - "version": "2.90.1", - "resolved": "https://registry.npmmirror.com/@supabase/storage-js/-/storage-js-2.90.1.tgz", - "integrity": "sha512-GHY+Ps/K/RBfRj7kwx+iVf2HIdqOS43rM2iDOIDpapyUnGA9CCBFzFV/XvfzznGykd//z2dkGZhlZZprsVFqGg==", - "license": "MIT", - "dependencies": { - "iceberg-js": "^0.8.1", - "tslib": "2.8.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@supabase/supabase-js": { - "version": "2.90.1", - "resolved": "https://registry.npmmirror.com/@supabase/supabase-js/-/supabase-js-2.90.1.tgz", - "integrity": "sha512-U8KaKGLUgTIFHtwEW1dgw1gK7XrdpvvYo7nzzqPx721GqPe8WZbAiLh/hmyKLGBYQ/mmQNr20vU9tWSDZpii3w==", - "license": "MIT", - "dependencies": { - "@supabase/auth-js": "2.90.1", - "@supabase/functions-js": "2.90.1", - "@supabase/postgrest-js": "2.90.1", - "@supabase/realtime-js": "2.90.1", - "@supabase/storage-js": "2.90.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tailwindcss/forms": { - "version": "0.5.11", - "resolved": "https://registry.npmmirror.com/@tailwindcss/forms/-/forms-0.5.11.tgz", - "integrity": "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mini-svg-data-uri": "^1.2.3" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmmirror.com/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.19.7", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.19.7.tgz", - "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/phoenix": { - "version": "1.6.7", - "resolved": "https://registry.npmmirror.com/@types/phoenix/-/phoenix-1.6.7.tgz", - "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.2.8", - "resolved": "https://registry.npmmirror.com/@types/react/-/react-19.2.8.tgz", - "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.0.tgz", - "integrity": "sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.53.0", - "@typescript-eslint/type-utils": "8.53.0", - "@typescript-eslint/utils": "8.53.0", - "@typescript-eslint/visitor-keys": "8.53.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.53.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.53.0.tgz", - "integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.53.0", - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/typescript-estree": "8.53.0", - "@typescript-eslint/visitor-keys": "8.53.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.53.0.tgz", - "integrity": "sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.53.0", - "@typescript-eslint/types": "^8.53.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.53.0.tgz", - "integrity": "sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/visitor-keys": "8.53.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.0.tgz", - "integrity": "sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.53.0.tgz", - "integrity": "sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/typescript-estree": "8.53.0", - "@typescript-eslint/utils": "8.53.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.53.0.tgz", - "integrity": "sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.0.tgz", - "integrity": "sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.53.0", - "@typescript-eslint/tsconfig-utils": "8.53.0", - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/visitor-keys": "8.53.0", - "debug": "^4.4.3", - "minimatch": "^9.0.5", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.53.0.tgz", - "integrity": "sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.53.0", - "@typescript-eslint/types": "8.53.0", - "@typescript-eslint/typescript-estree": "8.53.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.53.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.0.tgz", - "integrity": "sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.53.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@vitest/expect": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-1.6.1.tgz", - "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "chai": "^4.3.10" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-1.6.1.tgz", - "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "1.6.1", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@vitest/snapshot": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-1.6.1.tgz", - "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-1.6.1.tgz", - "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^2.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-1.6.1.tgz", - "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmmirror.com/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmmirror.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmmirror.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmmirror.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmmirror.com/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.23", - "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.23.tgz", - "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001760", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.11.1", - "resolved": "https://registry.npmmirror.com/axe-core/-/axe-core-4.11.1.tgz", - "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.15", - "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.15.tgz", - "integrity": "sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001764", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", - "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmmirror.com/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmmirror.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "license": "MIT" - }, - "node_modules/decode-named-character-reference": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", - "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmmirror.com/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-abstract": { - "version": "1.24.1", - "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.1.tgz", - "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", - "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.1.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.3.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.5", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-next": { - "version": "15.5.9", - "resolved": "https://registry.npmmirror.com/eslint-config-next/-/eslint-config-next-15.5.9.tgz", - "integrity": "sha512-852JYI3NkFNzW8CqsMhI0K2CDRxTObdZ2jQJj5CtpEaOkYHn13107tHpNuD/h0WRpU4FAbCdUaxQsrfBtNK9Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@next/eslint-plugin-next": "15.5.9", - "@rushstack/eslint-patch": "^1.10.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^5.0.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmmirror.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmmirror.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", - "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmmirror.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", - "resolved": "https://registry.npmmirror.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.9", - "array.prototype.findlastindex": "^1.2.6", - "array.prototype.flat": "^1.3.3", - "array.prototype.flatmap": "^1.3.3", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "is-core-module": "^2.16.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.2", - "resolved": "https://registry.npmmirror.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", - "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "aria-query": "^5.3.2", - "array-includes": "^3.1.8", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "^4.10.0", - "axobject-query": "^4.1.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "hasown": "^2.0.2", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.1" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmmirror.com/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmmirror.com/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/framer-motion": { - "version": "11.18.2", - "resolved": "https://registry.npmmirror.com/framer-motion/-/framer-motion-11.18.2.tgz", - "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", - "license": "MIT", - "dependencies": { - "motion-dom": "^11.18.1", - "motion-utils": "^11.18.1", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "license": "MIT", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", - "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmmirror.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/iceberg-js": { - "version": "0.8.1", - "resolved": "https://registry.npmmirror.com/iceberg-js/-/iceberg-js-0.8.1.tgz", - "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", - "license": "MIT", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmmirror.com/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/intl-messageformat": { - "version": "10.7.18", - "resolved": "https://registry.npmmirror.com/intl-messageformat/-/intl-messageformat-10.7.18.tgz", - "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", - "license": "BSD-3-Clause", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.6", - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/icu-messageformat-parser": "2.11.4", - "tslib": "^2.8.0" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.7.1" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmmirror.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmmirror.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmmirror.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmmirror.com/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dev": true, - "license": "MIT", - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/local-pkg": { - "version": "0.5.1", - "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.5.1.tgz", - "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmmirror.com/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lucide-react": { - "version": "0.475.0", - "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.475.0.tgz", - "integrity": "sha512-NJzvVu1HwFVeZ+Gwq2q00KygM1aBhy/ZrhY9FsAgJtpB+E4R7uxRk9M2iKvHa6/vNxZydIB59htha4c2vvwvVg==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", - "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", - "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", - "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", - "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mini-svg-data-uri": { - "version": "1.4.4", - "resolved": "https://registry.npmmirror.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", - "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", - "dev": true, - "license": "MIT", - "bin": { - "mini-svg-data-uri": "cli.js" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mlly": { - "version": "1.8.0", - "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.0.tgz", - "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.15.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.1" - } - }, - "node_modules/mlly/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/motion-dom": { - "version": "11.18.1", - "resolved": "https://registry.npmmirror.com/motion-dom/-/motion-dom-11.18.1.tgz", - "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", - "license": "MIT", - "dependencies": { - "motion-utils": "^11.18.1" - } - }, - "node_modules/motion-utils": { - "version": "11.18.1", - "resolved": "https://registry.npmmirror.com/motion-utils/-/motion-utils-11.18.1.tgz", - "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-postinstall": { - "version": "0.3.4", - "resolved": "https://registry.npmmirror.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", - "dev": true, - "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/next": { - "version": "15.5.9", - "resolved": "https://registry.npmmirror.com/next/-/next-15.5.9.tgz", - "integrity": "sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==", - "license": "MIT", - "dependencies": { - "@next/env": "15.5.9", - "@swc/helpers": "0.5.15", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.7", - "@next/swc-darwin-x64": "15.5.7", - "@next/swc-linux-arm64-gnu": "15.5.7", - "@next/swc-linux-arm64-musl": "15.5.7", - "@next/swc-linux-x64-gnu": "15.5.7", - "@next/swc-linux-x64-musl": "15.5.7", - "@next/swc-win32-arm64-msvc": "15.5.7", - "@next/swc-win32-x64-msvc": "15.5.7", - "sharp": "^0.34.3" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next-intl": { - "version": "3.26.5", - "resolved": "https://registry.npmmirror.com/next-intl/-/next-intl-3.26.5.tgz", - "integrity": "sha512-EQlCIfY0jOhRldiFxwSXG+ImwkQtDEfQeSOEQp6ieAGSLWGlgjdb/Ck/O7wMfC430ZHGeUKVKax8KGusTPKCgg==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/amannn" - } - ], - "license": "MIT", - "dependencies": { - "@formatjs/intl-localematcher": "^0.5.4", - "negotiator": "^1.0.0", - "use-intl": "^3.26.5" - }, - "peerDependencies": { - "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" - } - }, - "node_modules/next-mdx-remote": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/next-mdx-remote/-/next-mdx-remote-5.0.0.tgz", - "integrity": "sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ==", - "license": "MPL-2.0", - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@mdx-js/mdx": "^3.0.1", - "@mdx-js/react": "^3.0.1", - "unist-util-remove": "^3.1.0", - "vfile": "^6.0.1", - "vfile-matter": "^5.0.0" - }, - "engines": { - "node": ">=14", - "npm": ">=7" - }, - "peerDependencies": { - "react": ">=16" - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmmirror.com/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmmirror.com/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/pkg-types/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "19.2.3", - "resolved": "https://registry.npmmirror.com/react/-/react-19.2.3.tgz", - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.3" - } - }, - "node_modules/react-intersection-observer": { - "version": "9.16.0", - "resolved": "https://registry.npmmirror.com/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz", - "integrity": "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==", - "license": "MIT", - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recma-build-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", - "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-jsx": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/recma-jsx/-/recma-jsx-1.0.1.tgz", - "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", - "license": "MIT", - "dependencies": { - "acorn-jsx": "^5.0.0", - "estree-util-to-js": "^2.0.0", - "recma-parse": "^1.0.0", - "recma-stringify": "^1.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/recma-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/recma-parse/-/recma-parse-1.0.0.tgz", - "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "esast-util-from-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-stringify": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/recma-stringify/-/recma-stringify-1.0.0.tgz", - "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-to-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rehype-recma": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/rehype-recma/-/rehype-recma-1.0.0.tgz", - "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "hast-util-to-estree": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/remark-mdx/-/remark-mdx-3.1.1.tgz", - "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmmirror.com/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmmirror.com/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.55.1", - "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.55.1.tgz", - "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.55.1", - "@rollup/rollup-android-arm64": "4.55.1", - "@rollup/rollup-darwin-arm64": "4.55.1", - "@rollup/rollup-darwin-x64": "4.55.1", - "@rollup/rollup-freebsd-arm64": "4.55.1", - "@rollup/rollup-freebsd-x64": "4.55.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", - "@rollup/rollup-linux-arm-musleabihf": "4.55.1", - "@rollup/rollup-linux-arm64-gnu": "4.55.1", - "@rollup/rollup-linux-arm64-musl": "4.55.1", - "@rollup/rollup-linux-loong64-gnu": "4.55.1", - "@rollup/rollup-linux-loong64-musl": "4.55.1", - "@rollup/rollup-linux-ppc64-gnu": "4.55.1", - "@rollup/rollup-linux-ppc64-musl": "4.55.1", - "@rollup/rollup-linux-riscv64-gnu": "4.55.1", - "@rollup/rollup-linux-riscv64-musl": "4.55.1", - "@rollup/rollup-linux-s390x-gnu": "4.55.1", - "@rollup/rollup-linux-x64-gnu": "4.55.1", - "@rollup/rollup-linux-x64-musl": "4.55.1", - "@rollup/rollup-openbsd-x64": "4.55.1", - "@rollup/rollup-openharmony-arm64": "4.55.1", - "@rollup/rollup-win32-arm64-msvc": "4.55.1", - "@rollup/rollup-win32-ia32-msvc": "4.55.1", - "@rollup/rollup-win32-x64-gnu": "4.55.1", - "@rollup/rollup-win32-x64-msvc": "4.55.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "devOptional": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmmirror.com/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmmirror.com/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmmirror.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-literal": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-2.1.1.tgz", - "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmmirror.com/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmmirror.com/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmmirror.com/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwind-merge": { - "version": "3.4.0", - "resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-3.4.0.tgz", - "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.19", - "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.19.tgz", - "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss-animate": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", - "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", - "license": "MIT", - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" - } - }, - "node_modules/tailwindcss/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/tailwindcss/node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tinypool": { - "version": "0.8.4", - "resolved": "https://registry.npmmirror.com/tinypool/-/tinypool-0.8.4.tgz", - "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmmirror.com/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmmirror.com/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/unist-util-remove/-/unist-util-remove-3.1.1.tgz", - "integrity": "sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/unist-util-remove/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/unist-util-visit-parents/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit/node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-intl": { - "version": "3.26.5", - "resolved": "https://registry.npmmirror.com/use-intl/-/use-intl-3.26.5.tgz", - "integrity": "sha512-OdsJnC/znPvHCHLQH/duvQNXnP1w0hPfS+tkSi3mAbfjYBGh4JnyfdwkQBfIVf7t8gs9eSX/CntxUMvtKdG2MQ==", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "^2.2.0", - "intl-messageformat": "^10.5.14" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-matter": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/vfile-matter/-/vfile-matter-5.0.1.tgz", - "integrity": "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw==", - "license": "MIT", - "dependencies": { - "vfile": "^6.0.0", - "yaml": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/vite-node/-/vite-node-1.6.1.tgz", - "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/vitest/-/vitest-1.6.1.tgz", - "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "1.6.1", - "@vitest/runner": "1.6.1", - "@vitest/snapshot": "1.6.1", - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", - "vite": "^5.0.0", - "vite-node": "1.6.1", - "why-is-node-running": "^2.2.2" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.1", - "@vitest/ui": "1.6.1", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/web-vitals": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/web-vitals/-/web-vitals-5.1.0.tgz", - "integrity": "sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==", - "license": "Apache-2.0" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmmirror.com/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/project-readme-template.md b/project-readme-template.md deleted file mode 100644 index d8fed92..0000000 --- a/project-readme-template.md +++ /dev/null @@ -1,112 +0,0 @@ -# Project Name - -[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) -[![Python](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) -[![React](https://img.shields.io/badge/react-18.0+-blue.svg)](https://reactjs.org/) - -## 🚀 Overview - -Brief description of what this project does and why it's useful. - -### 🎯 Key Features -- Feature 1: Description -- Feature 2: Description -- Feature 3: Description - -### 💡 Use Cases -- Use case 1 -- Use case 2 -- Use case 3 - -## 🛠️ Tech Stack - -- **Backend:** Python, FastAPI/Django -- **Frontend:** React, Next.js, TypeScript -- **Database:** PostgreSQL/MongoDB -- **AI/ML:** OLLAMA, LangChain -- **DevOps:** Docker, AWS - -## 📦 Installation - -```bash -# Clone the repository -git clone https://github.com/damjansavic/project-name.git - -# Navigate to project directory -cd project-name - -# Install dependencies -pip install -r requirements.txt -npm install - -# Set up environment variables -cp .env.example .env -``` - -## 🚀 Quick Start - -```bash -# Start the backend -python main.py - -# Start the frontend (in another terminal) -npm run dev -``` - -## 📊 Performance - -- **Response Time:** < 100ms -- **Throughput:** 1000+ requests/second -- **Uptime:** 99.9% - -## 🔧 Configuration - -Key configuration options: - -```python -# config.py -DATABASE_URL = "postgresql://..." -OLLAMA_MODEL = "llama2" -API_KEY = "your-api-key" -``` - -## 📱 API Documentation - -API documentation is available at `/docs` when running the application. - -### Example Request - -```bash -curl -X POST "http://localhost:8000/api/v1/process" \ - -H "Content-Type: application/json" \ - -d '{"data": "example"}' -``` - -## 🧪 Testing - -```bash -# Run tests -pytest - -# Run with coverage -pytest --cov=app tests/ -``` - -## 🤝 Contributing - -Contributions are welcome! Please feel free to submit a Pull Request. - -## 📄 License - -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. - -## 👨‍💻 Author - -**Damjan Savić** -- Website: [damjan-savic.com](https://damjan-savic.com) -- LinkedIn: [@damjansavic](https://linkedin.com/in/damjansavic) -- GitHub: [@damjansavic](https://github.com/damjansavic) - ---- - -⭐️ If you find this project useful, please consider giving it a star! diff --git a/public/offline.html b/public/offline.html deleted file mode 100644 index 55f7e9f..0000000 --- a/public/offline.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - Offline - Damjan Savić - - - -
-

You're Offline

-

It seems you've lost your internet connection. Please check your connection and try again.

- -
- - \ No newline at end of file diff --git a/public/portrait-224.jpg b/public/portrait-224.jpg deleted file mode 100644 index cfa18ba79a704e6376dab3da73f5d1f95f4e8ac2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8461 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 MaQ^awk@-dd0M{~szyJUM diff --git a/public/portrait-288.jpg b/public/portrait-288.jpg deleted file mode 100644 index bf222b45da61fb56b9b827171e57f28804da9a84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12621 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 U7QYf#0+?f*czkx3|FZnQ0HpS+!vFvP diff --git a/public/portrait-288.webp b/public/portrait-288.webp deleted file mode 100644 index d5c1be4c364b2670af08dec6400f958eb9d22e42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6466 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 diff --git a/public/portrait-300.jpg b/public/portrait-300.jpg deleted file mode 100644 index fdbe292be27beac7ee2ceaee4ad6aaddbdc0b00e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11692 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 diff --git a/public/portrait-448.webp b/public/portrait-448.webp deleted file mode 100644 index 3b605babf4d55d741d3d6dcd1aa07c329effe8d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12498 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횋&eT5@p%^i{Dx0tky|=X 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 diff --git a/public/portrait-576.webp b/public/portrait-576.webp deleted file mode 100644 index 2030a3d1b91ec2ac44ebedca8664ce4b58312735..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21324 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 T#>FtJVm9}eq9astqF2BGd|S@N diff --git a/public/portrait-600.jpg b/public/portrait-600.jpg deleted file mode 100644 index 0c5d0ac50076d6b8bf5a7a2e391182410aae01c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51014 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 gKW8jLv(yP}zq0@qCkVGav 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 E*{XjxIsgCw diff --git a/public/portrait-600.webp b/public/portrait-600.webp deleted file mode 100644 index 41b1cbc99362a07f89e80652509da48f04378710..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31424 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 gKW8jLv(yP}zq0@qCkVGav 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 E*{XjxIsgCw diff --git a/public/portrait.jpg b/public/portrait.jpg deleted file mode 100644 index 932c8f9c6d1dbb56139889553a472743a785505c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1207506 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

Z$-&a?hNBTnC1C?6OgszZLepKi0xFB64L9q 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 xVw?i&q& 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

s1&FCFcL|bQm0_tYr4Cs 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 z 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

?7CD5-xFgbf|sdDk{HtjPdoMRFgAEWOgVj4Hz!% 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 M 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 z%0A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 - - - https://damjan-savic.com/ - 2026-01-05 - weekly - 1.0 - - - - - - https://damjan-savic.com/en/ - 2026-01-05 - weekly - 1.0 - - - - - - https://damjan-savic.com/sr/ - 2026-01-05 - weekly - 1.0 - - - - - - https://damjan-savic.com/about - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/about - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/about - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/portfolio - 2026-01-05 - weekly - 0.9 - - - - - - https://damjan-savic.com/en/portfolio - 2026-01-05 - weekly - 0.9 - - - - - - https://damjan-savic.com/sr/portfolio - 2026-01-05 - weekly - 0.9 - - - - - - https://damjan-savic.com/blog - 2026-01-05 - weekly - 0.9 - - - - - - https://damjan-savic.com/en/blog - 2026-01-05 - weekly - 0.9 - - - - - - https://damjan-savic.com/sr/blog - 2026-01-05 - weekly - 0.9 - - - - - - https://damjan-savic.com/contact - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/contact - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/contact - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/privacy - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/privacy - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/privacy - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/terms - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/terms - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/terms - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/blog/erp-integration-breuninger - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/en/blog/erp-integration-breuninger - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/sr/blog/erp-integration-breuninger - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/blog/fullstack-development-timetracking - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/en/blog/fullstack-development-timetracking - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/sr/blog/fullstack-development-timetracking - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/blog/rfid-automation - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/en/blog/rfid-automation - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/sr/blog/rfid-automation - 2026-01-05 - monthly - 0.7 - - - - - - https://damjan-savic.com/portfolio/ai-data-reader - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/portfolio/ai-data-reader - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/portfolio/ai-data-reader - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/portfolio/bi-vision - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/portfolio/bi-vision - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/portfolio/bi-vision - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/portfolio/business-intelligence-ai - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/portfolio/business-intelligence-ai - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/portfolio/business-intelligence-ai - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/portfolio/claude-personal-assistant - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/portfolio/claude-personal-assistant - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/portfolio/claude-personal-assistant - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/portfolio/e-commerce-integration - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/portfolio/e-commerce-integration - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/portfolio/e-commerce-integration - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/portfolio/local-llm-training - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/portfolio/local-llm-training - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/portfolio/local-llm-training - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/portfolio/rfid-automation - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/portfolio/rfid-automation - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/portfolio/rfid-automation - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/portfolio/timetracking-software - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/en/portfolio/timetracking-software - 2026-01-05 - monthly - 0.8 - - - - - - https://damjan-savic.com/sr/portfolio/timetracking-software - 2026-01-05 - monthly - 0.8 - - - - - \ No newline at end of file diff --git a/scripts/axe-accessibility-audit.js b/scripts/axe-accessibility-audit.js new file mode 100644 index 0000000..f17b924 --- /dev/null +++ b/scripts/axe-accessibility-audit.js @@ -0,0 +1,493 @@ +/** + * Axe Accessibility Audit Script + * + * This script runs axe-core accessibility scans on all main pages of the portfolio site. + * It generates a comprehensive report with WCAG references. + * + * Usage: + * node scripts/axe-accessibility-audit.js + * + * Prerequisites: + * - Development server running on http://localhost:3000 + * - Install deps: npm install puppeteer @axe-core/puppeteer --no-save + */ + +const { AxePuppeteer } = require('@axe-core/puppeteer'); +const puppeteer = require('puppeteer'); +const fs = require('fs'); +const path = require('path'); + +// Configuration +const BASE_URL = 'http://localhost:3000'; +const LOCALES = ['de', 'en', 'sr']; +const OUTPUT_DIR = path.join(__dirname, '..', '.auto-claude', 'specs', '001-suche-nach-optimierungsvorschl-gen'); + +// Pages to audit (main public pages) +const PAGES = [ + { path: '', name: 'Home' }, + { path: '/about', name: 'About' }, + { path: '/blog', name: 'Blog Index' }, + { path: '/contact', name: 'Contact' }, + { path: '/leistungen', name: 'Services' }, + { path: '/portfolio', name: 'Portfolio Index' }, + { path: '/imprint', name: 'Imprint' }, + { path: '/privacy', name: 'Privacy' }, + { path: '/terms', name: 'Terms' }, +]; + +// WCAG 2.1 Impact mapping +const IMPACT_ORDER = { + 'critical': 0, + 'serious': 1, + 'moderate': 2, + 'minor': 3 +}; + +const WCAG_TAGS = { + 'wcag2a': 'WCAG 2.0 Level A', + 'wcag2aa': 'WCAG 2.0 Level AA', + 'wcag2aaa': 'WCAG 2.0 Level AAA', + 'wcag21a': 'WCAG 2.1 Level A', + 'wcag21aa': 'WCAG 2.1 Level AA', + 'wcag22aa': 'WCAG 2.2 Level AA', + 'best-practice': 'Best Practice' +}; + +/** + * Run axe audit on a single page + */ +async function auditPage(browser, url, pageName) { + const page = await browser.newPage(); + await page.setViewport({ width: 1280, height: 800 }); + + console.log(` Scanning: ${url}`); + + try { + await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 }); + + // Wait for dynamic content to load + await page.waitForTimeout(1000); + + // Run axe analysis + const results = await new AxePuppeteer(page) + .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice']) + .analyze(); + + await page.close(); + + return { + url, + pageName, + timestamp: new Date().toISOString(), + violations: results.violations, + passes: results.passes.length, + incomplete: results.incomplete, + inapplicable: results.inapplicable.length + }; + } catch (error) { + await page.close(); + return { + url, + pageName, + timestamp: new Date().toISOString(), + error: error.message, + violations: [], + passes: 0, + incomplete: [], + inapplicable: 0 + }; + } +} + +/** + * Format violations for markdown output + */ +function formatViolation(violation) { + const wcagTags = violation.tags + .filter(tag => tag.startsWith('wcag') || tag === 'best-practice') + .map(tag => WCAG_TAGS[tag] || tag) + .join(', '); + + const nodes = violation.nodes.map(node => ({ + html: node.html.substring(0, 200), + target: node.target.join(', '), + failureSummary: node.failureSummary + })); + + return { + id: violation.id, + impact: violation.impact, + description: violation.description, + help: violation.help, + helpUrl: violation.helpUrl, + wcagTags, + nodeCount: nodes.length, + nodes: nodes.slice(0, 5) // Limit to first 5 examples + }; +} + +/** + * Aggregate violations across all pages + */ +function aggregateViolations(allResults) { + const violationMap = new Map(); + + for (const result of allResults) { + if (result.error) continue; + + for (const violation of result.violations) { + const key = violation.id; + + if (!violationMap.has(key)) { + violationMap.set(key, { + ...formatViolation(violation), + affectedPages: [], + totalInstances: 0 + }); + } + + const aggregated = violationMap.get(key); + aggregated.affectedPages.push({ + url: result.url, + pageName: result.pageName, + instances: violation.nodes.length + }); + aggregated.totalInstances += violation.nodes.length; + } + } + + // Sort by impact severity, then by total instances + return Array.from(violationMap.values()).sort((a, b) => { + const impactDiff = IMPACT_ORDER[a.impact] - IMPACT_ORDER[b.impact]; + if (impactDiff !== 0) return impactDiff; + return b.totalInstances - a.totalInstances; + }); +} + +/** + * Generate markdown report + */ +function generateMarkdownReport(allResults, aggregatedViolations) { + const timestamp = new Date().toISOString(); + + // Calculate statistics + const totalPages = allResults.filter(r => !r.error).length; + const errorPages = allResults.filter(r => r.error).length; + const totalViolations = aggregatedViolations.length; + const totalInstances = aggregatedViolations.reduce((sum, v) => sum + v.totalInstances, 0); + + const bySeverity = { + critical: aggregatedViolations.filter(v => v.impact === 'critical'), + serious: aggregatedViolations.filter(v => v.impact === 'serious'), + moderate: aggregatedViolations.filter(v => v.impact === 'moderate'), + minor: aggregatedViolations.filter(v => v.impact === 'minor') + }; + + let report = `# Axe Accessibility Audit Report + +**Generated:** ${timestamp} +**Tool:** axe-core via @axe-core/puppeteer +**Standards:** WCAG 2.0 A/AA, WCAG 2.1 A/AA, Best Practices + +--- + +## Executive Summary + +| Metric | Value | +|--------|-------| +| Pages Scanned | ${totalPages} | +| Pages with Errors | ${errorPages} | +| Unique Violations | ${totalViolations} | +| Total Instances | ${totalInstances} | +| Critical Issues | ${bySeverity.critical.length} | +| Serious Issues | ${bySeverity.serious.length} | +| Moderate Issues | ${bySeverity.moderate.length} | +| Minor Issues | ${bySeverity.minor.length} | + +`; + + // Severity breakdown + report += `### Severity Distribution + +`; + + if (bySeverity.critical.length > 0) { + report += `🔴 **Critical (${bySeverity.critical.length}):** ${bySeverity.critical.map(v => v.id).join(', ')}\n\n`; + } else { + report += `✅ **Critical (0):** No critical issues found\n\n`; + } + + if (bySeverity.serious.length > 0) { + report += `🟠 **Serious (${bySeverity.serious.length}):** ${bySeverity.serious.map(v => v.id).join(', ')}\n\n`; + } else { + report += `✅ **Serious (0):** No serious issues found\n\n`; + } + + if (bySeverity.moderate.length > 0) { + report += `🟡 **Moderate (${bySeverity.moderate.length}):** ${bySeverity.moderate.map(v => v.id).join(', ')}\n\n`; + } else { + report += `✅ **Moderate (0):** No moderate issues found\n\n`; + } + + if (bySeverity.minor.length > 0) { + report += `🟢 **Minor (${bySeverity.minor.length}):** ${bySeverity.minor.map(v => v.id).join(', ')}\n\n`; + } else { + report += `✅ **Minor (0):** No minor issues found\n\n`; + } + + report += `--- + +## Page-by-Page Results + +`; + + for (const result of allResults) { + const locale = result.url.split('/')[3] || 'root'; + const violationCount = result.violations?.length || 0; + const status = result.error ? '❌ Error' : (violationCount === 0 ? '✅ Pass' : `⚠️ ${violationCount} issues`); + + report += `### ${result.pageName} (${locale.toUpperCase()}) + +**URL:** ${result.url} +**Status:** ${status} +`; + + if (result.error) { + report += `**Error:** ${result.error}\n\n`; + } else { + report += `**Passes:** ${result.passes} | **Violations:** ${violationCount} | **Incomplete:** ${result.incomplete.length}\n\n`; + + if (violationCount > 0) { + report += `**Issues found:**\n`; + for (const v of result.violations) { + report += `- \`${v.id}\` (${v.impact}): ${v.help} - ${v.nodes.length} instance(s)\n`; + } + report += '\n'; + } + } + } + + report += `--- + +## Detailed Violations + +`; + + for (const violation of aggregatedViolations) { + const impactEmoji = { + 'critical': '🔴', + 'serious': '🟠', + 'moderate': '🟡', + 'minor': '🟢' + }[violation.impact]; + + report += `### ${impactEmoji} ${violation.id} + +**Impact:** ${violation.impact.toUpperCase()} +**WCAG:** ${violation.wcagTags || 'N/A'} +**Help:** ${violation.help} +**Total Instances:** ${violation.totalInstances} +**Documentation:** [Deque ${violation.id}](${violation.helpUrl}) + +**Description:** ${violation.description} + +**Affected Pages:** +`; + + for (const page of violation.affectedPages) { + report += `- ${page.pageName} (${page.url}): ${page.instances} instance(s)\n`; + } + + report += '\n**Example Elements:**\n\n```html\n'; + for (const node of violation.nodes.slice(0, 3)) { + report += `\n`; + report += `${node.html}\n\n`; + } + report += '```\n\n'; + + if (violation.nodes[0]?.failureSummary) { + report += `**Fix:** ${violation.nodes[0].failureSummary}\n\n`; + } + + report += `---\n\n`; + } + + // Recommendations section + report += `## Recommendations + +### Immediate Actions (Critical & Serious) + +`; + + for (const v of [...bySeverity.critical, ...bySeverity.serious]) { + report += `1. **Fix \`${v.id}\`**: ${v.help} - Affects ${v.affectedPages.length} page(s), ${v.totalInstances} total instance(s)\n`; + } + + if (bySeverity.critical.length === 0 && bySeverity.serious.length === 0) { + report += `✅ No critical or serious issues to fix immediately.\n`; + } + + report += ` +### Short-term Improvements (Moderate) + +`; + + for (const v of bySeverity.moderate) { + report += `1. **Fix \`${v.id}\`**: ${v.help} - Affects ${v.affectedPages.length} page(s)\n`; + } + + if (bySeverity.moderate.length === 0) { + report += `✅ No moderate issues to address.\n`; + } + + report += ` +### Nice-to-have (Minor & Best Practices) + +`; + + for (const v of bySeverity.minor) { + report += `1. **Consider \`${v.id}\`**: ${v.help}\n`; + } + + if (bySeverity.minor.length === 0) { + report += `✅ No minor issues found.\n`; + } + + report += ` +--- + +## WCAG Compliance Summary + +Based on axe automated testing, the following WCAG conformance levels have been evaluated: + +| Level | Status | Notes | +|-------|--------|-------| +| WCAG 2.0 A | ${bySeverity.critical.length === 0 ? '✅ Pass' : '❌ Fail'} | ${bySeverity.critical.length > 0 ? `${bySeverity.critical.length} critical violations` : 'No critical violations'} | +| WCAG 2.0 AA | ${(bySeverity.critical.length + bySeverity.serious.length) === 0 ? '✅ Pass' : '⚠️ Issues'} | ${(bySeverity.critical.length + bySeverity.serious.length) > 0 ? `${bySeverity.critical.length + bySeverity.serious.length} issues` : 'No serious violations'} | +| WCAG 2.1 AA | ${(bySeverity.critical.length + bySeverity.serious.length) === 0 ? '✅ Pass' : '⚠️ Issues'} | Same as above | + +**Note:** Automated testing can only catch ~30-40% of accessibility issues. Manual testing for keyboard navigation, +screen reader compatibility, and cognitive accessibility is also required for full WCAG compliance. + +--- + +## Testing Methodology + +- **Tool:** axe-core via @axe-core/puppeteer +- **Browser:** Chromium (Puppeteer) +- **Viewport:** 1280x800 (Desktop) +- **Standards Tested:** wcag2a, wcag2aa, wcag21a, wcag21aa, best-practice +- **Locales Tested:** German (de), English (en), Serbian (sr) + +## Next Steps + +1. Address all critical and serious violations first +2. Fix moderate issues for full WCAG 2.1 AA compliance +3. Perform manual keyboard navigation testing +4. Test with screen readers (NVDA, VoiceOver) +5. Review color contrast manually for custom components +6. Test with real users with disabilities if possible +`; + + return report; +} + +/** + * Main execution + */ +async function main() { + console.log('🔍 Axe Accessibility Audit Starting...\n'); + + // Check if dev server is running + try { + const http = require('http'); + await new Promise((resolve, reject) => { + http.get(BASE_URL, (res) => { + if (res.statusCode === 200 || res.statusCode === 301 || res.statusCode === 302) { + resolve(); + } else { + reject(new Error(`Server returned ${res.statusCode}`)); + } + }).on('error', reject); + }); + } catch (error) { + console.error('❌ Development server not running!'); + console.error(' Please start the server with: pnpm dev'); + process.exit(1); + } + + console.log('✅ Development server detected\n'); + + // Launch browser + const browser = await puppeteer.launch({ + headless: 'new', + args: ['--no-sandbox', '--disable-setuid-sandbox'] + }); + + const allResults = []; + + try { + // Test each locale and page combination + for (const locale of LOCALES) { + console.log(`\n📍 Scanning locale: ${locale.toUpperCase()}`); + + for (const page of PAGES) { + const url = `${BASE_URL}/${locale}${page.path}`; + const result = await auditPage(browser, url, page.name); + allResults.push(result); + } + } + + console.log('\n\n📊 Processing results...'); + + // Aggregate violations + const aggregatedViolations = aggregateViolations(allResults); + + // Generate report + const report = generateMarkdownReport(allResults, aggregatedViolations); + + // Save report + const reportPath = path.join(OUTPUT_DIR, 'axe-accessibility-audit.md'); + fs.writeFileSync(reportPath, report); + + // Save JSON data for further analysis + const jsonPath = path.join(OUTPUT_DIR, 'axe-accessibility-audit.json'); + fs.writeFileSync(jsonPath, JSON.stringify({ + timestamp: new Date().toISOString(), + summary: { + pagesScanned: allResults.filter(r => !r.error).length, + uniqueViolations: aggregatedViolations.length, + totalInstances: aggregatedViolations.reduce((sum, v) => sum + v.totalInstances, 0), + bySeverity: { + critical: aggregatedViolations.filter(v => v.impact === 'critical').length, + serious: aggregatedViolations.filter(v => v.impact === 'serious').length, + moderate: aggregatedViolations.filter(v => v.impact === 'moderate').length, + minor: aggregatedViolations.filter(v => v.impact === 'minor').length + } + }, + violations: aggregatedViolations, + pageResults: allResults + }, null, 2)); + + console.log(`\n✅ Audit complete!`); + console.log(` Report: ${reportPath}`); + console.log(` JSON: ${jsonPath}`); + + // Print summary + console.log('\n📈 Summary:'); + console.log(` Pages scanned: ${allResults.filter(r => !r.error).length}`); + console.log(` Unique violations: ${aggregatedViolations.length}`); + console.log(` Critical: ${aggregatedViolations.filter(v => v.impact === 'critical').length}`); + console.log(` Serious: ${aggregatedViolations.filter(v => v.impact === 'serious').length}`); + console.log(` Moderate: ${aggregatedViolations.filter(v => v.impact === 'moderate').length}`); + console.log(` Minor: ${aggregatedViolations.filter(v => v.impact === 'minor').length}`); + + } finally { + await browser.close(); + } +} + +main().catch(error => { + console.error('Fatal error:', error); + process.exit(1); +}); diff --git a/scripts/generate-blog-images.mjs b/scripts/generate-blog-images.mjs new file mode 100644 index 0000000..7ebbbd1 --- /dev/null +++ b/scripts/generate-blog-images.mjs @@ -0,0 +1,406 @@ +import { readdir, readFile, mkdir, writeFile } from 'fs/promises'; +import { join, dirname, basename } from 'path'; +import { fileURLToPath } from 'url'; +import { existsSync } from 'fs'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Website CI/Style Guide +const CI_STYLE = { + colors: { + primary: 'sage green (#697565)', + background: 'dark moss (#181C14)', + accent: 'dark sage (#465B50)', + text: 'cream/off-white (#ECDFCC)' + }, + style: 'minimalist, glass-morphism, organic, professional, modern', + aesthetic: 'dark background with sage green and cream accents, subtle gradients, clean lines' +}; + +// Directories +const BLOG_POSTS_DIR = join(__dirname, '../blog-posts'); +const OUTPUT_DIR = join(__dirname, '../source-images/posts'); + +/** + * Call Google Gemini / Nano Banana Pro Image Generation API + */ +async function callGeminiImageAPI(prompt, apiKey) { + const model = 'gemini-2.0-flash-exp'; // Nano Banana model + const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`; + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + contents: [{ + parts: [{ text: prompt }] + }], + generationConfig: { + responseModalities: ['TEXT', 'IMAGE'] + } + }) + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({})); + const errorMessage = error.error?.message || response.statusText; + throw new Error(`Gemini API Error: ${response.status} - ${errorMessage}`); + } + + return response.json(); +} + +/** + * Call OpenAI Image Generation API directly using fetch + */ +async function callOpenAIImageAPI(prompt, apiKey) { + const response = await fetch('https://api.openai.com/v1/images/generations', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: 'gpt-image-1.5', + prompt: prompt, + n: 1, + size: '1536x1024', // 3:2 landscape (closest to 16:9 for gpt-image-1.5) + quality: 'high' + }) + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({})); + throw new Error(`OpenAI API Error: ${response.status} - ${error.error?.message || response.statusText}`); + } + + return response.json(); +} + +/** + * Extract image prompts from a blog post markdown file + */ +function extractImagePrompts(content, filename) { + const prompts = []; + + // Find the "Bildprompts" section + const bildpromptsMatch = content.match(/## Bildprompts[\s\S]*?(?=##|---|\n\n\n|$)/i); + + if (bildpromptsMatch) { + const section = bildpromptsMatch[0]; + + // Extract prompts in quotes + const promptMatches = section.matchAll(/"([^"]+)"/g); + + for (const match of promptMatches) { + prompts.push(match[1]); + } + } + + // Fallback: look for image prompt patterns + if (prompts.length === 0) { + const fallbackMatches = content.matchAll(/\*\*Bild \d+[^*]*\*\*[:\s]*"?([^"\n]+)"?/gi); + for (const match of fallbackMatches) { + prompts.push(match[1]); + } + } + + return prompts; +} + +/** + * Adapt a prompt to match the website's CI + */ +function adaptPromptToCI(originalPrompt, isHeroImage = false) { + // Extract the core concept from the original prompt + let concept = originalPrompt + .replace(/dark blue and cyan/gi, '') + .replace(/blue accents/gi, '') + .replace(/white background/gi, '') + .replace(/bright colors/gi, '') + .replace(/neon/gi, '') + .replace(/vibrant/gi, '') + .replace(/cyan/gi, '') + .replace(/8k ultra-realistic/gi, '') + .replace(/cinematic lighting/gi, '') + .replace(/color scheme/gi, '') + .trim(); + + // Build CI-compliant prompt - REALISTIC style with CI colors + const ciPrompt = ` +Photorealistic, cinematic image for a professional tech blog. 16:9 wide format, 4K quality. + +COLOR GRADING (apply as color filter/mood): +- Overall mood: Dark, moody atmosphere with deep shadows +- Dominant tones: Dark greenish-black (#181C14), muted sage green (#697565) +- Accent lighting: Warm cream/off-white (#ECDFCC) highlights +- Color temperature: Slightly warm, earthy undertones +- NO bright blue, NO cyan, NO neon colors + +PHOTOGRAPHY STYLE: +- Ultra-realistic, photographic quality +- Cinematic wide-angle composition +- Professional lighting with dramatic shadows +- Shallow depth of field where appropriate +- High-end commercial photography look +- Shot on Sony A7R IV, 24-70mm f/2.8 lens + +SCENE: ${concept} + +Create a realistic, professional photograph. The lighting should create a moody, sophisticated atmosphere using the sage green and cream color palette as accent lighting or environmental color. + +NO text overlays, NO watermarks, NO logos. +`.trim(); + + return ciPrompt; +} + +/** + * Generate an image using OpenAI's gpt-image-1.5 + */ +async function generateImage(prompt, outputPath, apiKey, provider = 'gemini', retries = 3) { + for (let attempt = 1; attempt <= retries; attempt++) { + try { + console.log(` Generating image (attempt ${attempt}/${retries}) via ${provider}...`); + console.log(` Prompt: ${prompt.substring(0, 100)}...`); + + let imageBuffer; + + if (provider === 'gemini') { + // Use Google Gemini / Nano Banana Pro + const response = await callGeminiImageAPI(prompt, apiKey); + + // Extract image from Gemini response + const candidate = response.candidates?.[0]; + if (!candidate) { + throw new Error('No candidates in response'); + } + + const parts = candidate.content?.parts || []; + const imagePart = parts.find(p => p.inlineData); + + if (!imagePart?.inlineData?.data) { + // Check if there's text response (might be safety block) + const textPart = parts.find(p => p.text); + if (textPart) { + console.log(` Response text: ${textPart.text.substring(0, 100)}...`); + } + throw new Error('No image data in Gemini response'); + } + + imageBuffer = Buffer.from(imagePart.inlineData.data, 'base64'); + + } else { + // Use OpenAI + const response = await callOpenAIImageAPI(prompt, apiKey); + const imageResult = response.data[0]; + + if (imageResult.b64_json) { + imageBuffer = Buffer.from(imageResult.b64_json, 'base64'); + } else if (imageResult.url) { + console.log(` Downloading from URL...`); + const imageResponse = await fetch(imageResult.url); + if (!imageResponse.ok) { + throw new Error(`Failed to download image: ${imageResponse.status}`); + } + const arrayBuffer = await imageResponse.arrayBuffer(); + imageBuffer = Buffer.from(arrayBuffer); + } else { + throw new Error('No image data in response'); + } + } + + // Save the image + await writeFile(outputPath, imageBuffer); + console.log(` ✓ Saved: ${outputPath}`); + + return true; + } catch (error) { + console.error(` ✗ Attempt ${attempt} failed:`, error.message); + + if (attempt === retries) { + throw error; + } + + // Wait before retry + await new Promise(resolve => setTimeout(resolve, 2000 * attempt)); + } + } +} + +/** + * Process a single blog post + */ +async function processBlogPost(filename, apiKey, options = {}) { + const { dryRun = false, forceRegenerate = false, onlyHero = true, provider = 'gemini' } = options; + + const filePath = join(BLOG_POSTS_DIR, filename); + const content = await readFile(filePath, 'utf-8'); + + // Extract slug from filename (e.g., "01-agentic-ai-2026.md" -> "01-agentic-ai-2026") + const slug = basename(filename, '.md'); + + // Extract image prompts + const prompts = extractImagePrompts(content, filename); + + if (prompts.length === 0) { + console.log(`⚠ No image prompts found in ${filename}`); + return { filename, slug, generated: 0, skipped: 0 }; + } + + console.log(`\n📄 ${filename}`); + console.log(` Found ${prompts.length} image prompt(s)`); + + // Create output directory + const postOutputDir = join(OUTPUT_DIR, slug); + if (!dryRun) { + await mkdir(postOutputDir, { recursive: true }); + } + + let generated = 0; + let skipped = 0; + + // Process prompts (only hero image by default) + const promptsToProcess = onlyHero ? [prompts[0]] : prompts; + + for (let i = 0; i < promptsToProcess.length; i++) { + const prompt = promptsToProcess[i]; + const imageName = i === 0 ? 'cover.jpg' : `image-${i + 1}.jpg`; + const outputPath = join(postOutputDir, imageName); + + // Check if image already exists + if (existsSync(outputPath) && !forceRegenerate) { + console.log(` ⏭ Skipping ${imageName} (already exists)`); + skipped++; + continue; + } + + // Adapt prompt to CI + const adaptedPrompt = adaptPromptToCI(prompt, i === 0); + + if (dryRun) { + console.log(` [DRY RUN] Would generate: ${imageName}`); + console.log(` Original: ${prompt.substring(0, 80)}...`); + console.log(` Adapted: ${adaptedPrompt.substring(0, 80)}...`); + } else { + try { + await generateImage(adaptedPrompt, outputPath, apiKey, provider); + generated++; + + // Rate limiting: wait between requests + if (i < promptsToProcess.length - 1) { + console.log(' Waiting 2s before next image...'); + await new Promise(resolve => setTimeout(resolve, 2000)); + } + } catch (error) { + console.error(` ✗ Failed to generate ${imageName}:`, error.message); + } + } + } + + return { filename, slug, generated, skipped }; +} + +/** + * Main function + */ +async function main() { + console.log('🎨 Blog Image Generator'); + console.log('========================\n'); + + // Get API key from environment or command line + const apiKey = process.env.OPENAI_API_KEY || + process.argv.find(a => a.startsWith('--api-key='))?.split('=')[1]; + + // Check for API key + if (!apiKey) { + console.error('❌ Error: OPENAI_API_KEY not provided'); + console.log('\nUsage:'); + console.log(' export OPENAI_API_KEY=sk-your-key-here'); + console.log(' node scripts/generate-blog-images.js'); + console.log('\nOr:'); + console.log(' node scripts/generate-blog-images.js --api-key=sk-your-key-here'); + process.exit(1); + } + + // Detect provider based on API key format + const provider = apiKey.startsWith('AIzaSy') ? 'gemini' : 'openai'; + console.log(`Provider: ${provider === 'gemini' ? 'Google Gemini (Nano Banana)' : 'OpenAI'}\n`); + + // Parse command line arguments + const args = process.argv.slice(2); + const options = { + dryRun: args.includes('--dry-run'), + forceRegenerate: args.includes('--force'), + onlyHero: !args.includes('--all-images'), + single: args.find(a => a.startsWith('--file='))?.split('=')[1], + range: args.find(a => a.startsWith('--range='))?.split('=')[1], + provider + }; + + if (options.dryRun) { + console.log('🔍 DRY RUN MODE - No images will be generated\n'); + } + + console.log(`CI Style: ${CI_STYLE.style}`); + console.log(`Colors: ${Object.values(CI_STYLE.colors).join(', ')}\n`); + + // Get list of blog posts + let files; + + if (options.single) { + files = [options.single]; + } else { + files = (await readdir(BLOG_POSTS_DIR)) + .filter(f => f.endsWith('.md')) + .sort(); + + // Filter by range if specified (e.g., --range=1-10) + if (options.range) { + const [start, end] = options.range.split('-').map(Number); + files = files.filter(f => { + const num = parseInt(f.split('-')[0], 10); + return num >= start && num <= end; + }); + } + } + + console.log(`Found ${files.length} blog post(s) to process\n`); + + // Process each blog post + const results = []; + + for (const file of files) { + try { + const result = await processBlogPost(file, apiKey, options); + results.push(result); + } catch (error) { + console.error(`❌ Error processing ${file}:`, error.message); + results.push({ filename: file, error: error.message }); + } + } + + // Summary + console.log('\n========================'); + console.log('📊 Summary\n'); + + const totalGenerated = results.reduce((sum, r) => sum + (r.generated || 0), 0); + const totalSkipped = results.reduce((sum, r) => sum + (r.skipped || 0), 0); + const totalErrors = results.filter(r => r.error).length; + + console.log(`✓ Generated: ${totalGenerated} images`); + console.log(`⏭ Skipped: ${totalSkipped} images (already exist)`); + console.log(`✗ Errors: ${totalErrors}`); + + if (!options.dryRun && totalGenerated > 0) { + console.log('\n📝 Next steps:'); + console.log('1. Review generated images in source-images/posts/'); + console.log('2. Run `npm run build:images` to optimize images'); + } +} + +// Run +main().catch(console.error); diff --git a/scripts/lighthouse-audit-all-locales.js b/scripts/lighthouse-audit-all-locales.js new file mode 100644 index 0000000..c9b7b73 --- /dev/null +++ b/scripts/lighthouse-audit-all-locales.js @@ -0,0 +1,528 @@ +#!/usr/bin/env node +/** + * Lighthouse Audit for All Locales + * Runs PageSpeed Insights API tests for all three language versions (de, en, sr) + * Documents Core Web Vitals (LCP, CLS, INP) for both mobile and desktop modes + */ + +import https from 'https'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +// Disable SSL verification for local testing (network proxy issue) +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const API_KEY = 'AIzaSyA4qo0LfLy_ps-SyTQTQpFrFRjDnAUn_K4'; +const BASE_URL = 'https://www.damjan-savic.com'; +const LOCALES = ['de', 'en', 'sr']; +const STRATEGIES = ['mobile', 'desktop']; + +const REPORTS_DIR = path.join(__dirname, '..', '.auto-claude', 'specs', '001-suche-nach-optimierungsvorschl-gen', 'lighthouse-reports'); + +function httpsGet(url) { + return new Promise((resolve, reject) => { + https.get(url, (res) => { + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => { + try { + resolve(JSON.parse(data)); + } catch (e) { + reject(new Error(`JSON Parse Error: ${e.message}`)); + } + }); + }).on('error', reject); + }); +} + +async function runPageSpeedTest(url, strategy = 'mobile') { + const apiUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}&key=${API_KEY}&strategy=${strategy}&category=performance&category=accessibility&category=best-practices&category=seo`; + + console.log(`\n Testing ${url} (${strategy})...`); + console.log(' Please wait, this may take 30-60 seconds...\n'); + + try { + const data = await httpsGet(apiUrl); + + if (data.error) { + throw new Error(`API Error: ${data.error.message}`); + } + + return data; + } catch (error) { + console.error(` Error testing ${url}:`, error.message); + throw error; + } +} + +function extractCoreWebVitals(data) { + const lighthouse = data.lighthouseResult; + const categories = lighthouse.categories; + const audits = lighthouse.audits; + + const fcp = audits['first-contentful-paint']; + const lcp = audits['largest-contentful-paint']; + const tbt = audits['total-blocking-time']; + const cls = audits['cumulative-layout-shift']; + const si = audits['speed-index']; + const inp = audits['interaction-to-next-paint'] || audits['experimental-interaction-to-next-paint']; + + // Extract opportunities + const opportunities = Object.values(audits) + .filter(audit => audit.details?.type === 'opportunity' && audit.details?.overallSavingsMs > 0) + .sort((a, b) => (b.details?.overallSavingsMs || 0) - (a.details?.overallSavingsMs || 0)) + .map(opp => ({ + title: opp.title, + description: opp.description, + savings_ms: opp.details?.overallSavingsMs || 0, + savings_bytes: opp.details?.overallSavingsBytes || 0 + })); + + // Extract diagnostics + const diagnostics = Object.values(audits) + .filter(audit => audit.details?.type === 'table' && audit.score !== null && audit.score < 0.9) + .map(diag => ({ + title: diag.title, + description: diag.description, + score: diag.score + })); + + return { + url: lighthouse.finalUrl, + fetchTime: lighthouse.fetchTime, + userAgent: lighthouse.userAgent, + scores: { + performance: Math.round(categories.performance.score * 100), + accessibility: Math.round(categories.accessibility.score * 100), + bestPractices: Math.round(categories['best-practices'].score * 100), + seo: Math.round(categories.seo.score * 100) + }, + coreWebVitals: { + FCP: { + value: fcp?.numericValue || null, + displayValue: fcp?.displayValue || 'N/A', + score: fcp?.score || null + }, + LCP: { + value: lcp?.numericValue || null, + displayValue: lcp?.displayValue || 'N/A', + score: lcp?.score || null + }, + CLS: { + value: cls?.numericValue || null, + displayValue: cls?.displayValue || 'N/A', + score: cls?.score || null + }, + TBT: { + value: tbt?.numericValue || null, + displayValue: tbt?.displayValue || 'N/A', + score: tbt?.score || null + }, + INP: { + value: inp?.numericValue || null, + displayValue: inp?.displayValue || 'N/A', + score: inp?.score || null + }, + SpeedIndex: { + value: si?.numericValue || null, + displayValue: si?.displayValue || 'N/A', + score: si?.score || null + } + }, + opportunities: opportunities.slice(0, 10), + diagnostics: diagnostics.slice(0, 10) + }; +} + +function formatTime(ms) { + if (ms === null || ms === undefined) return 'N/A'; + if (ms < 1000) return `${Math.round(ms)}ms`; + return `${(ms / 1000).toFixed(2)}s`; +} + +function getScoreEmoji(score) { + if (score >= 90) return 'green'; + if (score >= 50) return 'yellow'; + return 'red'; +} + +function generateMarkdownReport(allResults) { + const timestamp = new Date().toISOString(); + let markdown = `# Lighthouse Audit Report - Core Web Vitals + +**Generated:** ${timestamp} +**Base URL:** ${BASE_URL} +**Locales Tested:** ${LOCALES.join(', ')} + +--- + +## Executive Summary + +This report documents Core Web Vitals (LCP, CLS, INP/TBT) measurements for all three language versions of the portfolio website in both mobile and desktop modes. + +### Key Metrics Explained: + +- **LCP (Largest Contentful Paint):** Measures loading performance. Good: < 2.5s, Needs Improvement: 2.5-4s, Poor: > 4s +- **CLS (Cumulative Layout Shift):** Measures visual stability. Good: < 0.1, Needs Improvement: 0.1-0.25, Poor: > 0.25 +- **INP/TBT (Interaction to Next Paint / Total Blocking Time):** Measures interactivity. TBT Good: < 200ms, INP Good: < 200ms +- **FCP (First Contentful Paint):** Measures perceived load speed. Good: < 1.8s +- **Speed Index:** How quickly content is visually displayed. Good: < 3.4s + +--- + +## Results Summary Table + +### Mobile Results + +| Locale | Performance | LCP | CLS | TBT | FCP | Accessibility | SEO | +|--------|-------------|-----|-----|-----|-----|---------------|-----| +`; + + // Add mobile results + LOCALES.forEach(locale => { + const result = allResults[`${locale}_mobile`]; + if (result) { + const cwv = result.coreWebVitals; + markdown += `| ${locale.toUpperCase()} | ${result.scores.performance}% | ${cwv.LCP.displayValue} | ${cwv.CLS.displayValue} | ${cwv.TBT.displayValue} | ${cwv.FCP.displayValue} | ${result.scores.accessibility}% | ${result.scores.seo}% |\n`; + } + }); + + markdown += ` +### Desktop Results + +| Locale | Performance | LCP | CLS | TBT | FCP | Accessibility | SEO | +|--------|-------------|-----|-----|-----|-----|---------------|-----| +`; + + // Add desktop results + LOCALES.forEach(locale => { + const result = allResults[`${locale}_desktop`]; + if (result) { + const cwv = result.coreWebVitals; + markdown += `| ${locale.toUpperCase()} | ${result.scores.performance}% | ${cwv.LCP.displayValue} | ${cwv.CLS.displayValue} | ${cwv.TBT.displayValue} | ${cwv.FCP.displayValue} | ${result.scores.accessibility}% | ${result.scores.seo}% |\n`; + } + }); + + markdown += ` +--- + +## Detailed Results by Locale + +`; + + // Detailed results for each locale + LOCALES.forEach(locale => { + markdown += `### ${locale.toUpperCase()} - ${locale === 'de' ? 'German' : locale === 'en' ? 'English' : 'Serbian'} + +**URL:** ${BASE_URL}/${locale} + +`; + + STRATEGIES.forEach(strategy => { + const result = allResults[`${locale}_${strategy}`]; + if (result) { + const cwv = result.coreWebVitals; + markdown += `#### ${strategy.charAt(0).toUpperCase() + strategy.slice(1)} + +**Scores:** +- Performance: ${result.scores.performance}% +- Accessibility: ${result.scores.accessibility}% +- Best Practices: ${result.scores.bestPractices}% +- SEO: ${result.scores.seo}% + +**Core Web Vitals:** +| Metric | Value | Score | +|--------|-------|-------| +| First Contentful Paint (FCP) | ${cwv.FCP.displayValue} | ${cwv.FCP.score !== null ? Math.round(cwv.FCP.score * 100) + '%' : 'N/A'} | +| Largest Contentful Paint (LCP) | ${cwv.LCP.displayValue} | ${cwv.LCP.score !== null ? Math.round(cwv.LCP.score * 100) + '%' : 'N/A'} | +| Cumulative Layout Shift (CLS) | ${cwv.CLS.displayValue} | ${cwv.CLS.score !== null ? Math.round(cwv.CLS.score * 100) + '%' : 'N/A'} | +| Total Blocking Time (TBT) | ${cwv.TBT.displayValue} | ${cwv.TBT.score !== null ? Math.round(cwv.TBT.score * 100) + '%' : 'N/A'} | +| Speed Index | ${cwv.SpeedIndex.displayValue} | ${cwv.SpeedIndex.score !== null ? Math.round(cwv.SpeedIndex.score * 100) + '%' : 'N/A'} | + +`; + + if (result.opportunities && result.opportunities.length > 0) { + markdown += `**Optimization Opportunities:** +`; + result.opportunities.slice(0, 5).forEach(opp => { + markdown += `- **${opp.title}**: Potential savings of ${formatTime(opp.savings_ms)}`; + if (opp.savings_bytes > 0) { + markdown += ` / ${(opp.savings_bytes / 1024).toFixed(1)} KB`; + } + markdown += `\n`; + }); + markdown += '\n'; + } + + if (result.diagnostics && result.diagnostics.length > 0) { + markdown += `**Diagnostics (Items Needing Attention):** +`; + result.diagnostics.slice(0, 5).forEach(diag => { + markdown += `- ${diag.title} (Score: ${Math.round(diag.score * 100)}%)\n`; + }); + markdown += '\n'; + } + } + }); + }); + + markdown += `--- + +## Core Web Vitals Analysis + +### LCP (Largest Contentful Paint) Summary + +`; + + // LCP analysis + const lcpValues = []; + Object.entries(allResults).forEach(([key, result]) => { + if (result && result.coreWebVitals.LCP.value) { + lcpValues.push({ + test: key, + value: result.coreWebVitals.LCP.value, + displayValue: result.coreWebVitals.LCP.displayValue + }); + } + }); + + if (lcpValues.length > 0) { + const avgLCP = lcpValues.reduce((sum, item) => sum + item.value, 0) / lcpValues.length; + const maxLCP = Math.max(...lcpValues.map(item => item.value)); + const minLCP = Math.min(...lcpValues.map(item => item.value)); + + markdown += `- **Average LCP:** ${formatTime(avgLCP)} +- **Best LCP:** ${formatTime(minLCP)} +- **Worst LCP:** ${formatTime(maxLCP)} +- **Target:** < 2.5s for Good score + +`; + } + + markdown += `### CLS (Cumulative Layout Shift) Summary + +`; + + // CLS analysis + const clsValues = []; + Object.entries(allResults).forEach(([key, result]) => { + if (result && result.coreWebVitals.CLS.value !== null) { + clsValues.push({ + test: key, + value: result.coreWebVitals.CLS.value, + displayValue: result.coreWebVitals.CLS.displayValue + }); + } + }); + + if (clsValues.length > 0) { + const avgCLS = clsValues.reduce((sum, item) => sum + item.value, 0) / clsValues.length; + const maxCLS = Math.max(...clsValues.map(item => item.value)); + + markdown += `- **Average CLS:** ${avgCLS.toFixed(3)} +- **Worst CLS:** ${maxCLS.toFixed(3)} +- **Target:** < 0.1 for Good score + +`; + } + + markdown += `### TBT (Total Blocking Time) Summary + +`; + + // TBT analysis + const tbtValues = []; + Object.entries(allResults).forEach(([key, result]) => { + if (result && result.coreWebVitals.TBT.value !== null) { + tbtValues.push({ + test: key, + value: result.coreWebVitals.TBT.value, + displayValue: result.coreWebVitals.TBT.displayValue + }); + } + }); + + if (tbtValues.length > 0) { + const avgTBT = tbtValues.reduce((sum, item) => sum + item.value, 0) / tbtValues.length; + const maxTBT = Math.max(...tbtValues.map(item => item.value)); + + markdown += `- **Average TBT:** ${formatTime(avgTBT)} +- **Worst TBT:** ${formatTime(maxTBT)} +- **Target:** < 200ms for Good score + +`; + } + + markdown += `--- + +## Recommendations Summary + +Based on the audit results, here are the key areas for improvement: + +### High Priority (Performance Impact) + +1. **Largest Contentful Paint (LCP):** Focus on optimizing the hero image and main content loading +2. **Total Blocking Time (TBT):** Reduce JavaScript execution time, consider code splitting +3. **First Contentful Paint (FCP):** Optimize critical rendering path + +### Medium Priority (User Experience) + +1. **Cumulative Layout Shift (CLS):** Ensure images have explicit dimensions, avoid layout shifts from fonts +2. **Speed Index:** Optimize above-the-fold content delivery + +### Common Opportunities Across All Tests + +`; + + // Aggregate opportunities + const allOpportunities = {}; + Object.values(allResults).forEach(result => { + if (result && result.opportunities) { + result.opportunities.forEach(opp => { + if (!allOpportunities[opp.title]) { + allOpportunities[opp.title] = { + count: 0, + totalSavings: 0, + title: opp.title + }; + } + allOpportunities[opp.title].count++; + allOpportunities[opp.title].totalSavings += opp.savings_ms; + }); + } + }); + + const sortedOpportunities = Object.values(allOpportunities) + .sort((a, b) => b.totalSavings - a.totalSavings) + .slice(0, 10); + + sortedOpportunities.forEach((opp, index) => { + markdown += `${index + 1}. **${opp.title}** - Appeared in ${opp.count} tests, avg. savings: ${formatTime(opp.totalSavings / opp.count)}\n`; + }); + + markdown += ` + +--- + +## Raw Data Files + +Individual JSON files with complete audit data are saved in the \`lighthouse-reports\` directory: + +`; + + Object.keys(allResults).forEach(key => { + markdown += `- \`${key}.json\`\n`; + }); + + markdown += ` +--- + +*Report generated by lighthouse-audit-all-locales.js* +`; + + return markdown; +} + +async function main() { + console.log('\n========================================'); + console.log(' Lighthouse Audit - All Locales'); + console.log('========================================\n'); + console.log(`Base URL: ${BASE_URL}`); + console.log(`Locales: ${LOCALES.join(', ')}`); + console.log(`Strategies: ${STRATEGIES.join(', ')}`); + console.log(`Reports Directory: ${REPORTS_DIR}`); + console.log('\n----------------------------------------\n'); + + // Ensure reports directory exists + if (!fs.existsSync(REPORTS_DIR)) { + fs.mkdirSync(REPORTS_DIR, { recursive: true }); + } + + const allResults = {}; + const errors = []; + + // Run tests for each locale and strategy + for (const locale of LOCALES) { + const url = `${BASE_URL}/${locale}`; + console.log(`\n>>> Testing ${locale.toUpperCase()} (${url})`); + + for (const strategy of STRATEGIES) { + const key = `${locale}_${strategy}`; + try { + const data = await runPageSpeedTest(url, strategy); + const results = extractCoreWebVitals(data); + allResults[key] = results; + + // Save individual JSON file + const jsonPath = path.join(REPORTS_DIR, `${key}.json`); + fs.writeFileSync(jsonPath, JSON.stringify(results, null, 2)); + console.log(` Saved: ${key}.json`); + + // Display quick summary + console.log(` Performance: ${results.scores.performance}% | LCP: ${results.coreWebVitals.LCP.displayValue} | CLS: ${results.coreWebVitals.CLS.displayValue}`); + + } catch (error) { + console.error(` FAILED: ${key} - ${error.message}`); + errors.push({ key, error: error.message }); + } + } + } + + // Generate and save markdown report + console.log('\n----------------------------------------'); + console.log('Generating comprehensive report...'); + + const markdownReport = generateMarkdownReport(allResults); + const reportPath = path.join(REPORTS_DIR, 'LIGHTHOUSE_REPORT.md'); + fs.writeFileSync(reportPath, markdownReport); + console.log(`Report saved: ${reportPath}`); + + // Save combined JSON + const combinedPath = path.join(REPORTS_DIR, 'all_results.json'); + fs.writeFileSync(combinedPath, JSON.stringify({ + timestamp: new Date().toISOString(), + baseUrl: BASE_URL, + locales: LOCALES, + strategies: STRATEGIES, + results: allResults, + errors: errors + }, null, 2)); + console.log(`Combined data saved: ${combinedPath}`); + + // Final summary + console.log('\n========================================'); + console.log(' AUDIT COMPLETE'); + console.log('========================================\n'); + + console.log('Results Summary:'); + console.log('----------------'); + + LOCALES.forEach(locale => { + console.log(`\n${locale.toUpperCase()}:`); + STRATEGIES.forEach(strategy => { + const result = allResults[`${locale}_${strategy}`]; + if (result) { + console.log(` ${strategy.padEnd(8)}: Performance ${result.scores.performance}% | LCP ${result.coreWebVitals.LCP.displayValue.padEnd(6)} | CLS ${result.coreWebVitals.CLS.displayValue}`); + } else { + console.log(` ${strategy.padEnd(8)}: FAILED`); + } + }); + }); + + if (errors.length > 0) { + console.log(`\n${errors.length} test(s) failed. Check the logs above for details.`); + } + + console.log('\n----------------------------------------'); + console.log(`Reports saved to: ${REPORTS_DIR}`); + console.log('----------------------------------------\n'); +} + +main().catch(error => { + console.error('Fatal error:', error); + process.exit(1); +}); diff --git a/src/app/[locale]/imprint/page.tsx b/src/app/[locale]/imprint/page.tsx index 1e5ebe1..9b17756 100644 --- a/src/app/[locale]/imprint/page.tsx +++ b/src/app/[locale]/imprint/page.tsx @@ -20,7 +20,7 @@ export async function generateMetadata({ params }: Props): Promise { const descriptions: Record = { de: 'Impressum und rechtliche Informationen für die Portfolio-Website von Damjan Savić, KI Entwickler und Automation Specialist aus Köln.', en: 'Legal notice and contact information for Damjan Savić portfolio website, AI Developer and Automation Specialist.', - sr: 'Pravno obavestenje i kontakt informacije za portfolio veb sajt Damjana Savica, AI Developer i Automation Specialist.', + sr: 'Pravno obavestenje i kontakt informacije za portfolio veb sajt Damjana Savića, AI Developer i Automation Specialist.', }; const localePath = locale === 'de' ? '/imprint' : `/${locale}/imprint`; diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index f5af648..f0e2e7d 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -31,15 +31,15 @@ export async function generateMetadata({ params }: Props): Promise { const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com'; const titles: Record = { - de: 'Damjan Savić | AI & Automation Specialist aus Köln', - en: 'Damjan Savić | AI & Automation Specialist from Cologne', - sr: 'Damjan Savić | AI & Automation Specialist iz Kelna', + de: 'Damjan Savić | Fullstack Developer aus Köln', + en: 'Damjan Savić | Fullstack Developer from Cologne', + sr: 'Damjan Savić | Fullstack Developer iz Kelna', }; const descriptions: Record = { - de: 'Entwicklung von KI-Agenten und Automatisierungslösungen. Von Voice-AI-Plattformen bis zu autonomen Web-Agenten.', - en: 'Building AI agents and automation solutions. From voice AI platforms to autonomous web agents.', - sr: 'Razvoj AI agenata i resenja za automatizaciju. Od voice AI platformi do autonomnih web agenata.', + de: 'Fullstack Entwicklung für Websites, Apps und SaaS mit Next.js, React und TypeScript.', + en: 'Fullstack development for websites, apps and SaaS with Next.js, React and TypeScript.', + sr: 'Fullstack razvoj za web stranice, aplikacije i SaaS sa Next.js, React i TypeScript.', }; // Generate alternates using localeMetadata diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 138e569..6c10fb0 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,9 +4,9 @@ import './globals.css'; export const metadata: Metadata = { title: { template: '%s | Damjan Savić', - default: 'Damjan Savić | AI & Automation Specialist', + default: 'Damjan Savić | Fullstack Developer', }, - description: 'AI & Automation Specialist - Building AI agents and automation solutions.', + description: 'Fullstack Developer - Building websites, apps and SaaS with Next.js, React and TypeScript.', metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'https://damjan-savic.com'), }; diff --git a/src/components-vite/SEO.tsx b/src/components-vite/SEO.tsx index 5f8c2ae..00cb83c 100644 --- a/src/components-vite/SEO.tsx +++ b/src/components-vite/SEO.tsx @@ -52,7 +52,7 @@ const SEO: React.FC = ({ '@context': 'https://schema.org', '@type': 'Person', name: 'Damjan Savić', - alternateName: 'Damjan Savic', + alternateName: 'Damjan Savić', url: siteUrl, image: `${siteUrl}${image}`, description: defaultDescription, diff --git a/src/components-vite/schemas/LocalBusinessSchema.tsx b/src/components-vite/schemas/LocalBusinessSchema.tsx index ab4354e..73b7fe3 100644 --- a/src/components-vite/schemas/LocalBusinessSchema.tsx +++ b/src/components-vite/schemas/LocalBusinessSchema.tsx @@ -6,7 +6,7 @@ export function LocalBusinessSchema() { "@type": "LocalBusiness", "@id": "https://damjan-savic.com/#localbusiness", "name": "Damjan Savić - Senior Fullstack Developer & IT Consultant", - "alternateName": ["CoderConda", "Damjan Savic IT Services"], + "alternateName": ["CoderConda", "Damjan Savić IT Services"], "description": "Damjan Savić bietet professionelle IT-Dienstleistungen in Köln und Umgebung. Spezialisiert auf Enterprise Software Development, KI-Integration, Cloud Architecture und digitale Transformation. Damjan Savić entwickelt maßgeschneiderte Lösungen für Ihr Unternehmen.", "url": "https://damjan-savic.com", "logo": { diff --git a/src/components-vite/schemas/LocalizedSchemas.tsx b/src/components-vite/schemas/LocalizedSchemas.tsx index 03dd151..6c77e99 100644 --- a/src/components-vite/schemas/LocalizedSchemas.tsx +++ b/src/components-vite/schemas/LocalizedSchemas.tsx @@ -10,7 +10,7 @@ export function LocalizedPersonSchema() { "@type": "Person", "@id": "https://damjan-savic.com/#person", "name": currentLanguage === 'sr' ? "Дамјан Савић" : "Damjan Savić", - "alternateName": currentLanguage === 'sr' ? ["Damjan Savić", "Damjan Savic"] : "Damjan Savic", + "alternateName": currentLanguage === 'sr' ? ["Damjan Savić", "Damjan Savić"] : "Damjan Savić", "jobTitle": currentLanguage === 'de' ? ["AI & Automation Specialist", "Process Automation Specialist", "Fullstack Entwickler"] : currentLanguage === 'sr' ? @@ -199,7 +199,7 @@ export function LocalizedWebsiteSchema() { currentLanguage === 'sr' ? "Дамјан Савић - AI & Automation Specialist" : "Damjan Savić - AI & Automation Specialist", - "alternateName": ["Damjan Savic Portfolio"], + "alternateName": ["Damjan Savić Portfolio"], "description": currentLanguage === 'de' ? "Offizielle Website von Damjan Savić - AI & Automation Specialist. Spezialisiert auf KI-Agenten, Voice AI, Prozessautomatisierung mit n8n und Fullstack Development." : currentLanguage === 'sr' ? diff --git a/src/components-vite/schemas/PersonSchema.tsx b/src/components-vite/schemas/PersonSchema.tsx index 96e4229..1f4537b 100644 --- a/src/components-vite/schemas/PersonSchema.tsx +++ b/src/components-vite/schemas/PersonSchema.tsx @@ -6,7 +6,7 @@ export function PersonSchema() { "@type": "Person", "@id": "https://damjan-savic.com/#person", "name": "Damjan Savić", - "alternateName": "Damjan Savic", + "alternateName": "Damjan Savić", "jobTitle": ["Senior Fullstack Developer", "Digital Solutions Consultant", "Software Architect", "KI/AI Spezialist"], "description": "Damjan Savić ist ein Senior Fullstack Entwickler und Digital Solutions Consultant aus Köln. Damjan Savić ist spezialisiert auf Python, JavaScript, React, Next.js, TypeScript und KI-Integration. Mit über 10 Jahren Erfahrung entwickelt Damjan Savić maßgeschneiderte Enterprise-Lösungen, moderne Web-Applikationen und innovative KI-gestützte Systeme für Unternehmen jeder Größe.", "url": "https://damjan-savic.com", diff --git a/src/components-vite/schemas/WebsiteSchema.tsx b/src/components-vite/schemas/WebsiteSchema.tsx index 98a7ac4..2338230 100644 --- a/src/components-vite/schemas/WebsiteSchema.tsx +++ b/src/components-vite/schemas/WebsiteSchema.tsx @@ -7,7 +7,7 @@ export function WebsiteSchema() { "@id": "https://damjan-savic.com/#website", "url": "https://damjan-savic.com", "name": "Damjan Savić - Senior Fullstack Developer & Digital Solutions Consultant", - "alternateName": ["Damjan Savic Portfolio", "CoderConda"], + "alternateName": ["Damjan Savić Portfolio", "CoderConda"], "description": "Offizielle Website von Damjan Savić - Senior Fullstack Entwickler und Digital Solutions Consultant aus Köln. Spezialisiert auf Enterprise Software Development, KI-Integration, Cloud Architecture und moderne Web-Technologien. Entdecken Sie innovative Lösungen von Damjan Savić.", "publisher": { "@id": "https://damjan-savic.com/#person" diff --git a/src/components/contact/ContactForm.tsx b/src/components/contact/ContactForm.tsx index 15bbb4e..54228c3 100644 --- a/src/components/contact/ContactForm.tsx +++ b/src/components/contact/ContactForm.tsx @@ -111,7 +111,7 @@ export function ContactForm() {

Damjan Savić
- +

@@ -154,7 +154,7 @@ export function ContactForm() { className="flex items-start gap-4" >
- +

@@ -176,7 +176,7 @@ export function ContactForm() { className="flex items-start gap-4" >
- +

@@ -210,7 +210,7 @@ export function ContactForm() { >
- +
@@ -252,10 +255,13 @@ export function ContactForm() { onChange={handleChange} placeholder={t('contactForm.email.placeholder')} disabled={isSubmitting} + aria-required="true" + aria-invalid={!!errors.email} + aria-describedby={errors.email ? 'email-error' : undefined} className="w-full h-12 px-4 bg-zinc-900/50 border border-zinc-800 rounded-lg text-white placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-600 disabled:opacity-50" /> {errors.email && ( -

{errors.email}

+ )}
@@ -271,10 +277,13 @@ export function ContactForm() { placeholder={t('contactForm.message.placeholder')} rows={6} disabled={isSubmitting} + aria-required="true" + aria-invalid={!!errors.message} + aria-describedby={errors.message ? 'message-error' : undefined} className="w-full px-4 py-3 bg-zinc-900/50 border border-zinc-800 rounded-lg text-white placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-600 disabled:opacity-50 resize-none" /> {errors.message && ( -

{errors.message}

+ )}

@@ -283,9 +292,11 @@ export function ContactForm() { - +

@@ -307,13 +320,15 @@ export function ContactForm() {

_e^oCR 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 aWQnwFuID9ewBEl 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%0D+Y-LxaEhU&lYjm;-$Q zKj{XTCviJzPKOP&ZX{HX^2frJ{{TE$+(bZ@)>x#B>sLqI4t+it<0RoYwS%+$LxJ_rlBS*jU;hOe)#BMkeUKs+~3WFZ+h~ z8hJSG-|QO;n{G1BNXM_-o#!MBIKI~7tqld#?=H~dB4{z#{{Y-4z8o(P?C&9M+Jn7) z-t7C^=wi0)TPv<+;rn*RY35*Kp8JgcopvH+4ZdbY1O+GF{{VToPD1Co{{Xpo6+pzzhq*nfEZV83ez0dcl*hEWq%iR@V&9xcSC{U z?Yn=6vbF54(Br!=hnml~_{3PzPdX^!m3(n4rQ2OZ=5Mb50HpC^Irwhh7f%(Zs{~(4ZtkImm2$E$V%paS z=q=5^A9duC_=5~^K|q16-bxXcK!aL!#oXNF?Rm`oD-kqhq>^igtL%--Z3isbw8sK3 zqmJvCfJtU7c>6`YDDe;b6}fo9Qjhsq9O}d8j^N~ZFLjT!9h-GJ&mSJ}q`bc?b0XF| zejdsn`els<;xgA9mG$=}%y+kV-NP(gQ*sjAUYKK$`fG7;tW07>uc(7=xndsv_d}fH zdr!9c9#YwPE?1Ykir0);HF@gV@|uv5T(fbag!M8q2s| z@iW`e=MK7OLtL`NyjNz~N^GN?qjERSWxBt3oP%;1tLewr<%@~?JB@B@l|ns!W^}I? z$K~c&<6-98nci_Pdvg{XXS;sk@UYKuW&Z$@OzrKDW8bS*E@!wQ9(%U606M^`k+Gth zkz8`V==*~tp7D3IkgZfQ*3f#e|M7Bj$NBgN)<<08y?kx8=ShT=v(g8e+$S=XqWtM4o%&u)7Uhv7Tg_RX!W z`#ZO;Zux%S>?ke(Enz50?h;-_6+pO#-)f_ITvSk7aqisbJKP+_+_6K(aqwK;-bTWN z!~HJNS>qY`cYqH{TQ1sHae&+5G!YadkxgDfu- z?r*vsh1}hrY_EOH`)};uYBl4VdrNjpYi``P;~{4SO9c*~16=xJkM9o5_Op`qhqf;} zi1D_4-|pXeaebwG2W~))^S2dk-B(ZqxsoLf5OAau&g++O3H3m(GYc}sra?h9LbdXvq{8#DCQ2^gtu%^SQ4hVz!oXvfJCMBUF0qgL72nPYrM(?7wg9yKjw(A5Iu;7Ee}GW}uawzy+z{ zg}m-N#NoJ@#`J@cxJZ_@1y0@7p;CNtEFH((O?MP1!b@%z*VkE`XXvX)DAgjB1o+`C zy{7ipljMGsGd-+!uX?vIsHd%TutoYobslYX!p+R~{m*GwNCP+MCBDeKObB;*b*Udb z3ywpZyIXi-Ey(h-GrhCA3bbtRwwZVXgs|cVAIQ$KR$1Bwtg=U$lhow6fa$xy>?gwr~jVjkn*JDa)3BM9|ivVP|0t+;Z`ckg>UNs<<( zo}SV^%(3;|)7+e;*AXLcpM>qcLEaWpw5W~A^Dblx>xScb&h74A)4!Ww&Z} z$~|u1542pTadCBxVdi^I=vi{6IKWq$9!46+ zARo#)(uW*W9@hJ}-QL`Lp#&e(A+hghEw~<9<^yify@OpfZ7(7$qdY_d(m9H(XX z*JO6DbT1kf3!d)md)BbIu$p^t^p;D@e@a=HeybFkFiml1-FydicDFg$%fz=AC69l0 z#ntaO#BLs(vDn^PtnEtWRdT-(i!X3EKKbYUr|)Y!xT4}atGCVFjjg<~f$7SV+nCu= zi9Sakms}rjedYe<`(K#s3;sSP;62ypeZG&}G20gC0voHY+Trf_>E=aRdClQrLP#eZ zmmS~V+}~(_b#ixy4QFfZuVDWGZNJ-ZJKBBsCm{^0J?}NocNcZ{rOy%}NcTr2yTu9f zhV^Q+FmoJNw2AE}VcXq2vnRa#?-Os{T&EaY(pezlyK-C0cwB&3MlPga2qu7ndy~02 z3E~_#Yj*b|+O^<6$z)*5T+_k}{|m?LOt>mQ~_=$G13M>Ea$`FC+0>=N_D; zoRfMhL~lw&+@w~d^rk%(^C~DVXuwgjP)lIZwgXb9Zt7H0fP8V}4VkGTwI{&+_})My z3Tdqc1_5lr>C-&(zyoenp#B&XX7xxrlpLZT>0ieIixL{Uv+7MNf+?XRU&jlVg>%-Y z3%Z0<4Zjk))a8W$Q_3sx&Y=t39p!pv0bE% zesnn0lm3bTRCP5c2df^H{us`Jx`JpsOo+g zQga|tPC}G9_&*AdNmu^Q~vd+0+At6|;L8q4^m?y^t zS!ybzP|y;FqcQQu?x>mX1DI`-}dU$`s1PsZhct2)7 zABV#k8hB9jrg~#j_n+H9MRlcrV$gK{I29DAui1qJ(C5&ezYKX*NbxJ4C`NSXG97ck z%m8|vhueTyu_yf`92!=Y%DU5~aa>dyc>GUXW`6;Ve*=M$H6Ua(_~J&_Yfw|JbjW-# zqN6Y?U&LV0E=R)$%yiJ4zU)+H$N53Xigi6Y;I@Z6sb9F_oj(4Qz++k+M~*5*02vdm znq&MQ--?r;hIkauGvoYlGx0yTW6Y{Yzyd~DYl^E>lb8b`;jRwtXm$IB9O?yrG!?JrE>UZ@+0uTAlAPsijJcmH2GzLuAuH2ustYS@xZC+P(iAiWmP}F1gSM< z0F#(njZPkwqHW9JT-zH+JAXJin$%MQhsPP7x??{YjXrcZ-Jzy*IehR5I#)j*o+>(% zU!Dhu7%D>$bON87T+i^uNnNC7gw%u7s5(~w0Y=T3)~iBj0Q+!3IUbsi5uO;6K~tWT z%Pbw5gF#I*6dG2)W;*WAb#x0#)Sr%+U4V*Y!1OqBxteQ1^TFIe%$)%IMtG>qgHz$J zTHvt212eR-_yK?hryW6ELGh+OS>Z;zes#|j{BQ*Zcw{H+ra&Cf|z5<@qka-63ert(Q4olfGch(ktcTynf;CoP*`~Asx2Wm9;w_T6KMOiNR z{@(^KTb6f4M|OQxR~&a^`^(r~<9BDcUf6rB&D`_dku8f`a9-?cZSVebLOt zdeL8TTX<_L`INx%2+EULnoM`!_b>0~CwJXG=lAXOk@9m|kv*>o+Z%*@S7dN@$`viF zIOu{w3=p6U2I6W?IR4(_d3c`I6^{P^`dyq-iFoNGh(|m#)Oum_4CK@(T4R53UqRem z#_=l-HrnJ|@w_@1w#5Q6*KsRCO==BsJ-=@yrMbC`$qZ?*_Se0s+exzUBL; z$VqA2wk;c3!*UE$_XW*&rLC|z>?J@n8T7R$huO#9`<>pG94{x# z^Zew5aGu(B{ka!2%Mvi$@`cQ~Ad)GPlZ#(&J-)xV?@#C=LERqU-ITkVm*(PHJRBS_ zs=}7&)fHXTr7<6QalL>$Q?&UuivH8SyXsZ47gkjDVjOjgkn$nO^pW_q)NpN^j0h!Th}-92BwrW#qFPRJ+~XeJQmg+x!d-V891)c?b~>^ z!)3%;avQH5<4_e0lO5WeO>-Y7aAA9a9lsrK6G?M1ccMaBb1Kfm z#`z}i4|zM5>7z?--Q(c3EpSryfBLYXJ5-c30~QJ29ktoN+kf`6+D{FXKHK(ng@*;* z-sAIVzx>Z<%p?l8mr8&smNLN*X2T#v?bu!oJVEFSlwOK z=DxX;a@*R)z1@WZis87P&*Id3isDt^Z~1j=Ysh$R=#y|V0hIL&Nh84HjPGB2FRyzx zN#V4&;(ea>zQ*&oy~Xb-+DDkLaNSz&Hf77GxM!1l5)_KAIDW(ScZ}_??Dw;~t;ZW5 zbMsI2J@L*YYHnwFIPTt#@01cGdz{E(P!6m$!uHI*>LFMu~HGYs+6oqT!kp3mkrwVRsR_fE;(kp3qC# zd?z{F0#)WYUg*5!%$!A%d1i-+wu;<96pFGKu3oyI3`4?pM+M&g?DB9szctR^G+Y<9 z!tNAlc^L1xIOJ)bB~Za)+PB;yK-J`YW#5;O{@7c73hC5iZ-vIF3eE zmV3o!1~^t2WA$tK*9Wvew;syvZ|xh|2vpl$dxI|=Y&H~GXx{KTX3z4^2_8FkzJ-v>=On{BFxsl6i zYEC8YKYKYlP7jWTadkfwS=##X5sM|P%CdRXMf?9onkP^khdB%0>X# zG9^eKEp)_EcXvC*dhE0%K9<&i?io?>^!D6*Y{?o% zk_B&65#={@;kRMS_V;$g*Mdt@HUkWg{{V3Wfhq&S z>-N(IZ*95gy|Lu&=VqI+JEmCqZVFbAZ)mMe^&BXv19LI)IOSsRp2_S^YmS25CWo4n ziu(R4;3(Sh#I_U(=B`J~D_t=BZwhX`weI^J(MxAxJh%KE`E+1f!DQX&hdXM9t4fVV zIXm9Q6wxu2s9?N6y?3G_hZ^GalOdp zEH9$Dw~d_1E@QwW-Hn!v{Z~kKcwOq)X+dbX@6{SOT@JDs`oDzy$85hYV;n z%B`Jq8IgdkuTPaS!2oova{f3zpwxM5o|#~l0)Y8xO$S0S)zFI5@W_f`RShUW1b7ps z6c(ufZvOxW0N1F;nxp}u>t2`$!jaOeN>qR9Bds;SRLw3f=|qmk1k@Luz^v#Qo7J|HNXNosG{%B zr8IUQ2y-y0A}8luIe2; zG36w9fuZ?~8fB*5w5GML4~C#q-(|s&p(D5roL0-*X_j*_|FEb zT&M+3wZ`Inzv-=O_Fw`i+KPB}2TW4Ab;VQ~_;epE9JQ?nj#!}tQ(UwITrFy<#DG{+ z3dIn#WEIOQ{6>cWfG7#b)clCS-$0cVp{)gJQcWw} z7*c2%R-0*&>sB5(OwLXAP@Fj*^%&5%jWQTvs}MfgYQq~vL0VTzHG1WY{sOvuaDF54 z#(&HT{{Y7yolIuYf9x$!(1Bfr^1?G zzTCm01DV=#B;kq4Bk}{L|Q%aLe{x~G)0qI(0u2_#~Zz_W$SIpaqu1EJ^6=AlO zt9fdADub!`j1oW}A(`>PqLcC!!X#{QaLlMfoujig}LrD;#XxC}b2 z55r6p021`isQF_y05(PSrF5(o9S#O{%D?W#2am$K zd~rf(qNLW8_?iq*WCoQw<7$DjwXafXOz^^ifl>~6jHKgR3ZUptR3Pi&jHGpt3NYF>x$8szc=*>(;f%@oQ(UmSNQsH2?+WU2#NPVyoF6IO zd?ly4{fq5)E64G7aV6Io{{Wobiwkb!xNC#A>8+$_)=2kmEWiyZj_2-9;klEK?f%T{ ztG@p3o5dCaxA;`03QH~ef-t6c2o=4cfeY;A_zCD(koks2c z?vnoixOn!ne!%{faI!<{&m3ZX&u|tbl(98hAMVGA_j8}&?0fULIoP`c*dEdB&O`qI zCgu4~8{R%mPbFvD+@#Q2Lv%QcJK13R!y*GTUxC2INtv6xAwRDo$ks10J;AF z?jCOHwe9EkL$PhUE@Iuk*xWok1>}%GIg0vs5=$_Y#!r&8824A+p2BOMz>M}^aeGhw zq5FHu*2Y=E+Sh`38myQytuy63l?rD~>)M?&bLodhWrw3#&G{0fu{QF znZ*1zY6z{2@~iSZ*^%ev?&SpTj58=H6BavwCb?AN9v_ENl1{GgEX+$gjO3ep<|r#g z#GK!IT}OAvSfa`FWYMoC+zQ3%k24BaA+O68A6sm(a9lz+^nMi~jcsDKj+-ZYV0N^$ z(z%>*{kL&_2X6LUTcon%;8Dk1bAr~7cP->#?i4`5H+gI0(#mK!n;0){8symbM;Z1(P!hh$RQrIjU)&+Dxe;t65eW1^v4BNWy>r`z_89i_FLbfB8)3s3?G8RCC){{Xv>et6GqIGfQu&9?!< z+(q3U<#%+@HtUNH2akxddWZ5C6+|a9i*6g*PiXs#w7VMC{M_GriQWCx%<_G~FA-?q zU0%s(Bo;zS5BdtEcmQ+9I6Dipcy9OZpK#!Q-uCmArh3paG}ykyO7$@9I*+)Bm8sYEl!#oJ2q8W@5t2*(BOclO!AUwh5ne5Z2R zP2OJm`Af_P2&u_|_e>B=PtE0K9zTf7j(Zla@rDSgmE4 z0&OiIR%q%yRR*K~0HMU4-`%`VZg)MpahLwq@VD>7aV^C)WSI9%#C<@9hxLohcWJl+ zaU7oQ@|K_6{{Z(D#aPE{!25m0tam=wdt+}94HcQPg0}vw1P$#j=O(!2``g@oi!H&p zhqri}Yjw5l`vRbgej|Xe3+WLUrs?MJmTX99!_6fJc1$=iM&xa=g{Gu;mwxzLC8vdk!Mm7D7sj z0Fkck+G6Y8FYf2QkGDOUXBfMikK`|{WbU3$G5X1BynU&01G>>xH(yj&%oO6&*#7|S z$G69~p4mq1obNNn*)_j+_r1JiAUv-mg{4^BR;pN5wfJL->K>I%JTxspwB$R{t|j{xJX-r#VGQafCibaI=7K4QdDhP2ZW zO)o0Phn#M8l21)70CEIqhoSMqTX9yAUR}7{ZXP<;HX14M_~3tB<{HA(hH(|FKC3Yq z`auSHcd{zwYGEwJ`pCwGfzu0d%iJY}x&b7sBNSpOT2%Gxg|Oh^i;}z~ ztd|N<`GBFL58;K`1i@XAm~Af@1Nli&PrvQPOsLle2TNqXJUD|Os@*Ia*54gEFRjU21` zGK%Crf_R9j%A}n9G4y5H!HE<$G4ww>#Rv1!-eN5p%E{{-WAt$hjRcduQ3*@y~Xp z;p|^#-%mkn7rEV$#qpePFD_o2Ny+iH zsx2;oQ@e5)+M={I!rgXnf86mB+)7g8fw@_}p|?p$(&LJjrD}Y>N~jbyrXudoYde#O zmiv?8Bw0IWv^%QTkM66x29Ijo{{T#uNYUImQlL0G<&NI=*SqHS#^< zt=#_Kb2Ho_k{f;>nd2^u@&~9fgKne)=1eMfpJ_qby{Th3Tdwc#+lcLXF5rcH%sYR! z*0aQdl}m|$3@c4W6z^$VMtw=hDz7PRP#&1w@zbxs;=M!D?9V)ZkKvE^VvAL$T}iLX zn4vZM1{gCQwbNgY0aN4@+*r}7bfy6!k*M2JYDzbnD3OssvU=c>Hl{CnFMh4Bu90N@#zv`wuel;C2 z8-=FhMq&V92T}${6cuAijK4R^m>yCas**A~(208E5mrAb?7O*&N@lnDZaUEM=ZFKX>dppMdM|!ju#eYAITe5;0vf zrh0tII4k!PkR)rKH2d+lrE5>&o;rRwO)3f82(1C?N5iHvYLob9rU`OYNb<3#=IM~e zc^}!B2LJ#7>y`(|{{Rdc<(MCDh5*+*DH5T26SNahS0S0fY6T1B^5h9Qlb$@MKb}V{ z0rAi0)Aj&0>U;-EV|XrUN}iu1U2uWP6f_`Gl<|<(;a1Y#UNKB&Gi)zK1r(0B$m#gh0}qlxjSu0decNT@l}M& z+@2&JVgyI}!862ND0yJgMDkJoZ7P9ryX2CW?P5^<8uq38Soi8B|yP6TN;vJJJi|w}qKU_eHkPp87#Ug!kIf z8o9Y`2*dYIh%ZFJXm+oOa?0n^-=C=YI4=FMG61CjPJv+AqV9(y$bBP_r~`gAZV5D$ z3gUAG^6~pt#zMbxLOMc~L|U!%4X$<>TSza(U=YU1i^nG_5g$Gc6c7ooCe=7fNqmge8bn{GFEQqyL zgTi>m`}Vk^`b1`6U1G~Khy$DF4~~MWWec{_g@M^F(&DhiC^ntEPn>rDaHjTH^RQK- zcB`)3oHoTQl-{A@n=g?IPbY7gELMEnB?33X5cBcv@jE-hmLCEg?8V2LPBIqBYvPe) z^C4Y&q4PWJrhgExJ%dv|{i&(IA>uMj?XE{t^d5nZ?0A}nB3kQJUx+8q#VVnH zKkfP!)y}wX*Y)m)sHhW+{`i^dSn1NW^x-`esfVE)M*W?*FH`zGYga|JeqRM$o+n1! z6!WlUOasU{Jy<(2J8uy2(-MfdNJRi?U zD})U{P4()+9Js87{}f~X!{UJJDp((e$%X-BYkI1oWxLVeva_?E7?~wE25uJb+4FHu zZQ@6ewKx}Wr$z0OFEzBS(r8g@uU6r6`6%b(e>eq) z2PhSc6AC$d($(BqrJ_EG_8A78rG4IcgzuH;YnUUlq=wZdxqL=~y)m)t^^ShN5WjWn z3*Ar^VEEx*Mj-(E6K`nBFM51@P}R%QMk5l4GvutE3~-M+qLzWn)33@0NXF~dJnoGHKgrF^DkRl53ow{t)2UfhfNUZV+WGTOQofE^N>9X zjI0hRzrIoC1J}LNEdKK7a{&5DOY?ZgCq!Yvx!uX?_jXV{CIN|LL$0i08J8fT_|N}v zs8L!m`@bu(Mq!2JHwD3ap?0-;WFC)FMk-3&ji_9;rh@wf?$<#>vzHdE({R=luL)bo zdWJ)bz9a4)n zugxg)oBnNoccD1(Lfd?X=XpEVG5?|@>(sFG%Eul=qSAk7lx!Q!+8Dm}P#x?}QN>T# zoAb{%dzX@S3+h+pcmnY|8+vaN@v-sa(t2Mp_kgfnxW=$`a9<0Z$@m%yXeu6~z^P~o zv%Lh4>AlLN$5UKfVDav~0KjrP+5Q6YzZuLwtb+`;CM5$T=j~wvv2QJV~ySNtj(vhnOdP&#eL~y zHaKY4+G%(LqRyO8#fHjzBVd#StY7=auqt|lP`_9Vi%U9r|1q_AN6+^^wBc8-wCk_@ zdo4RmhJVd(>5YUqX=W8IXNS-Vs9B+Yntg?@@Z!MKYkAOQhh;a)uGw)fM-{S=k~=l| zGEnXzA91v=09s>PNdJekSG)tvs`qdj-+ZNR8(=cTwrL)0o#*|nrvA`%1Tv3Ss`&Me z_!~Calhs7eIy+rss^?1<8~=IKs>R%spQAaM?930NOXbwlyYHIE9sokB`E4p4GFR2} zz_Yu)+u00pyn8E=K|zkVt{{M_WIBu7TK-a2Ob^y2wKxFM&Y<`-Lr z4e3zLF2(P@AKX6nAba(1nXed&H$Np= zyS<$ZW3O!9X2vJ7XStRQ&uy&R1SrN#>iF5+Yfmi4Y2sc+*M2C}T@HJXzDvt1DR1{6 z6+2L9Bc*5=%RudD<{TTar#R2#OzC$o5R4JYz#<%6#a$0U&B;4<7K=8YQT(UdKaWOh z^KT0EAm=7F0RYX70$4n+yyaU`YVgYNHZ@YxIX#Nj_+>R8=kfEn8}bX%Ux10!+5MtH z0dx7kDWdAa&67`U{Bs5kOf(-IXq3ehv$@7!(GUrDI_vnE4S#tGE)%v)SY$B-;uu|4 z^XL}ja2E)ES+NcSfNOWxNT0C1R*U@8SbFP(Jr2reDDe`rND9tnJod!;wEW@G8$hqY zOChoPwXj>0BwyBlIQO*UGn?5uG;mv;flE4*3wos1g7&-YU-jGEk#H;9#qv*oV<=_q zF44veY1t$nxMQckN{oqkQo7JJ**WxFFXE~o*OJEfRKAH|D?*9=&0nEx%-!+LivIBF z5FgRsI9K!)BrlgRG z`568Fq+fKWabMuElp1Lq=TMbAlu^m9vQ7>H)5sJr$s!Enx}+?7TD-vJNsiCK^VM++ zPgEy1jZTg~*#83UsJc%J%I2m0iN2D<5!mp^z@8RyD|dtz@h)cZePq}b8?RRId>!|^ z7IvcugN{|}LUaq4R7<7DLXwg(;opYpLXDLh}p*bl*j&8w;D>W;#2J04H zIqFo);2f&Grj^%d>)Z>RyfGD~E_Ng#5{{)?r%mF_om4+7^QipJoFyUu zuHP5q9+^sVTaqIF{4wzber*0C)oyH;q`pqQodF3pZ60!%8f6mq#fU_nuP#<^-wQOLFdNF_r;bUGQIda#(Ab{GxLczX(;wkzC{{mBYpvV-CzscB_vKex$u!64e@4#tqh6;k6*9Jf)`)wjtwgWqd7 zaIqam#dxghBAAB#5WiMYS*5{jF(f)C)JZp^HEQg=Jl^H#@kv$c2S*WQt!ibN`+^4s z=^6~l9pFGH`-4eI=%A6Wbe+|~2iGVZX0sTaxr_>bYfauxQjcBJG~N;xs=R*bql^3e zxp<;m-PjB6?Sj^t3VveaEsvBROE2896-B~tr7Oc*rgyIKP-9h{v8203HFS`i%;b%u zr#3!84e8KBm~Z&E6eFA|v#^NGRcH$>-su8itcVt*63hH#EAb^xjf*{iVTU_;j5|>= zfu5{Em3iZdk4mGf68LdSY48T~MIiomKd0Y-T{08ae+s&BaOeF@dg6i>BGGOsaFC%tigWu*0W!d-*lgDKi8o{`D(eQgkX zuq@$v)O(s*siUZ<#xh!w;y1ItDxy1-TY{=I@@6+-5*?h^4AESs6VO1ryy^E9I?P(1 zDk7#NUU3HeNZP-s8K}yM4T@Y;mYckyXkC!18R0j0Gv;J?Rf6pj%}0u2TbTUW<{t?C zwX6SYVPJFiRCNrjhSW&F+sb6A4dy-hrH4Kw;Y!m^u}V%dqmb)i5NmSZN3Al|H?GCF z6%?K-t+Y0uvu+2^OV*U3gW55}=&o!7r+TGFY97#E3`CogH6 zF2=5qQUpukY`xWedQizMX%YJ52El5G9FQ~7mfe_Gs+Utg2UzlIMAyA%Kwq`oPaleX!T3F3h2kxg|8O2IdceCC?x%91 zGiY_V9{}W6e2CoYX68yYFlWZ|AP($$sH&8yLSuQUc%k>~o&Rup>%$egijk8xG@VKI zT+oFv9iLHuviB|cAG{d&f-G65i$z1A3gcWId*7a;8#S*}N`d7y8jA&Nm6bPM&I6vKiJQK^>KcLx=DeYy5$qGTMxlvVf^CEH^+GsX z2&JSyPAHjM`^vHSkzeRc+iQ-8j%n{x6ioiZnGh*>HeZ^oJYXNB-g#sQR!Ak!%oJM+J0t8AC*}HPTkJoLlnslEP0E?2wK?3a}B37?H zDXt!17UJX3S^>5MyEitUyWTf%fu!2K7mjW=@LF$fqO_fpUKmx^92-z6O*_e`DeIx5gw8LV>JDzu8G|h$ktV$7H#4c=nnk|Z z^K6Dab~J8#%0pq?Mj<#kUTm-YO)2{VVn6t+SB?(hrS$ zzqlDdw=o6cV_DAac3_sqrgA}JYXUh2${yYm6NdmZ?QcHwVBAi5;Hxx%{*v1*1huX5 zheG(ggN0`He<6iK+6WhQ{Eod_+sT?>rM%_j>Yy`%Wcxorzd%DO&JCr>P1YgUo#B%~ z&5^hH-eQ>G)o-((4NkTL`+8JiwHa@_qZjRPRr}f5D_Z>|{Rw+}Ytf;<&@^+tZnPF5hUA zSU~{$i;KJ2j@*mvRM5=Ufh>0}+O*(9aEzv<5u$XGp2eae zyzx5x9+ocafx>=PYKK60mkRIk7yIB{ZRYu|O!4D(ih~rZ2_r0lR;7)~Q6a()pwe4{ z6#B$Ea7Gl?yP;Kt=-u4M%{!kD3~QDeoIUV(C;2RnAaO%gDIfbL2Efr?XMR1b!%hdG z^?qH{f0cK4ZpM6_>7NZ{^+e~t&eR@|om%|SSPE4e@wcDHIIJqq0OqZ0uA;?L>b|#6Ut1;NCqs zNi~1VwAV12&p||m3#^~%J6VS#+s<4~8lFA|!P6<&LK=4!1zYS7b8LEqg~{Elkua`|ds`gcAFp~uWE zLk_oykjGSjq8lqRw|iQc40_-EJ`uRaS}|?+;zOXKm~- zy3-bHew=cc5trsyfp0h)daFPV>Q!&JCN&Wzs40z%~OfBtCOT-DYzXfIWV#mcn z#d{mf$g_t;;kz?`fJX%0hJUwWyRfc2cX|7l7VlmD?`Aj*puk?FPdFI$s{qgkJ!_ZM z#*UF63lX-mZKKO^2wSuGGymfh9X>T)VQn3?y=b=s_Zu=<1q0iaTA9}ee1@P=STfeI zH*;X<;m_x8uL0d?>{>`K(oDwg)02<*4G@_HToJ0VuK;EFp#?8zCT6U~!M}B?hw1Hi zFOAiwe*x@u)@{9OygZa%8f)SEGndUN1uhrR-qdaLuY)tOHZP@88X;3i6gWm_dq?3|QmVsRvEk^IllrelQ zMw1oq_S7QsmdJrhBmND6E1qkges#E&r)p1`Iom5h$0`p>{}iusr3C#-j#ceQ;^Zl~M4mVNn!M$Gsnfz)ten#2UeZ`7J;+43KbwHeoJ zYl@da*2q$~adm5}1vDst8gBD0wW678Fne(A;!8PQ`WPK^LU@BHC|rZ<+p*4|iV`xY zV%Ytpb7o@jSZ7VMR9V+Da>{_|0D+193g-R!B;LCyvGhkGk2FzPCB7n6yWm^_aow%5 z>HADVWs7y`(iJkur6PGSFyjZO7bvRD@iFnw|7@r{R36>6I17`sy^#m%vm zZ;4SsbJ|GN;0&5sv9{>3p!y*yPq{7z$5)aN!BLl>4n{8S$}FxrIV1!f%otiX+!x%? zdiW;m>b^>8$V6d78QC>OtlK;~8fRW4A?Am6|J)@fG(xY`1v784yk&dl)JgW^t!zA} zU6|T%iPosNCQn)RxSzhz8+N?8=x9Dxo{-wVupw6EFKQrIykT(V*p6&QIZ& zgt(z<>z(|2*C|(bkqAF|%IA8C%pGETpUTq&C4O)*$>A71|6V72t1M{X61C?ToGo8W ziS!c64GO=QB}eV~I^43g4hEm~*jf-FDUyTF2d?i{7+L!SV^}f|lrJzE z71Vc4d3DfM#u~ztEEKVIl#}$?1BtFcYMbqs1<{7*{w~(uymbRnUjP@dzyS_3<8~!! zvM-MSb`V!toG?m9qwS=hu5)QS5tiIBBs6431|L{b1*{9cpbt|7N^VpdHR1h*{hqG6 zEVOHl{4I0o8mjVP;>4a3rOKYZr4@z{8X~2jItO9$E9;DWcFMf<(QjWPrf{U$F9s(1 zNay=}8P}66W0uy{s)iKZmA4-z1@PQ?-v3A0I-tx)s&-Yhg#T8t@}pwIb`$OFu6hQ8 zN${baI|-8oX^)VJ1_gn8Afxk}`lD<%&!mJU3t|G&IBTZZg%=q~$w$fY`jig79p5uc z&B1OCeTT~w>QBlv)$Jm*gnl3IF{WL~~&I2q6HA#|fWoxQ0GxNX9;of$rgTgm`yUJ4v>XDp(rRHz#x$y8 z%3IdovsnmQJiD%jmQv|5nYu_}oGfYO0Ga~mbFUxQ!!<|sykp8(4nI>%#dn1ax~_SWeH}BMeYWa>qvIUYb+l1 z+o$(%hbf`x?1<2ZJ5xc@1y0mq4B?xJtlAgO8|ysO(&srqnoYd=Y`ZkbJs*uo2j1)c z#kcz6LNzB2{Vvm*td9BXXJQvA2Y^kJj0pYck#RUZdCOf)?5Zf~|Sy(+l=Z{CF*g(^tfiA-}gYdda^xz7{2&L7gG_+e9 zBFqfkL~OQTiv0p_7{PfuZfRfk!BB1!Txvbvt-a3%3a!gRdLgw=9;NyDYfI(RsVIf^ z#-FCIU+CIJ=frhP|FHaM5_mB*0AO*lioEqA4W}Xdl~81(Dxp;SwRwouRR`zKKq`k9 zm+a*ieTmS~P3xXbPLEDX-W`1MX!;56^fU=BrE-^lIKpTql?p_cY$l}uk0#4UzQEhx zE|jvg_(n2Pavj^4=gg^()-mJ2_kSLm10UVS=)VutUed}o&pt`4{a%D*{9RTYT2!E( z2V0VvT5u?S=8>tn2WC`Xn`1X25K-L^v)scSsq)eafN8`we(eOMEr{miP}iZDVHESf z+oCAT+2WkfntFa9vJSwg_+MfQ^%;3n=dTqrXPF>ImyNWKT5KsnVCdfxR`PZ!4QSH@ zf{1fm_iLutCnrZ5!s(>BB58=&%*LwP1dvL;0{{#2Z=L=~<-_r3skXNH`)G@0?Ay+h zVgWcTOQ~!~kU0@7sy@r5_2ehS$Gz7ryT$0k{_Egm7NGB?xMjd4v>3j1TOkz)M>@Ht zU#?`l6r%XrzW3R^nz1l|b?JWis&*J-iXB#JcuaiOna%}*8eymlJbFh|Sa9tz0A|QW z3Q_3C&Cbg4o>7R*Lk&TpShCE```lDiUlaMutCG&Wia=L-rHPro+GY>)M61M52@teh zAP}Z}23!$`eFQX7Jpi_D1q%D-t!oqU?R5Iy&k$Ge2Qc!rY;5X449|#y0quqD-4BQj zVz8*}TA%MTRaglHbRI5{DynI;*u6(a&6vZ@^_&gM>2ha-kSH}hjD~;R|GuZRgf{od z!J1dX$DIWB6k-cXa=v`PXJLfHdwr}1A+mJ?65>| zE*B!6G1OLvNfT^lA71D2mAZd{zGnb_zc7i$Hf9NyAQGE)YcQ6I>1lZG)3*s0S(`J% zKN~JJ8171atjQZe&@e5=$nc@oJ>&EE0-d^Sk++W_EOf_ujJ^!!mE|!6La9Q%DqtIu zH2*8un?9b^-Z6;y=P&l8fMcrw_-TK%6Zz*u?K)zGKu1dTEEGG&;gpiES7|tB^gmcY zc*Tf@iaRFA*JwbIZr0-|+1Kim0)JkBo?+b!ER3PSGsbk7`*8j9+z-QHfiV^YM`U0bcAfZ!yP6Z_uy!@ zUdr_e;A7hgrf=!~V>g05{jNk>ooV&zDWZI!VZyl?b~?l7VMX-?kfhdE>rn1%zH}{W z0-u^sk?kdpbzex+5e&p-jAOq5ZU_V-ZW2zh;wT0TUgV4m&rsjo{6>FMPcsM1*yJZ0XyggOh+ zyM{S9S$5l3Km=R|w1G;C#+g3@`Ty*iX>XZb+qNqJ!pAqT5?4bGUr<#in32&h)W&WT zRrtyuvfthqW$e=S*@(bE%z4XMWs%R+MNHb6L>iF!7<%#8?I_d0n{u~K!FJ5_1tLs_iOlf^}QI0NRofv(3-$U-3xx!NYAzw>8nU23y0NTzd}sTzL)>G2CC`{A3x*k zpler*k7<%D4}iT9*^Uvfz0D%s{dDT<)E@W<*JC~>b@bS4H7TWiPER&u&6N)* z%fS?AIQ0_V%Q5pZnxSRiZ`j$O>fNCRE@(3NjGA)~vN$l~rQ>HTu{TNWNVQ3aQKx{G zLturz7>V&iD8bJyof{AaCud;lm9{R0gzqoqh2Hj&@@7kASK=of?c=f3bIk%vu*=LW z(50jMp>fvE7RxYp4X^+uuNZAqKSxWYi6`)-FD5-pkr+d7MqFW2%pe_@Cc+2IIModd z2bHi$N!jTQUR*_s^sy>?ki4ONBaBNkth^BErBvdw3Xg@hy|cF{E?8&aXpWgtmzfQ` zvMnr+`U4v&519S!8Bnre7@&X+VPv6JVh@x+$V;Rb7#B_dwe^Q>@w4TGdeR7>aprPjJwc-{3 z%wqN4uGuX2-(tsvJ?;>?voF?_Dir^jt8en|`t{^Xn2RFlztD$Y$FGlOcp8tg};V7GLY2z&`Gnfpl{%N!oUXAHM*a&NY5 z1EofW2g(x4$2BTntthrs!9O-`Wonznr<^gu^WtY3N%wdBFUEyt-a3Z|M=mnQGpk&@ z{*Y=_V&q9OcD%GwrzF9Im^$zbTTeDUe)~x(av{Dg`jQ<&$^im~{Ns}tIVwNAM%=XnfV z9;oNdmKQUcGyBHR2RifG~i$tkSm6>dGbUANjh-hT^xWm&=m4 zve*W?*S?R$F#1g5tR7W!-w$$_x=gpWzOt+7i|gABS=%G?8wXOcLg@l!lIvPLa?Uj; z0+c*JDqV>qfr_uN7*DipE1lx(dw&WBL({j-@FDWz@0m2AI7BMsmS}e3?zBI?mUO!n zdpAcRT_0CH1ob>r@<~fr(q2E+eNye6S`;R}^rK<^Yl`?zS9{?&W0I?yEj95vE{#h~ zrIxS=_HxH(&f+jhcJ1=E(5#V_pP1d3(lwe@TY5xOtFl*4a5J~&W}9K|ahFjEsq6WG z@Y>2VT)e~Ap{Uj{67KL01)Q3?SASld#8uG55%kwE2Gu7J6}jL)xR|O^87cbXeoJ6; zBLZ+4VNpP4g*BCL?{}HMbGiT5ADbt`Y{UFB*ZQe$aV&FDqEkBUWqM#um8~fN&+8EZ zKeTzKW1RrE%HaDmf8!<-Vh&R5*#CgxjcRbMfT?LN1V0UXh1WrKIlx9(f_FhgYPgTB z(F?6mi%(mqJ4~xp=9#=atauUr5kLCiSrQH{cf(-JBXDhiaH&NZ&HW7&>#L}LVFJDl zi*O^aj557spCbAj{SPOVuD}oaU|CC2bF?60h~PdZeDc~Am$3Y#vcvjmHEo@nXt_&} zX6S)_1grrUCu2EpQW&z+At{yFpT-kJJ1P9=XA7>r0QNJis9mlQW3<4IZm@v*kJ$G$3Xtb>EFcln6*ylKER`vSN z2=m=P9LK8j0eDG*z{2@?f2bspny%4P{VuJ^(*cgB`y8%imEh|^Cai2M=&tkbTsv61 zpXNoQbXh&-Lcgntv81E0T7}%FQqR;rF1cZkG`i!q-qA0rs4dCn*EA1!pEWW!!rtLySf- zgE4VQ^Z6OJ_(p2H*;8o=Axv&n@j;KO4r5tb;4L!BZjzKlJBkwOssg#QL{k57Dq9># z=XNFW-m1Wp-aXGSau?ctDPf|x6Z`t%&G|{Rbqs+=wgw^I(hp7SifR6`)N!fx7z>nQ z5x17WDJa;ZvR*)rNKUFGLMjD1(vQ97`&5bvB4r2+Q?W8Vshay6=2lFYKdmZHjj;wf z&3?DV5VOJQco~;*m)|)AicPt*{`xNIxs~PpX?3W7rz~s13#+vcs2sQ)l9)WSjmPE? zK07{1S21bzpK$LH(slV${Hxcz(&cOvME)f5tRB@w zG#Z!7alLPiJgE}+jrFjpg?9#TN-Oc0_J}K^8f5B(TFmBKj&7V&3|~_ue7>3#?O1PN zT(LbTBPD-$yHKepRCY`Ln1-&pOi8GYxcbNanweLUPnD5cHCF{OLk-M5V7l+)>1E*^ zakYw~FNaAB2M*3TR9~XvMaY&>CWV)&iR1zeN5-z{fxL*(}zN!##~3HO@vfTFOr!IWE1!4>M`wLK3N z38(LhZ4d3;LKn}jl>n5!?;~YF`KyXp$AF0Otui(DwSGpFh7jyYi*PA*Af&5g_0S1D ze4opQjX|BXV@evj*f+>1pPgivKqF?hL2tV1!3rkx3Leh2V^3;ZsE7D7=-&QcC-ot` z8kUV_RC$j{Sr3kw#~R{bLxCrJVOHZ)wgADD&K+0tY$U7a1hmtF#V41>cGcRl+~{Yv zxlQ8iqApsEHF`5024DaK02e zP|`GhMP;~8q&6~e`^6<%sE)1Azb6ZeoupV7Ic?#V8n75 zA^zj~AfP|ST>1^;(_&G%udi~!emNMGP8^_e|y-5HyTK~jg}L#h6~TYz_xby zLHyeWgpgNUwp`vMMW%yxt9cOqZ$i~MXFbY|e7Ua*?e3KyKgU6*{TcuJd?FeyB{MRt zJr0cYe+_)9-KwXw_F_at$AdB%{=%6|A*3=Lrt=^4+u>p0-l~2;4eb#0FH8 z^2Ij2@+VaON`Lp{&c4~`2}Cp}p)Lowpnr0(t-f>dY?sRc zwKpra7obp|53{hayA*;IC7_i&ac`Th4o}g-(r(7dVTQK!M9XjqbhE8(!Riu@5s624 zvmQ|^pvB(X*oOLGUJPz~aR+m;;_^fg&;C2ujnxe(dzbA`0;xiFB*?O2fUi zBhlcN)zhnt-u=Q%XdY8+ssk8Qe5@?nqM$h{e^#EjEL7+FU|z*Mu=>MI_)1AgRr^sD z%U^ZhJ1I2&Af*98R$|VocmbvIuE>_4k8qEUOobwh9?f;8^<)&t*Y~iy_H`B!xkhPJ zq2Q3F8+%sLnbW+)(W0J9Hab>ZJ4E#+Sx?s*^9Heh<{wyuKmq~3u-jHCI6YFuAM>*W zOm}4i2`Cr&Nb5^t`D~+xwv#UX$8w<`YUsZ1Vp;hzG8n!o%j$;Q?11@v4`^HN8400* z23xp~;$~VR_WYkSu_R`G1Sr-*z~O6tNOpA5Mvi(8n!Vdk6dNt|!h|C9I=tB3f^T@f zcOY$QH@=+W`h36C6abgB+*hE^V!Jz|p!_YQ}u(Y__R)i)<;z%ZT3E-)ydvr%_o1m6?W_AIRWK zSlCVf_4t6gTM0z-oB^(UVI}Xc=3#KOEHE&aURZx*4>>Im@TXCjsNdvWws7Im4Awm%VQJQxug!gCdgn2}n2VR6dYeT-8R zE8*a^wyQJ@STuOfP(eVNDR$A6o^>0U_Wn`{GCm0w8E$&wHe&7jOA+`4lBD7H$V~7X z|CpO;vE0KtVutrHvj?2-+_EeDO$-nO;MLUh!Ks@ou`E6Z zKGD0am2(h~B2DFf*kPTFxg-$joOC?7p4DOa@|bekYAIX(@vpH^0_M3D4=m3IRDmQC zmw3P%<`5sCN_Ti^i1Nag z@b;HA6+|X|j+jTQCD&@oJD{clsw`tCCcpOMS^g;}c$Y&3-#8UYOs z8ZMN5>NTpW(RqPw-Pn(DCCT-tRCY|wcQp2$JxIl&nPD5k6@9LtH_qn&a2i3P^}Pmu zU5D-UE0@0x{T-gq%Mj7C{lyN(?WV(5*_MXzAFVnNwfFWlrvNQcXgAbfir+C+W2JcN zsBHzqw8k_{@B(Dr|2kLVU(;Idr*1c(x_Yn*Bm2J{ExuyiN~8A5qEDd@Y=iK2 zncR2&m$~9lj#|cY;lKa+i;B%}7M+E<7VI=dZbB^;cbY!{8ePf2iF{?H3lu@K0VbF9 z$o-vHZrYBxKQcb(rIWreB^d!EopB?%VrHO=3W?G4GAd*GU=IhekQKsC@Tpj6VNx1RYmlTC^3z^0zb`+1ea` zy%h~Qni5b4XKlBxRNlGif*XL`i?J^HzvUutLawHnAw;lxgdSYfA+997h&=;=5&9jt z9yVmxkaMt-yXE^T;{%ZlHx5PzHbY%PN@4MvA z=-iJEIhJ!dGoSUrkwY}JCNk>1yAs;1@j2TY>Dy)MmT8%MjK2FTE39a`x0GVI)l( z!n1xqR@nH;Q|xu$hPdoI$i+-aGo%Wd-1%qa47=qK&^5a6i{Kypp@tuEN}#6kC`j%sBe$_VL^7q00yOk28UgpcC0s3!G^&zZKw z#~v|+g$X$nOG?*)EyUJ#&xfRLRhdZL(AcTd0%C<(jTYZ^(KR0bfGBzdNY%1%F2~JI z4*7KdaSyOx5uwb$6NRUKbv{H{?zN3gQfD13)w<48~kuc}2 zx^uH7Wl1fG5ZF!PpE}T)+Ahd)sh_Rn*Ea~M0H_)`^dU!8OA@6A(;iD6zi@2JlD-p& zEe;I_hd34{IFeq=T_#_Sz4I4}wtg#ltr(wacUxa-xkFms{98$cWraRvPh{jN zyWiOm^(h|CBdTjNH(Q@lTWp0_pqPZI$S@6oQAwo;nA)P~e@imj>g+B#c^KsmdGV$A z<4ZutA5y6c-n`_9pg`3EpBHp|+eQB4M!lXNViJC+$fyM4nBkKW5LqXqV{dj{8LFq- z&17|rn6^H0qRj#N=4+tn#OgWeOCWj7F z%9jYe)L#Z;4}y38^HPi1Tt1phY?bI1QC^UU_qLd=oS!`GbFx zD7OqvR9Tfn;2Od)S@L)ePoqfZ7Q|ahr*_H@ylf$k8Rvu`aYx^5L734&g`)&)1Fg!%6P88VMrr@}i*@jN&( zGKhB7qs)%3Zs)e>XoYr{JK#5tMbc#hs)UgcTccyC>Y2XFq(7CCG#|6Q;k`(EFrGVR$PmAJ}b4pU#sd6DK zX8Guk9lr9uqCy7yr!zWq0=K?wDE2pRci)aNy1Zm~5t@@d$%d)|1%_>6dzD^dYnT6U z{;;9#H<-WHVNhLtp^ODs%ShdRv!To(y`UkBcz|;0A6>z`)s$7FUw~`tR)}FxPufF& z-`^KNB5-$DTVZ@v0NR3YhLSmYw<~jAS4#$?#oAPh>L2UpsDYp`XYB8>I4UTuP)K&t*{Kj}-gsznSB@jgZz2I8Zt_ z3~St`itxAFbRD#ULaoQgtk*8H54L-Rli8Q8S?dTqQt!@`g+3aPB^-oWA#W%Jm7;yN zGX1!{8-L4Q`@|G87*@uGzeZL!z5nhTWx=s!FpzC8f!^)-Jl)HNfKTWak3=B{xDE$fC#QCqUvdremn$4C!QI%nCVZCTUdQH}XwV>5<7i7T0$)FS zdex50X%oa=jI^oI9r0s|wwqb149V=j0$}oyMVPXKs$EyF;&sFT$1^>{<4j{;Ui0yM z!u447*T9sj_E81EQJf6B1)^jrn99fYMeKhU$_Q!8%tuaS**jNVsq!?8W&tWJDWS)1^fG=Q0 zvc);$)=({5AR6lzQm&^M9P>-26u73=I%sP*Ui8ITU>!jcK-h9O4$&?!HE8f<(kiN+ zM3TU!iMT93{YM#NQkZ|JuQTFs?^dS(r43Jv)O{z`1HA=WO!N>^oaQWF^0EC72SND0 z8a@YwcWGcewb0u9-ONu_X&_pZEHY|Rn6&r1-79OJQ@1#A{cC~Zd3E%r?;iAK_3ct+ zf6Z*J5?K<2GVLD>a^2_MT%E@=+jmySMJ5@QYoe`EiCWuF0JnQ896RnxOvBO zCC%l@FL%sy*RuZr)t#N2U@efyP-B+%-`bv3_J6l_evIGTC%@7bw(h@iu27Ftu=^hP z3fl3oQR0dlVhr**nawuWwz#e{w4?Uh*(-Z{i|)tmGZR^IJDKX9TD6`zb8IkPkQR{>> zAX66}#{U3sEy;HO0JP7@$$8%W^LFw{-(K+d42NOv4(jF}le8oQ=-QUpyNFN}0f}FQ z5dQ#DmI4gX#Hl5abSP(zUXp>BBQc3?Czg4oGVhq&O43U6F+1cgy|Cvdzam=E0FF~nq$iL z{{XanFp}%JJI?8cscihaM_;9}ECWd_l#C~EcZJH+ndGDbg2#*sOItx)s*Sm;T(QJ^pWHpS z{{WqPjocSzA8$JbjCBu5@nlKB!q@xk|p`@i>3jpO?xv|itt;d>j3 znWB%GzO;>HwBtCQ9FA{^>9{*MV(fm(yV<(mfw77vkz^4)2KIJy zOu(oZhuyF4$K5ve)7vX~DPzT3!S!9y%i6`~JSSy#xT41G7^2)vn|ww&uWmoLehI&} zKkY}c;rh>WJ=^4*Pv3p!BW8*lPDh@7zP0`RHpj&`{ng1OoLhMQJDb1b zH(VXSs>>rP`LhITh^@KEyIcEI_N}bK@Lt?@rM0N{Bbm3D2aAyI7e@M)xs`u6tJbx| zwtmz0$0f&bD#lzdieOr!ely6Q8xWU+9 zv!=UqH9F~wUcvSYwU2%Kr_NH^`@XrT5#g{PZIQwDy`>>lOr6ePTtHas=l{C?7QhBs`Y6#|)^`D5yP+WU*``GS(%a$LtX^d&$W zWQ`8gJ|d?P{ZmcITgJ=;(-0GDA1T?M?+`vXG4R}MR_F;(WL?heb20_inAs)U0BHV@ z07Re)0u+7NkGi=kLCbfmy|-=KI{vZY(Y9UkQi1tZvk~~9Y=&!@HM z%xjSARmE+tUQj?RS$6Fqvr(mS?7#O3?cWW%hv2=WyKiy5yzbuIyNmw-)_!-|zT=Ma*~}!TuOu+- zw`Ns4P>gmDxIf&tzY%wj_bJ|Xx0d|(JKhq(!kBF=*hgu>@!h}1q&5~OJ<|xFjfR9% z5Z+nd*z$I=C#@aN9eD-yyTBwUtdrX_DaZl^Yf51lRidc?(v6VThMM?+fDo?S&zNpf z4?-)i#}q5pxf)mPz@-#YnpEZEgPw$q4@_33nRGrlt!hSpZn&=6iV#QfrWG zr{jRk`^f4vsXYn73R<-L{_Hm6K~i<4LHOb13PO#lgT$Za!R!9~3gkyq^3wvN&sv&h z22`NNhP2G(o(_Kz)TnS3%>1Zw=Kx6c{8BK_-M|585zY zb319axiIQK-;TA&=hNYX@HG9Ic;lhVFh@^@2?e#t7ABu849;H@`!f~HAG;Z4^8WyT z?9_q}k5Pu82Wi%{`00S;jjp5Q2n6MgwV|$rfLG)GSgF+KiVaSb`|wn$s;L5}TAI{i zGz6(-V#Twm>c3_-?afF9hh25S`Jdl{Le+wurl4j08g1)cJGF?##s*oQ=g4-u3dV4*rhTbJk2%0-L7@}ako1B zv05DbYf^l1r{ZuJ9R(@!H5jdbF+aZq)BJ0$CuD$iLRa>96ojU_|WV7C@Wgi zO>@%}6fKygdgyh<4!>dj*ovwPD~4+6u$+citVK+6F)Lq*pz(BPB+q4Rl(-MDF<9m3Vg1d3~3fDa5Qs@8{kR+KfRGr?{7FcbrzIh=To zwKV&&x8X|DJOH5XEl{7#5NoepX@J5rumDX#`$h-^3RKfD1vJwDyMauVc7hm;fd|LO z8(3}iDk^f*T+RkSx=ejYSS%4MFh5 z9+V{NXmaU*M1e(fS^`h}F`j@>d~=|{kdnJp&>X9~#2id?8X+K>oxtn%>x_@vj--*e z3T8*c0w!IK6q8(of9W_bJVhy{c%?;tBje+W{{XA_9DMLqsZYn_o(98QzU)x=pTqrv zNb$^gYk(IaT4J9JhuMRkULF|vS48VA)QT1tL6D=kN%sd zv|ZWQtL^W6`!W5o_D^Zu`+IwL^&DlVyZyn+M4ZzMOZ+rg$VqU@j0Ba|^pyg6a%TMkPw!6mPyE~%A?wR*pJaF5zQz{z6y05M~f4RAI zlY{Nf*5a1mhwUp%k6Vhi`b!wNLwRBjLVKvK;<%l^*3I+B7bH^p++^_=jeuolKr%^( zz)%z8g^E8=LvBb|z$}4Um3*=vD&f0^<)dC_mYuA)_*uZ7-rOK&x|Be}|yE&W3ChU45recW7>fO?N~%b1J`ZLSAx_RWQ-WOlMI zrzaD^gKsw?pimxC@;W?~8j8}mNBXnI+c`0{$&=G75pUKJ0jd80nBvE?yL=^w0T#qy z5yaQCCbHa0#;gg@6Nch#9HdqCFRm3U>oN?GFw;D#TuM|4SCCT)wrVh?D6ZXesl~T% zaZpQj$xZ~mm|xJhwL!h^XDj(t24tKkZJ~nu`fTkT&CEm}ue^z=I|9G^YQlqdBD`qK zmYi@!g5E#^Fj{Q4E;^Gzhnta(mu}nUwK`j-kik!YrX}x=@UxaK$L++D`u!$$h8}kP zPWwI{2dK%&{XnNov1xtCMF$Y;&fv3e zH#^)g3Q7GI#kNUzdoUvEYQ-Or5ZB)5e{&t-bHsKh^#jcJ4WAhcM}5oL*oxYAe`ax3 z;0PlKscjgEXsJ>>`QwP}t_zG`Zat6eUryVyJ+ra@0JXGB3v~KxTmE+54WfDWfXXBW zg^A4IvN351Rc2BEBTWbK8DN>?D%S;sLQ3yi(LF#9;fQWIHW3-vlvM$pqpd1OfydKW zNMW-j5Jwt>EXp>v{-(bif8_Jpy!?%VyxdF;x6qDsN~P4tI_=Js$1%zBHy_k*@2(l; zxtK9A)RX!rr9UoS80BuSS-AcSi%6v8ub@)b_cJ^CR@_${hM}5wqPK>)v4-!DkDT`w z`gj+B?2Bdk%Rb+lM*SO(S)-_#qsW9JfQp=kB<+q1lumcwUiF4K{lf2p%Pf7R#ZI8b z*}c6?k&OYJ*FiujN(=dmpI?y@CFSR=Uj9(*OoAzom?WQ-*k(q!SS=Ducx08fTf)RE zG?|^uGb3786L;6R$qC`Ot22GYaLsnGNy}WuO(nb~TWlz3N>Z)Rf<_;U_Fsg4Q@Hr+ zX{F^mj(asu*z8`}gD2!>k?RZ)M7x1`$t+au1mfe{uGH@<{{V4$b9WPWKe)HDxb4db z$r0RtL$^mU#b}3ejXg?aDUM5r{{ZNTadF%{vW_>tIquQ7fxW@yxMx+G`?ot+l5N9< zhUlM4YnTX^V&uS@;!ef(lZUnTE7^-XMdK|yGqVfYay-q7FQ(Vs=iEpB?~%K>B*`?A z$ZEust#NP5SwRigCK^fYuJ6<98s6kmGjvvJs}Kp>QK%SCd%wDVW0>Ll1y!ctdvfaW zpzl9&?$NzixZc^~!tgrQ3b8Ct-Uv+j6k%e{1_oj-8;nnLke1 zXY^Dx_57e!?q(*0v07pKtNX6^i2EDZyq&GLBWLX|Y;tQ4x;@)&u?Z%(y^oD8a~!Rr z#Um;ZPg-JIf9<=o+xsd0)-0^%9Pjr5-m*o(_x}KSanPpAyPn;?8A>W=Ol&x{4s% z7a74I6O2~a_ba&8J6X893yQ4v{3=UVs1@{xI^+KU9OBY$@s>~fj#s#O6C`(6T!C%! zxOY2<2BdVvP;!^?@O&&ZQ^j!*`nNY*6fX90Oq)TvxK3-dy{X~72mn~SlfJDczqVl) z{^T*-fg3T@g%s(5+w*r_orR2X`gZ(xYH=`d{9T|LMu`fkH0xfq#P|HKA#un}btB6u zxRK=C?#0=*xjNSyuHrJ99jX5Sw;%k*y)dHECX>{af`kQ#p~#vHvD(%g&Cq#yPF@Ik z3nU;kRzZQ(b#dfBhB*s%unWiVWBMlp#E&hX8D(Jz7V#<{%38bF_@4oXH!-42BrWMi z%1|2QskWow2g4q`oIfh@6<2v-vAA?l*>B#UgYc=>0DT7{%ravnQbkkZK^kR&7azl` z&s6}BW_R+?Rw^II5L|Lrma!Jx(OawvlE!IOG7K55;I@szm58(|{)>VSsI?TWDg}IS zexzA)g@Y>_>luT zEihVGP0gfiqJfD`zmzaur^gRz8w*sNIB4Xm#KfJym>SUIx4qEzSGqZl$@e?7F3sO< zcP9(TNpCv`W_yk{@v~X&W>#q4300Allpv`%;`qM#;(IH%``O3zU+y#eW$%7BkG1U2 zaif>hia&P!lDfH%WtHo(X5~1qQ94Nzvb6=jaQjQfOUCvmc-$UuhT=H3xWBcrYt;3P zvX%O4~qM=Uf46#$VmmB$i1&n&8{Ry}aeF89+pgf7H(-mKqCpid%J&BR42q zY<7$mNh(OllbFhs_~53yPSSdk1_3OKO6qC*@F?m}fX=yK0&C&`r{jvB1DB3i280Zb zd35o?rg>9oH59=$J~Y8<8XqrT4;*beFr!z%iW<`tY}C?>Y2~h2jBHOk3KHP!mX*`M zW7DlFH}1<*rBCq1Hz;AN4y5a^-HO-wSpB-^fJemLzSMkBF zV2V?wcvY|E)2TQksXxP95C&k18u%Pk=|%g_7!jpEAK6AHcZPTj$3TArOa;=q6P13< zWWuC$J!wpNYg*I{^(Lg?0oJ>ZRW!<@4ntRM2?nIo0OSToT4nnfkM$Bh{A$W|I@j&5 z-f=*{fl42qc-(11-+p+lK+KV^jd2=~0jN8xLGi9OWNE7g2A>R3i2{|&U3A3|wsfsY zp&tw@3iuL#h7Qx_P*{VNMQKIG(dNh+jHt(@4jBGjx_BLHim9RT*YLvl-isy-sQeYnhxXb+EuE7zd-{PE|` zrz&(csMi1%Wdx5f)KJ$f2*9eS9(vV{JVC;OLj#(UC@M1*!p4rk$(SQiLOP8;7!`8F z3LP{11_5^gwlIl~l?I?36p(<0MO^0J5VZaHDy-WxZ!pe=gCcXw8WIIfdS{sb02Reo zGC$ID`)~w|6#P$2A247#=6d3)4FxmljHm$DKfhdVs_hz|huMI45-M~8zbbg?h6DLc z2m>`IIvV1j(0oVRt|~PcVnGZ6{{V&w9tN1}_hPg)`C^Ce0)sz>0P!>*KMW8SjJs=H zGBx|M&Y1q&Ii5bwenQ8D;=7N!xmfJ&-s*c>Ya973E^Y{q{+g&8*swlPTz_|-;LOwV zUeot?y8i&%AGrO;C6vlIuJ-ND(zuF#Pqw)G*xO~)(#0cgw#)*wVUJ}kI}e`bChh)n z`>_7#{n50tzJ^-@J8!=Eek+jSqU5-i^2pZ91H%lA4HI!%E1X$dL$l~XbySZqQ70g= zE76W)#A45hNm*>mB1WR6VpC61*!T{ErXk_DIKh!vJ@xE>6C=hIfL-77R=9<)Zd!93 zX$tOY2%%r(jd~0$QYu@(We6l7vyQv!O=v(mVoynT+eXTU;wAkSQYs49Qgk?8BE~;L z-XOAEtq3y97!-H$rlZ3a_jfbM{;LQhfwu>jI;*O61mq8nEjX}~UH2zF>AG3kRjw`5 zZsAfn#lgqgb6x1)_M+-WNw~fb)M`y{Xf%0$)W<_y^2B`iJ#=ktE#Yro za^f)^vx8e(Md-x!!&q_cJ)aY9#bLF#XZ3FYJAHenL*_mhmg@OgqlJA)gSO)=%jO~L znaofFiDtQ&tGg1Jc@+=xstpF23_a!Iqrz&I@e}8H6~8yj4RLTiS>y$6jdlo-l7CG+ zJaKK`H`7CF#&Xw3iQ>MXdQx89ua(87?9R@t1wJ^PAf?r+Dy+8(LlG*_JqJ=45KqGZ zT3bsai5Jq15t)mVSn08*TR(-FtXPbHyyfYaSbKT=eThm+J} zrAAfwV}SP4u`Oh_?T+6>YoLIi>O6t~C%BoE0BM+9v3YE4WaPQBt8Za!%92A3;K^hQ zLz10TWn6I`)7|n$dH(E*OTIsfX4^fTf>5Q`q;pksQI#~r(Q(AFjeT3W+agQ;WsIFb z>4v)Q3)9DTj|VhYyrs&}TwlQ#k+}Oqmzmj>GV%Chw{QD`IXFLUyJ8V%*gc&AFxZQD z!Hr~=&`^5wC{rx7EJ4ZyPM7?d-UiTW!A^V-cW4sm)XYnLTm9 zb5}O2amVm}k%GjcZHg$ExL8o-TIy+?PB|;@NLr=s)y&rRtfzc1JcR9Vbs6Q0b0kb+ zl!c=ZRm+{@#)ggszxM}uc5nQ0{_Xz&x|^10d45y4r@807yX-D~%Ep%V{{T>A{;7Eko>d9C0ULaz z5soYSivIxViTAH?c2Bob+6&IX_O9yV`e%Lj)JRI%@m-zRQI!6X#<1G5UP%%+rb{hy zY)i#<6{i*Kx3K-Y8`;FN-5v+CI1VNt$dh*!=qTKk{E>xu3I_8NL7oq4{{U(In?Ji9 z=f*_M-2V3Wg{Ps~Tr9E1P2=#E_j_QD1RzNj#N6{mgo5eb*M=> z3z78j1CTXVE7alR>>uqS4(8=76-N`?-sg9H&nSX)-5MFTTtPI!Zr?jC=F?Mq9BB_rjo9d1hQK7Xc4 znzm#DaUTWQSGT_4bBEM3{{Tbd=Z4C}BW)j1G@^tY&g`_rT#tKl_LlAf$tM>C$kN7H z6@*hz>IosbWB$(GUrIkIk`)16N0b!nrly#;xb0pOoa8wxc_W_GaW>Q5NgT>|10b_T zU_}K0*E~nb-1etC+TUMkYMg-NtZwZM#r!H`dEqx`j(3AnDWx$QJ)PK^Rt*e=9hKA# z#p8b-xLCOUYWgH)O2Ui`X+yVbZXGL_!nMu(Je-bztTS4{Fr_@oz^1e#I^sTBYbz(@ zr#^H@mKr-`#)>imxnf&{ibiE1Hqc5B8VYqeSK)%uh{Du@UDZfEaz7n#Kv2gaRFMs9 zs1>GKV2Kp2!Wfz>0&DqADl)@bucp!NQoc}1)KrkzExkoRJ5MyJD>*V|8eA6(w`W3H`ZyvF}%Ka9+gs9}#uT zdwK6qXK{RIa&vyz!6TVpxBH`y=Mr%=F)mTbK;Z|NvBW*y*kAoKE8ls&y5n6hd0xx0 z?H2Fek?XFyV0;CChIj;;Py;|eH3#L(O8DUVY5`gnRx}}h zC>ejN0F|cUn9E#kA7CH8n5jQ4bn(FzHKk5xY0q50&jbvSzm#j?t~ELJ282`N_TXfG z7&NcPO@7bVO-_2{iUI!fPa7kaA+!Ma=`MO$RByYuAm&gX1F6je%w&4LXtJ5Ksi?d*DuRK(x(;MlM-lh z{egohKN3F?iXEhmqQ5*;kTS+XzlJ}CJj8*OGo^m~R+RZ=isnlS4D(VBM-_Lg5(eMg zDS!sP1Hzsd_-pg{3;@QN)RH`_(_udh1&=mSn|RkUXnu6T-H@lm{y(;r%LJaaKWWCJ z@5Y~q#YTS|vd87A`2&~i`1oR=;QgPw_6+M>bkP0hOaW2|$&Za`Onz^~{g~R(9QArr zL0xb_&S90Eq&{$D5=^~?-;fJMrTJiz6U z+AtI%70b?i3CWIqa2S>IbW^9kHs}ax|547OW5Ct;VT=mA~U_~<(&pj$Ixs0f2M^)*KN`XbwUpBSC{{T$~!{JKc zDhd^13l9oV;UZd8j@?682~cZ;e{dHLW&d=+W!Fhy5np?eKY}>^!TUG8(8fs zNX!}5qEO~0Cy$H-#ERHJbSy-Iwx}8QhMoh96{HB%8 znbWNX3X&L{$pVx;S1f;zpX?%zlr_`yfvS(5B=bmfYO7L+cVrL8sK#V3$L#pvkmjdT zUxr6La6HD96hDEje|8XSx?>*gGaYI%xRbOEwWy^&HNrCH)nHVe6zBl>c;b}HT(v(l zgkl$HC3fUW9XF4jC_??2;0HM`LDYQkRYd?4riPkz!^ypfkw7R;KmkBSF+fKB-^{f% z)EfT)3?{+}6|O`8b;p>GkWiwXG8iFEE0NIUfRa$7He!P+od@s0Sl4RT0aZ=Qv{ImzT(d3oNXwz1HODRNpSC>3$8q~@?O(cG>D(NjJ|~rI`Cd-@xbEP#u!<(A*=-95 zf}DX*C<7h2+WeKpk>L0Sir<2_w}LBUV{DemX*$WanU+9U_#9b5!_s*!;of9cJ7aZU z)Y`)$py)Am>oVT*cRP{>eY>Jc09!pX@fa^B{YNF8fCj8}ClYd3^P#b@{dcui3oX;f z=17;|dgIXJ?L;ci>Bnx0M0U_CdD>@PDbUwA5DvZNwLMn^eq^IF<`q*Cu}&bihSjitbL;pe)3Z zxby?B&bSuV*7Ds*;pqelW|h4)te%plqyS0yVScR}eYce@XG7%%gehMU@x(ULQqzFr zfV<`Iob0!>6-2s$Lb0Rb&aZ%COAT z_#9Ywzi`C~wvUoqt9vQqR(s66h{s)w)MMgl;f^Z)NaZ~5b=$3v2g9Xghm7JaKveXj z{$15IQH}$%?PFuk-`Ynl`8BGwzzohn^H2)wmN?Gbi4{1`Gtr*!(ptrPd9-@MNXA4B zesn!CZ4)Voams?iCUdoD0BIC_bj7S(UkcnuBO=7NJ;}_s(%H0fEeMHUX>y=$QZ>T% z#|eMS_djyl+AN&cbT~0^mNNnRmNu~*j(?So79@^IAc{APZGx#IDW+__JTODpGTg4& zBUzMwidLY7Ief7&w?$(rDUWL$=)q_~Bxi}c z)0&1$i(3h&lVq&5S)D}OO;d##CC;F!^Eyc~E(=I=bW~>m~jWg;7JqLj( zQ*QB-)pB=A`08;lBw5>zwlC_uf~V95gJ`B;9)l5bozrzSpK5W$O(lh(G2ilVvES+2 zodU@~yUv-@Om^6{;^M!x?ORR0*ZXxwleW8J;$d69>g22tY`VW~spgWVs=XH%e2+JO zJ?|@baP#tV+|BA-$#b`A$nBK?QdLC&11e>M`ffro{Y9xYf6BD`4nCumxQfS*$d_^i z(k`J89V@0krS`pycCZ?WZyXnze8>*2J~`w1t^D6pIVJ5aCngwuLVxPdPtEXCiMyM* zZ$G4P)m2N5M6!`=?NFR6(Vc<(xiIROPd6GvFJr` z)?I~em)!QxPW!vLNCcA-;E4whYW`S9gArO{Tbb@|E+?7U=9=n8cah)Cn|?STgl&3* zL-t}j-XnnKZa9c;mfw-*1%<+J*2S25cB{!1^o-6cL@!a6Sg_-Ly#Cg?Zhw%niKFEE zgSvQ&yW4(LJcP(>oGR{3*)0xv(;zW-dH(?X{v5}4a&9HLnkYHpV6tSX^tJ`Us3Mh6 z26^Ic-Ry7ef0XuLwfV`=@*JlJz*=##EsK)!i;hm|-sf)Gl0SAiUecSEyym(7GU8Zy z&O-Cmf-X0ZNnIs|{(F)YT~}$KCn_4}i9f3QhsSafMZ49q;_iN;HE+t%7EJ0ed5+BQ zo==FohEGvsvX)z9Ru$T>AR?xk6Y;_x;^et*dys$~yp+?)bdjh44U6U*m;eaE*~!LT zOA_z&u4RPPHwrVI!lNqGW5@)&gn^5~bv0^&0R7l_{)%Ydadc;Z)BrOhEkA}CiCx4i z5X)KD~v3b?1X574XLi z{ndYFR=v;tu6z62R~&yQ-@e&*caqZo4!nS;g;Gfmv<(l0^Zex(>&q}h4s&dO7wZ)apHi3er%)&TONYw*-PE-Y` z*TXz3LnL<*wE&h0qM9as#a$7iBpOp%oFJ(Uv@seHTme&`#|D{y#~3KO9hCgOSS& zO*bm#U-`1A`D1bZUnoE7rdz*;Db~Me`5a^xzjxz+SOWh5>ivR%pP8rb#zQ0EK&RXL z38@6rrA9)S*jA!q-wNM~A~3g(!S${4fh`srZqKXXB?&o-^zIU$Ze? zX@wwHtu@0g(U{L#fHTOC90pbbyFjj4*IXK~k#)cktz<0DKEPx!4dukzPUxC=qHwIFL+P>_Fy0TrRw?b5iVa;<6c z(zpPehMIoQ%y2SXv#y4?^9uc-53pbc1Ol|6HPb_i0Z<9+@y`|06#i8|X(aT)`Qo`! zx}O?inP8#WwTZ6%0VDHQ?82q95(k;3AS*LcyG=C%rVyOX00%CWsQ&F!X0F;Wp*j+zoGTr`0#=%WPf=WN1e~e>&50+b0ux3#)2%h>h9<976ego> zJTc{M$35K$@TtP0k~Y<~ARO}{XVj5`d`{;0(-i;#rghg}<;MN{jVrdfDI=i#Fe{Kfe#(pkO~g=~>0X(U z{BS4~5<0OY^b`jNI@3J-DM}B!_A1kLO>0U}(45Ed#Rxamy4ekoB* z&3;&N)Eih*xm0{Vb{Gm&iuBwnK0Pt7=KD0oPsIJWG|K^qQq0*lr*BbH`!%BH`?2|k z0})gooBkO7-F?xtw@CXFn6T%4tM@ayOoqHo)RHeNz+1}#`A*5B+$4O!Ru#zO+kfvr zv=S%ayTjS;{q_UjC@#P4JAb+?IEY&R0Jjf4d!mPVS*_)Km@Prax8K}vWAe8>tKUB4 z`#ERq4>8-fR||c^@%BRY^N+Ibt^})ogtluCjiy0TtPXV;<+$7Fp7Q&inp5h< zd&vuibe--VtWIQ?CpIUpEg|j+Og1qqTwaWwt`&$W96#zOD$^Hy>=rQbyhV%+XJD$K zNre;zX0BU}D#sE{Jki5#7U^c1W3dqn77OYet4zQLuZ|wx!YQwuO8XMhIVYAt4MQYo z2`W5k;e_IB;<>O`sr6ILTGl&DabH?%qbAS-fmR1UhAln4_V%-K7vbaPdyS6!j!sxd zk!)E0NCWPx4RL#5Qr_>mC5AgI$tqe)Qj8gzHt0bW%LeA^c9okXl45o!W00v71Plwf zV>j0@v4352v4%mKuH%z6dSUKwFK#Bgbuqg%jrwts{{Trwfqnx7@|Uk;YM`?`ar&~z zR8*vE;&cZCh3)HjJ5RJyMO*vJi-b#?=?w<^+mb|*6R3>^FyT2xzMW($Be#v|aB5UL zIp_wRam;gl(Qyo&!^m+DaocAQMDc^yKfec+?%28i0HBoYy*vf9Qu1&xlmQ>7KajYt zS?h}^uiyKx<2eiXIP1o}pM>_X_GPf-;}2ss#nsRK@j#?BsyxFm2AJb}QrDE9xGni~ z;iTd&q@;H|qw{bj#f7{Uqb1(2wweMBGQ|Cr$no5Twf7Ni44=|vh+Evt$z4IdINBmk z)m+Z%<|~WN8??B@zvWpQhrZ-heO2S`UgOY_B6a%#Z-yI>=eYNWm0JrdQOCw3Y55*o zl88$i#>?ko)e02nTy_U&^Y-FC>gRYUd;b8qZ5K4)xG7(Cm7Viz>N<}w_~nZktnK4G zXBtJdg_Mi}P9WOXY(dLtqZMQD$GV>S`-9AO{2T{l_A_1fM;FM-u}m-Jlj*FDs>?wj zXQC4rIh=QoyB**43w3+QHxaS6q%eqnzij9_DV@;^l*f;$mS8V(vy* z?4^Y#w(dT9;jXzdZ3unWZL~)`V z?q;9$Or8AQiCXbJ1U<8Pu3Q%|qTyjy`m|){0wVM9` zlDOSvV}E~oNM0X?j(x8)mJ}#}?fkXV9A%QhJ=cBvugypO54F+>W962@$OuLA+c4c6 zTC)-WJ#lAm-PgAKw{v$&+gz=m8os<-XB?E3VY+3hLsPYbQl|p;>NE*%o{hb{1N7}6 zjBWvxWDj2)UCk1hIEzU?UhncC+6Zm0(3AM$*4}uwcC~;&6<2(`mB2r$)an4GaCkn@ z?aRJzkdS>G9CsgU>&+xP!pAJpsG=Zxg55ge9vb%E-@1Re4*2xm=Gxhoek0q?-&O0|ZiI2XO}Q~aZ52fkB3cSib;Q=3d%q39vjv@Pz~TB!+b zW&Z%l?IUVcL9V9^$XeR! zM%}fujMUY(cvGf$RJFOdXw(N)R&Ah@s1>CSHyE!OsAP6A?HfgO$YF%)@g0EDU{EON zL2U`ab(MWZ+Q(}Gjh~e=*W-^$^}8xObKR0A_c$3waRkJ5Dy@LB{tNZ%D?S{pyRc;e|i4^ zyUU+u;r9l{-;?ZLa6Pl-xb7phdCqEK>)-ZoJIh^L+&3G`5VCr9w;@b~c}`dy7kB>v zah~LR(cIT21h(OOtG%xImcIIh{{XZwF9`$@5b2tl_=A|mw-vzghlS$!^bm2@O)I;_ zZoYW^Ln$PaTGX27jpV6yU7>*)t!tPV3WIT>W4EAPYlYfXg-Cry->~4Slwh>nROT!2 z`|;$-RwF@`cLP8P!?jU_g-51WY>DRl+sB! zdWU{PWqO?}iq||ENWg6wQ!qwH0d?8{2TqlX`dxu# zKe*vofPXesr%xOOjTLrkz*BbY^Tv1}YQmekgZ>5?81ATITi{Iwe|{)GC?Ab$`!W^9 zd^I0sarb^X;=Mo5{eeSL1rNf98_5Jz3RKq#K|!}6_Tr@cNXot#@=%QpMQD10Fahdo z@~NQ$xX;3$U=+r}f0di;L!OwCl8qc~vvQi}S`WViJiZ_9#cP-z1LcL^L7t=zpBxYi zW@>9suwc_uPs0N`{3-hY@xe9I;etF4245xQYfK7}ma6{{ZT#f&kk>Ks3}0y5p%QB57QXwfJH`OU&{(AR5pvN`D&T zZdF{Eisr{QH9s67QcKtJ(=Bqrpc&?Vc+VgKp{}*?7!@^d4~IN8_grav&JT30No_hNv7K}xQUYt-W^u2~RhPFZ|@SdaxFiArTfU4o22 z@h3d+NfjrrTvI|te(Zd*`?AK>TAcGF06a;rh6s(f75v#{QIVm>=Afa}nhKn{;+~@I z)kp-I^{xbFOoq{r&mpcn&$t|_*<>^rWm*FN0HzppW7p&|!l7-)Ry3_DGSBe+lqQta z)BJpJJoV2i4D;~7CsF)xNYGCgP_`T!UI+YtfW-_Y|TzG z2A&5heWwLTrAN-698qbIr&C+ovDGUGWBH%I*Ofj#rHC1XSye)9mhBl(@X(`&WoapyMuOGuhhR zN{Y`cF_TTfRZ*B~K{}jq{FB~oxte*WiqV@{y+X!N24c0TK0iEvFUV zu_kk8)2@CP;~VgocYh(r!*|KeT6&9V>Z#bPT!=kuOtHvDyn{$@o)_h3nS^k(V4cXz z9RLJt{`_8C@OSpyr+Qvmn@KLCWeLVxXwX>&{$R&4F)i;QH20SmGc&u~Id^j*K38M& zpv1HDrbV)jGGXITuKS6P{WF$|I{Yx4Uma&}#_{3AiES7P6sV`GEA>7k;6-lFaXUCx zK&0-Ic7XgbbAQj-$ISE10BAUw7k#C&Jt^vumhQAT-w`MCLugDP8E@SI#Pd3e&z zK^3;xE4jfVCB$OuiI&b~^uqD?*6(k31GykYa|?~gwSGeE z0cC3hLaet6G68_6n;frbPjI8O`+4~RdC!kZTN{pUz>=0maA*Xsq%BUkwDy;}tX})w zwB~!8zB__Rcg*%3p%fz5w-m}V5dyRer-mc%PW|Sm<$bN}OI2P=xaF8myl-+_fZP{u z+)R~{+EfaW#gzaen6>wpxLG?d+-mQqCvto-*6FxKqZ?vo-a#M-hE@U_hiRY?YDN0ECL?kM0ZKgU65jCxySY_LH{x zd$A&MclRB&XqMcpHWC_c2|SW4feF!Z+x_A01hep#x7Orvy|HHipOx-oeIZ^Z+6bDA zBAuCiM%Qk*?LT69?%3k{W?yDI3jYAVJGZ(x8H-z~y>)RVnQ;gs{AYH+4 z=^M+-{&SwTEo)(D7!qYd!Ac-B*B-(5eV-l3@^JUpbX{^ckR(k9JKa6w%-hJE9}VbD zLSy=;qiyn#yG<%_-&eOhHQy=jE#JIWJ+0iZ$xB-fJC^P{WVx{8E#z~xr-VAIBxah| z9iWZc5I=VNWyjjV%67GsR_}ew@(@O|PjMOyq+pdh2gqXQzI#*JUP(KTj^S-AZaEv7 zp||4r3;T#f6WHB5H4<2fGBE<61BoZJ0d=n0>eyqh1~ww_a8kcC2@0k z-IsRvw)5ZI@)KMa;y5c6bG04**_I6lt7h| zNzm;LBUfr`L#8?I54Y^5bHH2AcgtDYN4m~SNR^CN6Ek^K`AN?+(@b{!_G=B#E8lkp z-;1%>-lq@8OCX5fv}`IElJp(}7T(zVO+AzM6nrdPmwWq*eGGE)A60*E>bC*LB!a5I z9Q4oQj%$;W33(pyyOQP@u6Y?{xEAOnsF|6oo^>cRIJ&%*?l1XEo0(9LO_7m}OEfA) zN_ZZW#D|o%i|bekSyU410Xegv_~6YIswK^|o}?3%D)7F!f%7<&!xz+C-4pzuwA0P(y>WAm7>rV`v>sE^U4&*4#r8C?q5wc48Mgw><#xWESt)y9T5tyL&!XH zz=F-o75@O`GD)JhRZ>3O3%Ha>0|hcT3`-M{H0fGUVoRA-oq^q%EOX3Jx(@+@#TcBt zq-JP8oBU~ovEaDto4b}{xscM{1?uxo)I=JR%y9*WC(2p!SIme$kB+++(ZtXEqUCB1 z(fPLNjyl7c?-JHKA&dqNSj?cHa#9Mz#;zb0l#L{ZD019GhxQ^St;VkYGYWkM5 zM;E3uQZ6L|r)eb9u6Q1+Ht7piSyYcJ&{IZvX!N8B|ht`b<7OqJcWh-F|Z6-9be_+cXxZOV!O z0E#sfd3phsBeCQx1EgCw-NdD3krZY(0i8M=B}BGT$skQ*o|CEG$nfUrxb_ucj!{ zDf0P_b@0MVZjw4U-y~=WF|ew$ARZ?wVimr*y|{!v41 z+T5SCt?ox1dDwiPxn9h7{{U)Q@w}gWa^0hCvAc`6xtZH}EHk|RhB?=Da@lt7>J~YD0NE@i3O9FP0LVoP<%&r+p1Qn;2 zf|#z_MxgCrIt-{!Q-IVI*0dF^DVfDd$hYZ{mg8T_aZfH;d~sH4(Q4GDXQpQWoQcmO zX`Xlii?MP_h167&mImnP2CYx!`Hr|UAq>jiVO;N&C(N5s*;69}HK`_EMVVOc1O<%TNvueQTxxuAdLty0kSJkBvzf zDnBn#rA=#{F_+5~QBrdGb-*>D>z0O^V>YkCgBfdqT`DW4D~&%KYTvVUO-6qVutCzl zvaO-WXm!^NroAcm<0Dc2JaIwejQ;>&r^gzaD6iR!{{Ue7e|9O?@cZyZXliNmr-mAe zNL-kaN*dE2AGZ`Dj8FKcH)a@*C?>T&7!|2B=|hUD<`@7;ugfo<2K>WR*D<#$Wq=dT z8FL_w384mq0;F{!w8#>0v})_S{{U5b;8L1v*A*s}Igww#1x{`l?WTlNAV-b}H2Dm= zpZ8M@T!E&Q8XRhV{8oqOjI;S?fO=Q>90Q#*AF>jryFdgDY6e3*6JIrJ*D`vX5ksv= z1d0zF8Ukrfhpt$wA!kaAjTWO!1y=L8W}r0yfl>*;AXc@>O;6j3ijAX29%EYOFh@|# z58s2bpqhrD$b-`xBaV%q*hrAP+_YN@uB4bwW}tb`%RK3V2;A7R0&tJa^G3W^?- z!49eyUzrAj6;<-99l3a#;kSP<14Gc)03Y#|6m%r+9wL~o(Uy69Gr^I`RMh}9%l>co z<3{_3`QvtFj$1MUt&z*{#`L8dGNU2$7$!f=)ZL|M2Zoqh)|#;MQzpwy7hQIhA8D`M zaQBE7uK3lx#Uy_$1HxOw6MohsA7k1b;~aeaadWmkjS8lf*&SYWu7AR zrk#7Ex(!;+CT~(36$V6di<;KCnqku6%#tUS~*4DccfhKk}2)!w{R>*x|Q!A}e5^%<9K+E~CsVl@6a~EI4Qx z+DHmY>ZFhW^2UG+!Rw1aV2z<7Ic6Rm_)^x`xohrnaoPUG#V?R#&zY}QUknfDi+n%j`(?)!qm(Pg{f zu6V9jXtk0#w;@2eP>dgO6Nlw{quv+qenXAqd5c~gT5T90Klvm2K!91PPmVeG{hZ>s^lx^3 z@xJZbeh|+;A$P@g=Vsg+dHaFy<~z{bJ+!w{8s=onDqwdP1;TNix!qpiaUG5AEzRwU zFCk-n#c41QJ|+R*`Bm%;T2sYnd(hNc&&hc&vTm_Q$rO0?U`NwBo+6f#k1v z`BszMN@F!-2AxeY{pEjiT>k(AB;Cm;JHT+h)E-NYOSyO-YddP~7nM%khHztzrLj$+ z)kSt|LykYP`&)|Scv}wfxL%y2#~&KJP{!u1w2-VuS&Vy$ z-+kM|_b)AT-#xwB{6Dn(=W#1oKOyc&5TQWMxb|=Rd**Jr zPRHzTV$$+n{qAlbfNT3nJJXt%`lI(RJ;B@sX>1Jg6}Y>Tac!d)QbWb@{m9(JoDXPr zAGmyPZ={@OBgF9`Vfv2Q;X>BT%)VoEP*Xi|?-y=+ciJ7>&-;=Z4{UJD2R92emlPBZ%Vo*#7{mStm`&0hkqqO7s&VT!L>`qtS?(^pFt^GS5KGT%rZZErD+E%w;)s`f>PfYF3L6FM0 zmAi+5va|N?+nc)g3zF`B;N-73e(vP`z-zzzORgJ;vR9H}2-*R2`A1Ao?k{tsy}8(y zJ-jWruIS*HzkKr@&21N5caI0|U$ybKclO5_?MH3F$UhSC zLB-#t$8>vx-}m=)7ca@eCRMSI@&+AoX~uSc1!?WC_Ve53acvBWEx8|YuK39PR=MP^ zj@MfNMN0;w9D&DYUUnA&?c6f)e2kr)?cZ&3Tog|h8LkYPllX6FIS|A{tu@|&mjc%M@ zu)fnmF1w!Y=HAL%uKDNehu2(OcMl;j1?84NX1QQ!jKxMFlGm_1d)sauOack`YgK{! zvxv4JG`A>dv#S-<>ygJlA8lU0Y+2aw*OJF~+uTeu-azo%GTK{OS~5D8kpBSGYK?Tu z98b9)-Ar5`J7E>9uyLKXY$CC{=))#yno3HKoE=mIn+`N(pia6A=nvtd@x$a)X@1Etf?c4D!rPB8MTX9kU08VdC z(!$PV19UNkRoz8lc{#t6;Lvss8kv!wbVUmSP zXF_YJ#iL%_Lv_q@YG%I*Yi$IL7pkx$Ep*Tyo+Xk-U)7nd)=0=`%wC(fYkO8wk|Eo_&tDv>YlnNAc_R+Qph3Dlq?#EYPP$;h zX3~k5ZuL7vYNS-rRAQEJy2-S$ED6cKGqCv7;8+M9_Wq$KTCQP_@EDlLqFG#5(E@=H zMJq$p*CB{G?)|yuJAx5#tUDuvrKR^Mz=jaZ$v#m=bY}3aGsX9{U7zgFv0l-3_cgXj zxslv=bN7L_G%*Re3B1hUeq)d(xpc*z&e-=eyZM_dc&4?!?KFx$4K6}ejCb**%_KyH zKbJ9vyy88z>|bbjFF^+_?q|9jB6xV%XF@09mfpI=%F zF5thgUL&=5M37QhyKjJH5;0(CB)K4hD^D3Nxf{Qzy`7Qgl6jToV(aDg3CLtRVz!t3 zTu>~cJCyakOSqRJB=kq-9t0dM{{XmG9na2$iOWM`>&4q6qT~yj*UehzpvNJ@dtvPF zY;s-3t1LWkck^72_3lTCGMwdWb{j*3nDFt%=QrK+$;;o}n!&a>;k;*RDb4Qwn{2%&|lH%5@e02I?B zfYH1OZq#MSuNtY63p^3&35ba#Fq9c5wt*2n9CZP(u%=r zUWYFnOKWio$jZa@MOgx{)k@TL!!ZR`plLzk0I2DTpEhUh`wjxGOmbi40GxrYJynj} zBLG2b%XO~f;k#Pkz4UgoD&$aK#kdpy0HVGaTl)`55CQ7KDy2+kJ)^JLu01~~2npQL zjovzrl)!0GU7CbfDgpeUV5O|G(mJz3jk#FsoNd$Pg9ZFQMahK#P6z^8=t>;(>M$pg z*=Mn-NbaIi@i9^PTAC5oxNCua_FTdjkfRak?O)5c;(y_UWsuCV5E&b3MnGu6dK!%@ zgywBxTLZi=C2JyP#*{JuI`~r@KW}^EVaRhn)^esec&=s{*)P4i=WZ_ITQ2pp09JTy zO0!(s6Xrrl%|Jb;-2U_1S$}n(+XejmoSw}80C)b>*#7`l4glO2A}U2kn<~@cghf&10V_eDO5nsv3J&a!ZJkcz z@xtx}wom|A(tuKw!6?NRhN?6J;r3;Xy(&+~GrJmVnefvCHS(wcWkHs-@#&8#9yK*N zQ{jSIg_%OJBT>*1flm+POa|%&1xcW;xneYqfH9*QX;V-4;#37l^BRSr-L|Hq1IZ)R zjY1j|)})*oD=4CX8||1RzpHGG+<|PI}^l zI%k%6&;9rm*L5=LPF1Ed+)s`v(@YGy4yP0v2BH0gkzLs>HLei+GdRn@emdiJS%IlP zepsfI#VJ~RemLBtKDqw@3{>he%NmRjb3ZfuFhHQKHLhm4=5TB9!&j=2_v2r{<4?yL z0r4l{itZKh{{SDm1SzMSlq3zxMngPPO)5u}w~c9myd9udI_FIVPB+4!Y$L4(N8{iy zYn2TsD?!$RxM>&pdLF+4k2YC&XmLg~4MS0#KK}rQGRzL7AHOVCpw#K%_+WXuRM3G< z+Eko)(=A3aQ0qcU)|ve9N|U&q4~f$+mMfzV%mDzCjO(XUh5TKe{4&8RwHfR1>4M>iaxx(4QPT`*m{1gxkR;TaVDBSy3RQ9%Wn2@swWeZ+ zAC!-{VVN6gBTz+0O>j${#EKF?75lJJn9v2=T<8Zt4k`_6QKdd30JTFG8C3pSisgf| z^9+DF4L{$2MpXd)`QvHG)}IViX_)(sFjO4IN2l+` zqccyp!}bR*P2^-EVntmh;KFNf0*B0V0*hyC2*4XWZIwU)fjpW9@wZ0Nf7~eSXWxMcZ~<&l0?+ za`%s9OA#g*AQG0EC80r5GRM7=N8em;d+_{k1=+2{_m>UIN6B$~w+lb3acs+Z32!PX zs{`g6=yB-S35-z7DnlT0&e251G&_c(gCmEzzOZY$uakH3H9;?*&k=H5UA&iu-Cjvx z6)$TZPLf6jy7lnFO)Q7jjBOi+?Sy1$T&u1c&TGi6_=qju=a{*?pn@AnfM=CtZ~eiR z)dXYeJ9ClcyMp4$jDj9c+B@v3^nD$t+kRi!z-{=Vb$8YfraAu8<%ss zlTQ^#t541t*YcBy2OD}Q-&w}s&EzLK(5 z2(|_k%-0>w?-#pVgI#j`<-&3o^7kdW*jW8ks%4Y0NG)SpgCj;V(Br+m-1aWU!|;XmQ^@;$QanY*~9f*t}WnZUPI=YJL(6d(PgsGT(O@ zJb&rN+oRpNsLh}`(;dfr?Pte#C%azxUiP;Y-Tcbl+wmQp?Z+8thY@Z{{VRNjgc!XZtZNOb+18> z?f1Mrx9m6>{{V6>W|n?Cxa>IjsUhJbx(t!c8&lINPDd9# zrQKcg%l99$e#m#f3C(hy!EMBw?l-r+$L1oqlE=3%?HQNrp28S_6QS7)R1(3)JHOq& zt?bYSk zkJLiSv~`L$FX+?xam;t5&BSrto7h~haB?><*_>^+E6Q`7-!9{F{E5BVjT(>obzns@ z&lfk(cfrl>m%bgn&C2)OJ)g{6pZg0NOGF~J++=A4F{5Nr9Oqnnv+uWJ zb3FGQ!FL_Xdqdkd_c8rE)}6Q760Z?wKOwt00vOMxhci+RLmX9)by;ydxAa>{c&;Q{ z!*MnL03pb7P<;pW1>?7RYYxh8EQW-VGsg@60E>3_56XLU${QQ24qExl_WVP_!sn8j zE;PCYxMNz#0NQm27M-1C!EyG!*mkwes`r08YsSUL$=z4{dWMqE>SLl9&;llPOvP&0 z?w@TtQ;@gqFKN3d!oA1N*diqZ1+j0H{Ipe6hw_ch_{@ zS@2!AZ8saj@{)2;@b>o1d3I1@{B)xd)4lDxWCr*9NWe;|q# z1#6Dfx9#%%zsK86DvN#rPaEy00}Lo0%3foKvD*r>0v1LoP(UQ-gBSM;{{Yhhm-~P2 zo=1?nTMp9p?%$T~&L@-OE+it-cd_6Sc~Rs{w(Y6MaB#OhoBiB>aZub`lf?VC?Dq%W zycn7|l#6@*Qp3EyjTlM2iCEOoDcwq~c@EXQ@2~D_+e_X~H@3I5?q6nm!3!{n&d$#Mz5^IbK_^xvt0V!l=QWUQXvkk=axiQyyfG z3|iTD-?rZSe{J{0O7gee&Doc?{2w0D8o%`JO4(grm7pgex#N*O+4H@--<`9?n4sXd z-mSkk$;gYk=I-Ljh$um+JB4eAzpHRYE2om)+A9zuc^>BB#89uOx{~Y^3g&Xh6U1F| za7LH*t8f*_BF8N4ve2%S>Tz*zCC#@e(ZKR5Ap>Hm+7?l$V_bm#7`^2=IF{PYNfE{e z>dh%0Sgp`zM~N7DB)3$yA&81p9-xa|?OgHeFA5;GlVxT{0i15ih`JX3HNz(oCN~B! zvl$2v2mqG>x`1(T7|(JhB{zn8#zsmZ21fB-MpXZ=Hxrx z2YIrVNn6>0B3~FH$-q^(G z-ePLy!26CbJ8y#E`?rehUM=MGEqU2)Y_INB4%nvVB%o;bMg>K3sjda*b^E#PKeqe~ zDILME;T8LnxW1Y~Ncxj&x&2Itqqe;;yx$+&z197~cVdZ>&;I}@iMxBWYPE$*kV zaIZ9MSQS=Np{N;R>TCAnJ8O)(V-#J#+SWF_FA-~Ntah-)YAWGYfU_|t;fSA)v!3cn zh#T2U57aay9WvIqNpH+^Fpt)b4^*<$oyvlSm9C=!-;cNw9iE#btZ^tL4=?XD!$9}N zCLv2K7iFH5%~&bvr_T{R+!^oYR8eZ zWzG*B^d`FaVWMOKl+uSPH-WEAM{>>* zK!i9~^2)t@gCKfT^sX+rOVMlVh?*-q=}Ih?QcxJAf-&ZkbqJuYJ;MIqQ{8?yu}^pM z2bJv$FLdL#<~^402&aHqbA6l2as0OUY>nzS=Gj8bPDZ4k?hm-Vli_`!_lMlitK{HM zYWd5{uV^{G7J0+l-EkaMgT1c#T~6^ziev!9cQ)K*^vG(`Mk0_1ow6$$$^j}4d1p;A zYS-x2g7j^)7Y`J2TopqjdKT_PhJYIRal2^?N`}i$xnY}aX-$BdZZ+%SkD1$F0&p>z z{4wP_kIO6_z%l3-QhYE+q(w5!oNon%dJ~t}Fj|!&pKVWEA*}%!(@)N8fHTaFm>YUm z@%jGEY4~D=hu@5j@0LF)`^G2%TIF1gKFn8onh(kbwaW^8+O;Tjtw;PYj7ZwR<&ZR| z!{_?|>c)yXldtS>To5Tww^5b}t#j0!d@)rt%TeKg$1*8H@--N#*A&kihKI-DmMgEz z{rJ$2#~BPz8k6DhKeD6)mV{RsWn8kwOIDpLpWJb|Y1E3=xnpY@(?7T1aas-8Ql!%w zwE+RM%C-JnFxH2200lyxqaFjt6giKMDV;npssSH84}iws-AZX(zh~L|O9C?Kl}H{H z#^L}iT!+KQ8T7?Pe++%U!}igIM^TUZVwCaeOjPGn{{Rhe;*_DM+B(+-XhAt(K!lr(U>NjRbWiSX0d1cbo&_O5}6MR-|C$ zD^pze1OEV|j4>K5Yo3304s^)2y$+Zl*Qu|DRL15Y1N<_kE1%ndYJM1@-CvFk8~Jh+ z8tH+!)S6|G*WrRX48A#KToMIqfC`EN56+!2zw(dtjVrDRWhTBGMmHKzjJ#+G0=N(H z&pw)9jVrIqsK=EynDVLWM_g3v`@UEN(xc(xC{7fg$~C56JP^6&ayfi@<81)jl{KX@ z!9G({*1y99w^~!fD(Qmyobo@r3hYgCZMjf3G6dl0X_3e2A}OJ%BmvXQUyhiejS+Jy z7gJj2h6q4AMQU;)m_>G46rs<=Yf+)k1U6-IL9T}(o(s_DUCpg4LHOWjTKH#;8}9wJ z%lsN(ZdO{<<}^?$SmOOSx3-s;Hs!dBdF5E`?yr;+9kkQA+aBtEW;x@8?C*9v7XJXS zxjy8Ebl^LcXFQK*@|Sj!dUIIIdk`Yta%<_UpD+~~zT8`+@%5JI8_I z{kXcBVCFd=aBO&q<+ii5`i-axQEn~d3~E52G{?EUo9>Sz$Mc`vC-xKU{{Xzbh3#e6 zEpUm*^Ip>iyX}7Tx4d{2-u7-f)6@~eB7hkMMGjBfJZrRF$IJ`k9LprqTXJ?$PawD1 z1ckjdpsvR?9suI-b6u|2FMN{Oai3OLagoa_>^I(`eq=%C+6{UfN!t8B4<9vq3es*` zU}-$e1d_{T8WBj;ovIkuBV05#7V`0T7WMk+S~80_m;QjRWM`jDO>j`6J5-e$V-hjX zZE~qSF&}RvYYX$zrPbu>0coul{)7xhV>;AgJFY}}m-kf(u~*lzjdER;2Be(9IgE1f z@qC5QwB?ssQkARj9yHSp7ocE?6}=}PmB|2+PQMxqa$F}NJHy-EnZ@37T&>JIr?uiO zZKK~KnGjRel#qxU`G^?y%a!hqLLS)VyXlvj?fa<=H#ZzVZQQ@FW6IpaY)cx%K~w{E zNUoUU{kHaxgXF9`8q1J}oc9;pj#?uJKkiR1zG$Rf!++9-&KR15t~-)DK1$9z?pn$T z?m3Q1@LF8M03WSpQf30>?Vdst>sa!4Rw%`zLFFpIQkdsCI}dX^Yrmu7F1Y&+ z62p*Xv+fI9Ye|dTUahU+3f`8B7$3`1mOJ0v4pZJPFN5U$znbT>IIb_4x0mTIxHA(o z%V?IP(XaBeMo*fR819Z!hM$q+{ip5jM(#gup?eM8!Q4IXb#=1l_l9<5jKRGPC!K*- z@(;_7JH5Nli?`&T+OEihuxxveljaki?@s0JEBi&WVkga#lwygl*fEYmV9O&u}>p z=zYuBb~gMMGsat+TfWEVIGzS_yk{*eVT4U3DAJNQG1nab04-zPo!`L=7@^~N7;G$( zE*H3nQ54Zwaq;Z*ZKQ}Yuml~guZXhkSh&6yf=(Jv7Jd_rx0X&83kj3cxVVBgW?^$Z ze^WC;a>oVP-G|2V(A)Rty4F3x-27J#exfw(k3sHvYtBYr&LcC3-)K4l4p`*-rtF)` z4mDL@#$M0ZR`!vO;2p|`Hg{_jRmpgM5@UAYVZZ_TIQGK%2?A{97>Z~@3ZCOlaA!zwn+C}u{S&Gty^#`fPbN4T`JTgVyS2u@|xQE;e zI6Ie+w7H9Mcj7MY0chf*A5J>{>|Vv)6JOg|-doP!McdIEd#GViNu>0X^6vx@@~2E& z!Py`SFs{-S?xtuAiekzS%ETiMnV zpO_jGPyxu|9$UNr0JxuVE_k>Uzx3X3vAZvq;$4{0tw8#NBX0gxrFF*}&-S-%@_a7= z$-7T;-L?`z*gVG}B7UPyWykQ$eIVJ;G4oc3EO9&+ZTFwN4}T=)*zs4~+*f6!ap|q) zw<|0zLq^Bc6*&RZrMI(PsmOOd*E86B9nW<4_jmJM#~(KzFA;x3hwW}A+9(rbvPqLn zWFbyFm%Ka6wJxl#Q;RtgzclI(b@JP#P1CSHC;Ahq$t`v2`3T6EhetITd&G<5>cVfYk+eVjCV4+F3=6 z(YzAgG|h1=QVfYeGobOs#pR%m)g<2Tk^#CDrfa*k(lY$^%kI ziI2lEjylJ;_`5G|_h7TU|N6AN#xDxXF1_7@OOVaXVYv z&e`C(3(ii=?~)$q;=5`%UU(P*9FpyDNzRzJldyO`*X`aa{!};ip-rDS+>8jnQ+YkO z18uaAoGRL9mS+sM{lgvQsiO$DuKxf~|Ho@k1;`fKU6eBpeq5f5-_4t^WWg$QeinCuE8==xIVuC${eEeoEDD z13j(flwq3GAIQ=~XkXz^w+(A)X%}!=IV}zBo~@naK+#!bfrfbsik)$>=9h?Ppjiv< zRV}9Aax}o7u(@s{xTIl7?4!T8kC0_y{{Ty#L&qEjPS_uNESV&v9{Kw zdTT@Bh^L+zRtdbKV;eFl10cuu;S{iX`S%L!K576y&2b+uYM)bJP#I*4&XBz`Ndk(g z>T88%8PdAsE8&L-%0!A)kdG*8R51Bq%NdbSRLU2qCY7P^#M0c((cSIc2>$@so|@J} z{ME~D4X&|EBP$^_b=IYb{6;M9t>!V9Te2OA;^Zuw`<*tDJn7$n22^#7-XG^%^ zNusv8lm$Z{tC}rBmvqcf0j@or?Ec&LANy(cFR^%TW1H*_H@QE!%f3hYJ-;Q!&PN{? zY`vsYx{1)CD={8&KsdMNy~qCjJ&l4?h+fBI>~9Hu&T+!tzLl-OXz92;cMpvQFT0bu zz0~f1d^_6gdU5v$I~3e!8N-%S8xAjt-aSo9dsFKjF|j2rYl_ha6aGPiVyLYGdRx~oqrl& z{67lx#amqmfxxW;cnpOxks9CtX`OMmT!w#TKV>>?tv?J3R<-zIO6$76X*ubDe17kd z#F7P2eLBcT<%}-54C&x7O%|1|RL`i(8$hd9bwzUqnNtHYNFHj6XYd$j0;HXw4MF(e z5Wu$3Bmhv>olXI4)r~$>8U7e`E%|hy_y(?jb{OZ-fu4iqfaa>%wK`W(<^KRYQl(cq zouq@be1CQd1}2$gyFfUjlTARa2{-`$Mae-Qm408dYOdz!2D$k3$DL@zYC3$W*93w} z$EmJ4wAp}?*{0#1GcgIpb%*Fs3o4EUm_uwrBE1B>F=Z`HYH6(Z*l<9#$p|q$6Oje`r@WlZY282+2^2USi81m(ne%&#q zbZ|)3j>>|5+%%ItBU3^%$egGEz}fjJGIDR zFx)6bBMv5LvDQsS48(P|1q%p%XNHk?65Ha0f+qb#Ab?fAcweYc+OrvarnM#k6uL_Lno+i?d{sG zJv69aPa-k>s{a6RU)!%2&vuWxQCM)D@$Khy_LRJb7S~42+Aec|kB^JXJn^h_kVvQW zn2J)h?r-i}usc_Y=KFK|hV9Psis?CHHU9uP?4P|o-wN@5<5_Z75J;_YVaY=$D;zM~ zDU6$lq)nzFR_^I9u1_Tr-A%brOKSTAHmsXBTTHam~)y@(*z=_8f`} zkz*y}7n$vx=N4xrQ%;zhmi6Rg6D))gBREI@0H{lN9}bwfu!VQ+92ZWoV~rAQTla;JcPFt%&`CxPJb*|@|JffvD-!H8)sl$BgC`{^^zJRlrq<}7e&TxK3j)x`HO;BGN}7d5K4yAp^UoJk zK{8#(W`U#-5bnN0%e*aVT4E0F`n}dScgx3pDI}`QD?t!%%Rn{6D}QYa&{~N$OjS!m zF8&`DUd@%>ajS8R6Ry6hi!eZB7vHTkYThW8(gxM$C{#b>(zvwdxhv=9EW5FLeS066gO~KZs_tav7CXeV<~S)-$-3A%gVZb|YE&HY z-F^M;uW$DpR-KE+S=(K9AF_SVVeMt#a9y+jwZu2}llrm5m8YzybiF_a92aYGeTmQT zy@%RM$;I0pgYE&b=R32%c^j8fi#K=ifhE3<6Ss3bh0jy2J1dHV+pZtmu6Oz!+%oa} zdnCMn2XK=;rLEr_(Fy`03hyH=LlQSJ6+OAi@?2f7IdR1-_epm&qG<0shE*avw;)m5 zGR(wO8dn^UFmrACIhJ)ZWrlJOI=&2?bY zH`PyaW%`1NBkH97U`;8GN9f_QihHpHQE{rVK?S{>NF!L{Sq?{Np(9g{N)a4fXCY$- z%YHq;7P4JV;7flzv^$jv(QAmB{^oYMN!rJbHbZ(yhs|*$U{}iL$os#P;^&3$I}2Vq z$_U_Y8vf4LZN%4rew(O^O{SF{amPhJF+9U!b21p+!_RJn2;!0>UojaP(-Pm=vc}hh ztk*1d?v1k84G&Fm9_d<0M2FY>-%T=gPZLiJar`x}ZKNHo%iGBb?g(8=$upshLpr8W zG19C*97AJo72hFyAd=GN?%p7faqi!eYno6R0s^q>i4y+G=6ShWhq!J+{Ks*fN6`4 zRy$#Mu)J_jZiT&TuT^Udk^pi7yJ$QyB$3%eV)5G~iElF#Bq=EM5~`$-1#4R2B5`o? z(4>+^po*PYTAA)Nr+~vGml8A+BQK{ThGZ@&R0E|z%f}q2BrHFxSV0MvSb-(u3+59l zYFKCh#~Iw6xg4ky3Va{=a#?IygZ*XO{F7c2xqk}>G z)UF=mh~}BB5#)|ITPR80QV~Xk_+WiioO0U-qsSB&0I!s(*E;8cVRgiDbJ^Ik&jfc+ zhlV-S)g_iQKw7lcxOuLpx45#ljM~Q(X&2GL7cm+{fGMEJ5Hi8ydsmdV?J2n%<%^Q$ zZPGO5KczVzS7pXd2I$^Zs**F>O%5Rbm)l%-9m=JL&?hIx$+wWXhTc#Dau@Y4I!m03 z6O-p497WvT;FU{G&3W!3xw%xH+9D%6R3$3T&9*fflaR+9%-#2Gr?uU;%3RIKTlQ)u z_^z$znzonspw*PP0oXU?2NxE0*8E3!_P-Ng(%$o&7a<(hk{d|&ryfv{t8rR2X_wCe z4hxaIjVo6@)JYfdui`r_R~?N+?y6naW2Pv+Mgmp#w%938JE#aX8y{gKJ@FkW*P zXeidwTk#xmXR%p_$0p5sX(OQ;99!^PRI7afu8Tm+xRCBv)~C^KIyBhnm-jp7LP3rF)yFyJl$( zWRdElI@H&9@4)2v`-_e)WgCgI+^Z-ezMae_i%yiSHOBkceHk6cuOtxyWqJ_Og-|F7 zrb8A}@O(py2&iRe-X#w}?GdF6L%t11;>;I^c_ElIBR*Qdinyb_Tt52MvA4 zL2cU|w_yxxf81Qe%fxV&@Uv`bgx6H{A73g|{urD804DdRpSfs5Lv(vu(%};FNdEvd z7S~dpraI`<)8~tk+MVItGFrPy8rz#vm-B}^BKnJ><9R(nr8)}Y?~vsjvrm<5^=4}r+xIIiKjx9^@E z0gL)~c3P(*8aElD-rNHy9tU@auM0^6phVbi?tz$zkeSv1&midt(e+{<0IQopQ(2@og2Q%17$NXue{# z9-o%Fk(MqkIXhY3iMss`PR@8A>8_xBy>$#JxD3N7`Qk2rizzn^$i^-#t}UeXE!01p z#W5M9D9EXy$A*+3gIuZ(UL#Bhtt4Mp(=Q@O`)H~8iUawNz+wwKXr)<0x7A!ZWi`ov zPAUwJbvS{DW{A%p>X36#`GQZtVt0+5Y$w=l<27YNKpHxaj$;RkfrY{{V14)3|7kUrjXPCItqXcsT7?H*VOA&pP{ zWm!lJ56E$rT>k(k+P>oUS&r#0d#j6xaec#Oa&xy^zBx@MuB`nAc#b4KtlQGCM^hnr z3Efp%p1v4~Zs&&D;zQ)HorGMAs+yUFsXK}aYf9j<{$r{59+=scYg%;i&^R``q^wHZ=}_#6cxs!1AXdSKF-n(2Z` z&vBD@akwzesre|D^5Z1m^p6u&fhFGEkLw;Vh>8=POgc=c#xAUhA4b68$T(?*F*6DW-wgtlXD7DSwJ<=6Zc@E3R5;9lab1+^3a@a&qJme z)}T8pGS^ZtPE|e{R~eu3#SiVz{B*zrASg%KhHz?0K2T|?*9}8l)`#YPI3~5v_z&5w zX_w3PQ>RatOntw@_KKQUO66ad?bG*T^9-ri<3WaCN0{gcBxj~r1o(CM<0@z~9u)Dz zW&(xZxa2A@NC4L?!NCVJ{zEJkZ9D-r*U)eJupEuAk-YjULvP2vm4HBT7q@> zXMs>Z%PjF+zCRpkUxq4NEk@ESL!V3$opJ_-wWfIM zk1JKY4@@&V?*RUc02*&Pvl{(_3LS_vsTqPPl?_H0aooR{)|xL*wzw|34uedzpvLw- zQ=U|>%Q1oJuHCs0{U<6?nvR|rYS7m*PmaBRehy%NAC@YoDrP)t0*>BOU2-(}^ueNmxYU85@TMzT9-j{#KVkVCYxoRSqM7~}{{XAyUobQ@aC4v- zs`+vNWkWzZj~suV0~gMNKs47n;iD}?CZT}No&;AD(aB_G@{$LsOsk*9zsCTmU;(b} z`2pY)0$05NVw{j|_PMB6V zGV6+IxKM>1184>b-e?twtp;FJEe;m? z_Y&ct_hZ^gA!y+3p6`Zj{G|34E&xb=pgz(R5NkjNShY(#bIDuWyl-*!G45Mf)kfLl zl<7)?LUG7j*@%+H)_3~;LH6t@l-`l<(9{vB>4C^w@lajyuIpuaXK+Y)p_)1ixL$;A z>z-J6{V6}C@({yy6mYRdYkRoFu|^yDwv+f`NchWJs~dTvR04nclaf5geKf8t2KLx?=1fAe{8%p{1Cmr zCk4v)x3T=}cN-Uh?uadaUO27gtZ6eXDV=GK3%ENh$Hn%KeDJQE*K^phk@+TS$2zDRlu3|dk*+Lb<2g=7 zAL^6;08s9Gj$&D7k#0j&ahy!j9jRo7rBMw90Mi#XaM?>Qa9pfk)9>Jq{@tXBLdPVY zo!2$FkVn^n#R2u7xNV#q@_S*JNPWn}eHxtIP zN<j)>7?b-RBL(Rg?zzJQncG%xVPnGx>l2uMmBc&E!)Ew zqMu06uH%}T;@0u(q2=#**yR4D9mBd_I4cQW;93P^yx4rHgrS)%S$y1BA z(py$7N&%e?9`H%CgSUT5>1(+9t5s$W{{YHPRr|2FOfBY)N}fNASpJ$gU{P4*c6t^h z0$b;fN$)Ne(~Gzc64GeL)6XjTqmtB?suej3SLco%8)dn*2t*gJ7y<4`ZK(G|YB6Uc|nE_lu+ zAzQekr>JdbW!J=<9zuH_Gl}|EW5vf9ib-y_66`MR-%_U~G}9B_#@|uN$b?SHDvBt7 zO;Nl6%9x7gSt5=|)5y2kg83KYbxl`gXb!~lV3hzMHx1m%?G>EyY70!bmkfhSP zvn*@0C$GAIh8`5I9_3(<>SI=h?$%ls-?#Yz*1rr%cX1hr-Bo6d#Wn^R>|@ub+mA}t zUss5^ca=AQ(kzwyq#D+kTX+ZMRUkb=iV6nIe(V^l92ZJ_j~LienTOmw4t2-$j(?4{ zPs1@G$0)Qc>0SkQ@8nupq9xL%p`(g>6k5jD{)ti3CTv?kGDL|`eEl;g1G_u+dszPfu3t%byj6_wyVxbudZNjK z8Yogt0L8_R8^)~MglbHYq)RYJivE)pT}d4WTuC%`?Gnft;5`K_9ypFvoEWP5%H*ZTBy$8i7C<4D?JxX0g5%K59|zC#94BV+JbXcSi4PMz$i{;zx|4yx zU0-or-Q;^R||h@zU}qs*oY36<*A$xSk? zdSUC&_kTSztHkLw-27J$>73PVtgPKUr$Vf>I^ee-V)oHPTC`?I6s0R9jLbCq^2eoX zH6@j{sVzxr*?AhdR=qH9j~Cpx7J+?Fs7UWGE!96Z;#9B#opI|;Z#+||HEAMrB+`}K z)FbY}kC=hyft;A{8Fc6ooB4q8@WV?Nqb#ItU5u*LB7howx#P+`7SC!a3>?yc5%z0@ zc$!!yCvpM5(;y!yBD5zgMaK6Py2s1M7>eHVyIo`oKqKIC%RKQk_N18n<&}vjsamB0 z%Q3IVJWqc;t)C@hVIoC&S}paWNWMpm4FJs7OpZ97Gu$pFdw*{|##fWI`-074X9Gcjslaj#@i`{{U7QDkv+|9}411t6jxz z{;NqCYBX8`t%@P{qCOLc&+`v?!>gX z?T>VLg&}Wya^}YUmMf{HiKTx<^^eq{1Y@=T0KM;Gd%utV!+*Usx?UgM?`8Oa%l)-? zpLy}FZM&Pbxy~lweI1y;cqEbKh{o~A6@_U8vQm-A{Aq^ywYc3Z2 ztcfT=xG5C}7e3*0-r#pHyPoUvamjCe-u&kyaCq(})Tv20o(G7N^rMJo10n;1T*A{& zxND8=`G}#X<@+mwDMCj;FbL==Pn|RTFc=yz>*72J#(aaNwWTmY$SYKT>8|BCsy0ZzYKjA&LxuqZ6PRO=02)%b zJAhGMgd7U>ukOat(=Ua3;1jffa~YrBFxd~vdVDcj=stJ~MRtG5Jv9eCa5`&KmRMRIqPhM*@5XxLcB4E#oK3LjwuAt}FED#9;{IbEXkKvD*&*S?84Jlul z2TW9SP!G&t(>#>@$Hy7u0PFLnm{Na`nrEGB@co31Mq}nQ#-EA9A}+y2(30kd{aB#g zyk>LwJaxrMAoy2N@E8o?%xEiBq4R^4E0(@L zEEw0yYFpK%Dl1b}>40lm{4u_n(v|D-{fufr1o-^$2(M2aa7AcNqe|!32v8#(z)8M@ zn9j7OSg4@tHi~_?&mbyTc%Oy|(wZ96KAGSaaaw;cEtiEr#aVM&P-UOWpNS#b1V;hKQGb{rZr}BVwt^%sj)O?Rz zN9i(>!Hkq0K_Cj8Rki;B9dWcAyf8j>&&2p*v<&sDRjHLUuZ{x&v^GE!y@p3x_~(j) zO88;70>>(RD0Qw9h*YY1h@h^WG3MP?0)SMLK|m>wKjrEGsX1kaCnDs9LIDwh<^+ru z6gAY621mxeI8|j*h-#z%08XN~sGuGdrhYkMn-}jtzXiobX+SbL3TurI{{UKWtnImw zsaVP|uJP8i0DsJW%yt*OzQOT!-OKNnv0P7f@b_Jra*fAXdx`B|b4JIIw7BIY*swj4 zBFPw$h-&4YINxEt)&AEzgmc}u++D(*t?oawUDspCcefGF_Wkpo>+QR-a*&&qv$#<# zY}b{D&?Re+Y_7Q9?sK&L#Nd6t_J4@>{{XhQ?gxP8dpp0oYR1Z6R?^qMuJ2SwbIidT zn|qtM30VwEbKF}G%W^)nw-?eCzUHod6Mf72Z{@na*+|JcmQzx7&paER9_23@W{w-X zh#6##+A3!oqME6p0g1@vgul3qMI6zQaZ&nLW>+u08kJ&eN{^Nej~^cOxR4JBAytS&2JPJF|ss1P{5#VKnE=GZE*}S+}f=0ywNgmH#TbCXdN{b z8dDv&HIE#ehaS*J$VFi!Oj0L~g}CXufn4Y)P9&B~IUdOiBSjc^zM46oa@*U%s*#e0 z>5G2G=B*ai+QYWw<&t?2{bE?OexfbpiYnrnDXux+sq9Nhq)WnB=-Vw{I8|dXA{2j_onrpWLrvN5%Vb{lj*@ zW!l<*bNqbAO?D06nNFEtSzN>GSiExza3kd`^_?(Mkw*4_&)CbdYQ^?cGh0aN5$ z{BZ|1VG|A_)+-J=8<#_J%K;IOT~qSNdW`=73~~)6uooP|L~X3CAqHkvY?+Q*LW5SX zOm^j!$(=Zk7mWJ5dFe}TPnm{**;~1$;tpoHQn}%{u4?ML5?cNlD1wik>()^mykGCn}~@;f2yLNEQLU;X^Ts!Cx%`(hM#J1kXn%! zHi`gMxGX3_{%W0ZZ_Hf-#8}x_3rMV)k{#f+URKLG=~0WeJPJv1kc8vBZB_Evm_N3kYm1$eLR?B?Wg#A_2EkN}i(@^1~(d zjnf)P+^+usUg386L^jfvqV!TTJ#le%Bo?a#jT}ZWOw%!KvI%0=hcCYq$}a3U$t>Xg zX*pG3T)8Y72>O~DAM?kkC1Qu(@$l=ppH;I2RX%-p*}?DdARjE=fLm3RA|t zFa$^Iq7i_${{WhcwyH}WxRs%>dwJBA5wK#cHr0ex@ieX^h@)D{vJ(`8Z~=kcP&^O( zFefo2w=hE0Pf@o`8k!b~LCob-xMPLoCgY2wQiqbxX05#K6A3?2&+_G3WBR418nOLb zEy_m3%N>}RPUcPDl}OOyKdZO3lYLnvLWWJPU93%V&&LUM#LQamSvIwyUn|t)s7878 z6#H>*w{RON?WFYNM-&D{sD0(Se00ZR_xEM@2PN9~9kp?2y~n~d#!e%-IQw~O{)|AK zk`q!`^wS)dF~ab?Ts+q)cPzZ8C@f-&k-JA~kY=S#E3aHdEwfqjjdIMBaonsPt*of) zoNY?bkx&SwF(+bnf47|dr*&S*Je=PrX(X0>Uj(le1h&_~Ya~RS**2dXNo6;Bb@NbXi7)OKvMp_qy*qBz=H;`xouXsAZNP_f zBOM5)A-wL-U=5EoF>k5oyB_AYmm5EtTN<$kFhgw>#Pe{x$8>Pkk}26{Z6${82-xkX zsEP@CWIVulW0AX;`%1O8jyT~cmqy-D>yw{XX&;yjUUuhT`}ORf z_V?Q5$zN}~N1x?Ay5xAO)ml2)*jqg$r*@4cN}Myb=(<^!pf^8M`)=mq>^dZ z5lh`SZOu(@Aa#juAAFH9p^mHquYokkV&9Lwz2?2U#ehfJ}Fg7z8Y>l6u4P z=S_6NadtlCbKcx@RY1vcG3EPO?d!O2T=psav^jX-^BjjC+ui%ZTp?(A{?oR!nt7pR z{{ZC=1)>&QhnvZW>~8GkvWCbPTVz<^^jUIbWpEm-aBsLfhqsZVs@pSc6|dxS=UlV= zGQ&mOHX9%dM7;@LRf|(*st|!JG#SnrvpHr(ei(~~?)zu6ie(MMcU^)H zQV>OAH0 zYEDMz~hA7GTkLCuyeiQ;}Rm!<$bIS}rz|qRnoM<2R+%>=YXLQ)2S7%D8_|WiU2eQq?!TtYlflChIH$K zPf~Tj+O=h4m->SW9Po3kXlwC0j31Hx*r^rB^sm{90rvg4+BN(&#^xJ<=nV~Pk;d&! z2x|WTrka2{;4-aMz^Na~Ms&a#V?8h^bm^HoX`TrT$S|3g?!DVW=5&p*5$DM*wvLKaO|} zN>?w3&j(F9{O||}Dc~vB zMgS(i9X|n#oQY|+w5ZwsnjF5|4=$Q#N)l>x#X!t>=a|9XNvI)dolP{x>Jjid(_DCw zpUdNg29%{fXXApVfKx`xQH`}3ie!Eiz#8=j?ZrC$yhd5$L-%5&zJL)ym)qg?VzkH) z-JS~O0oI*Ih7XjJT#ryb+-FS&q~-Sh%8{2tP<~lsLPz<0u|trkIes<8Mw$v@g-7Ke znrBadI0`k-K}rGezz7LpT^fOhP;pQW%GGPH#-sOPL(-J{bj4}=@sY(kjPPlZ6tCOy z#ZKc-3Yz0aA7%qM+zx{}5=K=xI~0~?B$J;_2|X~(00mh=(Y&l^C^Y+U7;1I=x#g+G zOKSfu}t$cr;pi+ z6|c|Xl`)iPq=E?pIvlXI`0L|QiXAkr3f7>|em!xwPY;F-LWTyF+yOQ5rad4(Q+;}A zO4D(|nuFu>28OuGgw}urcF^Vn0ZewcJI_ZnoxRF;r)~Ci?aVgwUAG&^_a7nsFNm9v zw}niQLS_^)v}HvDF#|k%%l)hS`|sB+!FEq;_RleW?jLJk@EorKl1a;1dVCKl?ccfG zu`T__F)T&Ib8yFvwHv&zD$^X7_WCS)hqkV}PlNC7=JsRW4hw~|;6L)-N0+p2R>O?$ zm%Vm2_XbE&rMYK5txjc)F)u&dwzhn&rS~g;XLmmzYYm;{u;Z@HrM&l&3IbUIn$t1! z!DWQp@Q)dm-zd?{Oa{n;H;2II)Z*s*g^b*UnEiKeT&rAJU)ZvSXeUy2pr9aSki?va z2{iEW$PAPA^n??^a${R)nWH@uQ<)~Xis4aWQD+7g0xSlLova}q`gzUDCB_M$WhN@dUsg67A)p;HU?puiaf;*OHiMajE z6keQ0p!ktYv2=Z&n&X7#Y;Ol~_rD8ccLjxyJ;B+2v@kocqI$(@&T_6t92frpv{$s8 z&uM!V%-#0S31+>uG}l~Gza4oa*>YU8*2u6uylp1?krFeN9z}4VhmYxhr}vPMgm~jrj^@_wV~{mQ%y;eN{iVkHd)a&?=N-ZD zJoTK4@NxWK3%1D_R^5PUuAL08F|Ouw;ekJ^w}rK{iQqAyTbtG?Rh{Jk5?f#M#LsKT z2w<~kc?HBzRrLTZB$d?H?b8=`T&22P*|np`Z*6W0pH^W&0-iX|B))~JCY}*PE3>dAQh-zpcNCmYr)g(@YwO+K+bbS7(v2u0 zwOVa6WC5YAdSlo1B4wSLIbyh|yjY8vo?@nqG$dCS?P`j3!vc9Ej zpW};k;ucrEf)|^ZznU{7a7a^nSect92(`#yLvTnvD<3Cw8$JI3DI+0L)Jj05>tEDW!2gH8&q0Y4**nye(??(Y3!Ja_!`b7^08^ zRykA+ege4SmzFbnmkJw=wYYFSRi$HTivIv9$bqgRwzlJ~@9(F$YDCI@irng`B{~t2 ztue`1@E4JDee2xU6J2*5#jHzpZ^OYfrKEVTAO4D}ov5{Am1r>yg~vS`@-6AdeKJ<= z8)!DwVF{{Sf|G@9+yen_fngToUW9x1tgR?IuxcONBlBwTkNG>Qw!YZl$RKshP+Pzu)` zoR&P5z0`6_l5;+SO~u{40S2N*`zi)h%f>nGtkaeg?v6HSn4k)OGP;TZ@Futxmp3hG zYp^HNqHJh3S7=!DHP1h@t|PFtxV65pd*y;AxQW0ajTt0kkTm}QEM31P5Vl2%Gj5hB zHanh@IXizSI%kNC7V87W7%y`;8;p4sO|;MqiNsNhsA6JN8KHO~|Be79_Sd3Pj|G?LxkcTK$fy};HT@$$vZs>R(_ zck#cZwww_%OUMmU1Iepac{xVcU%+s^oYG&yACxqcEWMsnta#3FY&4;oVu z&F&Qa`NnbAB!gqxTrH0y$=pd27B=K^*EZ0`O9joCif%f9Fp0+dci+6d%$FXR{{ZNR z&i0QDYzYhxA$M)SPdxNmoPnl!;W(A=uX8vq-msQQt$SnI+xZGW*D8H@o0H`|SlI|C zZ6YOg*BpQTF~IZod@aA}$og5Qq5`b*1Yh_XxO+vdrtDt#xMAeq4XdA~xM8cR zvXjwp-MQ`uZ1)#z^I}Kc+%Wo=Tw*Ayi7w92u`ZQbn&Qj7dvB8GF1sE{2h;aI0dpnK z4pcTtac>c2h#Y|u05Ki6y}g}dGRlTA?hk6jYrcP&Vx+N@Myjb@8|TmCPa~$*}I;E0E!C=7!*b46w(3h{aFx z(R0IFr0`8jl`u>kIMSn_`*9pHCdF0^^l(U4HLfP%FWM>XE`rAwsKf{*eKv*^uTXQ- z4`~Q#Z6|*rEEK6HEu{YdF!30&y!TVE6Wl*)x!wGKFT%GaZ)3{vyp-{J7Jb#i@-mPk zn$kifA;<&`aodphj}2q)zxJ2!7dd;$aeTKU1>D`??N4zT$?fZiuJ%n8>~Ti0Y~Gvq zsTvW5z2W`k2Ng7-A3Oo5KMIU0Wk%gXKl*?9 z;)gT;03i%M)0PDVYAc2WP*RzEai`*NrA0dRI0GS_e60NYNr6v2#Spo5^!n(6oB zWidL8^ ztOyjXI{4CRe< zDyL!Axn=PG0CqCXK%geI6zDNfKYlVBo1p&yh6=KQAO^YDJv8`Wd5O$dDpT&pzb}P+ zF|9>ta{KY8`1^6`#;)L!UB!2SLrOP6(+$0-6^wOK2;2ooA1@qbr^ghj#T2i`y$8r* zriQ=2PPorAsi#kd9elp4EfDU_N5Z%QMj+OV{{YOb-~i}+Z~+USTF{Y9u)`ytQC$2% zIIoRrFcP^ju6Yw*kM62I7tvm;m z{`_PCp{b|@8g!w$)_~+|kQw0BbNQScw9Dt7GNxcxs1!YEfPl3C6wHBK#WKJ& zq0c`YF5_AX0BNObjDY$5xTw?$j}3a_tZUO>!z>a$8FcW-bj1nImHnFZzyz&x+oeg@ zUxhNnn}rB80F8e9R+R%fc+)yyzo)c5h<}qkO*OzEYeV;(@up);^~Rr$I(_E{Xw26! z(xQZewBz|6+q{O_ z;rTh~2%7dm>7tNzayHh6l*%sRuE1=Hxw<_Y;qggqC}$YX>HH{p}M7 z9mL{T!F11SDbF9;w{P&?`~LuITqiI6zTNXYH@sZOdv>3;T$g^_$J`uL{{Yahy~O1s zkNTbPVDT9eXZ+N?9acgmO>P)wWC#KTp%qj(Q!`Y?E$0fW+>BZB2nyO@} zk<4kP2Rp}-CXK{*mgT)D=i1dR0}??~)MEPlvaI}(iRQF~s8b|ymCb2NYg+x7i;|w* zVuz3dNER)@9Z9Z4)5AQ^a9qkLuFy(N@~l9fByDN(ECcwOq3_ z%NL2l@wfLD{A6&$%knl*FVpdFA@WG>%_%`wGuS9TV%0sjDd+uhy5a!E5oaUCRt z%CTl?PcS1LwcQ-Y9{e|bcJ-L!Te`Tdw!^wh#4Y5t<*nfIvalpIdJ~Rs++Irc;7dzp ziu1h{vGE?(^0#;P{qxUBB6A$ok0~-0zHVJqMIT<3X^vZ%>|R26A>-}tuAsJYSmlXO zyIZVhB81cfsIDUC`0Fm-c%9l6O`n95IZX})=$Ma%Nz+>4&Zf;c^# z?5}b;4l}a-wf7zy-b;<`PA=ZkA#*Qaa@VXbYjVa(VTLp5?jzCwE;t?);O|Z|wt2hG zJt6HllIOXvudSrJ56kzTF>`QQSh%h|&1ReOp`whdi{9S1?*2o$Wtv!r4sP1s;_7=T z>06aC0I|gl0>X?dL)N(Gdk41%6LQ_3VQI?qPkP&ZNVyeew_BH!D3<2cyu=ZB282=?(ap=LXC9(odLYg|Ei zW4o}sj!#pXc+=FlxZ2c0AQ=GXKn82};@gP&2;SQM)o){hBnc#Tajxf{YUx3lTwih4 zoTLq7ad=wF#@f?wB{Q0>CpwkhYEOnCc&(<7o4&CtZN6~tBWtv0O>k%7{)30%-;5UAeC~m+AhdYcG+cB+NQ_A5 zPN3A~grB*29{TqWxyxJs07>9xv2HJg<8Nhcmh(=+Nhq+X{OqJPG|v}zy#3@~Tf<8o z)xG;8L2Qc3893xLV$3VFZ69F8=WzEM+i`udDoD{;g?V|4%ZXwp2CPJ-@UAZSF4el_d%EL@xVpNQ zYa3HKI}IhusDx7w@9o_Dj4l*W=(S&zRVc}E$hbB zkiCDHROjL2hPr~joFuIj(C)1=Zvmw+g8s%?E|`^#{ZOCnx)04=2*i?dGuWGH=5)H_>@8N_OQ?5lM!j_da~`#>9>cS_ zsrwG#sfyu6kJa(F!Hq zVXb>7v+TI88r9k;1;-Y#+!)4VaGiZZJ_MY72LZ=V9m!G=V6aGRZ|ZsDMgg?^wEqA(g|`zcPb6Y7cgtMM$y^dnWki53dK@ek zTs$`LMhb+6G>>!W(rsau?MTGly^EP0s$XjohNKEEgi|cA&;1_WXxX)r0OxSjQM9fS z3p-nm0`@q;^*HAdB<)%eAgLa205I?UDqd12Dr9wyT5Qfo)IpK2hIk{4NY^! zg~KFUPUL9PMqmga5^&wO-PfPiVzjma*6tfyskvB)qeksW`poeWkrip4sHu zLEQe@cM*>7wE2s92{#s>(5?ZAOJ)Paionx8nB=>Y+UVl!KJJ9v-gkeqoaWVzE7S$< zr|mnL1tzrKUQ|{5fr3*B&wG8#U3RB%TA??5Q!B?|%0PsZX?e!USlcZ_FJF!$hT8he z*$;m?7cD)HCaY=f*SU8)asz^_q_GkR(BjhLlegRMKVw@-#d4Qkp4mBGG7(F8Bv1zD+9|m7$Mh~* z2cL*`Z_%9&`)4jks|jqu2cW^YvzO{@+DZvf77?or!Z}%MnCo13Pj1anmp1WT+dXO) zSkfJ6C zqGpZg;batGDT{7f`^f&?f3*+qKtrWFjz*B zv=Nc9X&G(=TzE34mAWHw;xs7{Figz{{U{Y_obE1m$?>wrNQ^rpSIoHThyPl`npgcFYUzUMn*`*Zhaw>W0vC9%8Uy_Vv8BfB{p&SGVf^N!~%W^-$9L~XgGiT4^3 zG0b+SV|!0!?bkEgiwk!Yd{wQKmW7<{W?Sw&5wrq!REmu2jr+F-0)?2CAfG6&U*C+W zPMM7{@dKuPJ{Tl{LqIy3_16)~)Tf&vT>vYg`QZG?A0lco`k?x4RW$x=vr+bV;~=O8 zKrM8o2DlAh$_8INI3Sbd(w`gA`L#nif5*y_kPL&Q{j&$ zWPWVxL4avh+yTrFo(&hF>VJ*}MMqJE^iJHGq||i(0FDhtl>M7dqb)L~mHz-|&Ivl? zdSl8q)5M(q{0f}@+<8yW{2Wj0iGDs5y{4ItrXAu502>2o)zx zW%9rP8dTH7DiPhRFSj=)EopY{M)({pT}Gp=0E1o3IG8}*QG{% zab3ZQ{GodE%ws^`AK+>**$NR)5)Plc1XuEk=c&u40qI>n&1--Visf3;l>Yz}#dHHR zm;10wlja*hTJEiS5%a)Y#Z5qCxax7AH)5fX^pkhEN!9IyQyEZ z09QYTSd)L6fUea!)}2r9!i}U68q;oiYE61$I_*q>r2zuJ#<-wwFM-m&HN^twK~tG0 zrEtKE&1g+>si>wANeZCma?tCB2u9E6eaJKj>0u!!e#H8kH2FG_87IRwGX_@{?UPpui+k;D2GF z14U|*b?_qsqiTRb1L5(;N8nF^(*R^EktaOqs2HFqsF4FOWF*$5H5Bl} zfK5x1bFO17z8PYqR-Yq*`GE$an$&5gGdg5R{wEvA%Tbuq?ZS|9HKhRQjLv^A^2JU- z5CsNj#G2vNo1iPqP&vc8|P|vkVG^2P%=Ntu)3}KP>RaF+g<$50-df5l9LH z5~8EvD}tLBG8hmDr%j;XfkCiT)aOi9Yfp|1Ne4O#kJ(Qg{{Y1>M^Bc&f9y2Z1RTzP z?iD!RPSP|VEc)Yg#-#OJx^=+|r&4}61e(x-GbgTi&pLcOGp2Y?^%gY)O{1weKO=xL zu66Oi0-5FV9}Gauz!4cue;kXKV00 z{NZUJ8`{#|qE#R!d1Ht7E55(A?!^ApJ^r-frsI37g7)^=IGY~iS%Ixc2OFe<>dl!+UFjrj!1+KMv#t}45>ByzP|GMRq*rMmp12Ut zG4%_B=WOK)bB$Q_II`oQh+8)s1Ss+Cp>|=BQJ#J}VMiqqk{H|82qlFJXp99 zTU%VqxH}r#Aqoi-pv(^pa&h*x+?)9eM6vFU*PgKO-P^^+yQLPg=3%&0sfs~SUYN4? zBifGcz3)$KuI(=^`;)QxTf-l4b|)qfz4e^YTs37yUEYZZmCKFsYcW3u6AwvTK!e4h;g8)GfB+$)kBr42E| zdt>hXmjfplc=^Bj4tvAMTU~PfmwRn@9XRQEo*GHbyWLQ#0P+A1MF__+ZQoLMmpvaN z#y2I~@$p^1#l^yMGEZ_twcIv_HB#)MLZJwXKqtof*NNgUJ*S_!IOut9PCM8?se5yO zHLP^FdE>ZI0&mQzT5b5#65ICg1Ka$k4+)KJZ@X`}EU)Ih5(r~$d+r{m8k z1J7eS#_>rRC1^^JI^BC&-z)VOw)ZEL<1Zz$Son_b?CuH$F)$soK|s4C|o_1ypNK;ySUurk>cWQ_YAR) zrDzan4RPI%+pTRsF?n-u#@lkdg-jOKmegjnfmEL^(B5U$s0{|Q-1AneYj6K~o#BcrnHF1He&V`yFj)lh3n-(nnW{x-et44Wm*b|RL~X4( z2xEBdZe7g<)zgfqe>tujk)M)obCbJ`{)QWL`g&Qh+>0?Hqzwf+Vm9szyPMc2c|L=* z_@pm9yn->JU0nbJDQxzgbU3^B0L=yOHORm{Uf8*XpNQkH;~tfzlG-k6)S{D+G_D=3 zxZW?e`PpC-a}(Y)H#YV$f&TzX%FyOe4C>lqWwzyE=i9aeIJva3M&qw#mQu01E{0?| zQiKvQCmRQ7^WELujPQR|UQj)xNh=CSw)SwuO}cI*B>~;zi|*sT_KUS!yLm-s?Vc;@ zC*(Q%dt1rY-ELme^vWY>nD2H#i?={A{XBDj_`DY!UA6xJcXw~4A01)FST(YJWEKSE z(y=UwO)^c6Q#Ew!^^lVtl^n`Oo?R``Qf;!xhS~4M~zVw zy|cni8(m6w4cB+_@ys4NiUm^MY{96_cRj6>bWjxGYwje z1uU+nVd?u zo9`^%NZ@yo3d*rCqdwo}1aUpq+$z*n{!mW$UxXgK5eT{yAn$nNThHJ0sv-EJk%@v#ERlb8JP@Xg(yfT z`M=_DB8>_(N7hD0028*9q1VLWRW`8PsV9^lE`Dve1!Z&$caVc^ML#K}57~wyja}o- zU2B;GGEYk2wXgz7LE1^&Yf9$5MiM)hr=rTzdL=w%}T>@@Zc%YzPixHk4TZJ+JzF8zkr~u@E zoI6QtCeZm1yBTO|SqaCh>4_#~YHwOlR=HR4!$m!g<#M1gKu^FBbsro_^zfi1Ng#p% zR?3;>*D7JXSc&sdB+8y&$_CL|d@)N~nPRW=DuzF=xup1pIe%@w-=z=4~TomfARp zqDZmr1qmj*fv>{=^N~mY0F+ra-|CjJEO7E@Kc3)F0N`pxemF>PZL2g6WwpFY!*OXk z=N!;Zlw~S&3=UZ1`2OzYzw+5QDAvP>=dYxdIoLSiO7Wb8;QqR*fEDE2X^s=QqOk_v zDU#orNYu1f97W58xfcp+x#JtCP9W|cE=IoXPU*3ap5QN(28klR9-y-_N04M zffZJLsXPuhMn^_fIR-V?78Ahiv5D3@qzB715_w8Lh!w3cc>a3kZaa$Q;4`)Tx-^#X zLv@ylxIXypV9jsH@wNtct*peuH>?&^Dyxn& zw7u!>jx+ne?k?PP;;rsGU;9Jt_qdyafs)qOwj$%bu;IR%Kq{gqk|$zL@=IbbaKHLj zeWC9!_R+`k*Y>O4zR!Pkyk|4Te{TKf!d$q1M~2~hf4=Qw=d5G63%Cm@stF#DRRMAR z$^QUjf8O^y+aK9q_ZQlp`g^e6-|xD%PhkDPJik2rrI!oA^76EjCD8hWOEr?{#)=3L z1B;FWy?7_&dpDJ5zU(gh;$1JhPrLYLVqv`Cx!Yp!O|~th#2<2sk}=vn`)|h%PmS#! z;NrQ?;^5H|XXU$o+I4PL*=?GuDfIVJH_47hmB)1U-+4&B=YMa%-1la4UDfP=y*w{@ zcNYiRa`Sice^h^O{mZwqx`NWjyf{mv^!$ClG_bP(q9$N*%k!She|tXscK5i~Za5B8 zwf*hjI6JAVd7x<<&b$WUoP}UZ00F}SXOa@>^ zTUMEw5r&{gP6>Nuz zrd8{T4W^X-Y{=85GSjDA5-C7MP8crQus_OcL8f?8H!~;|-TcGD<${c-T9LGnDmq{t zq*atqihb0*h(!h$s-w9t-} zKV}MsB}f^aemHHU3eYJT>T}59cI!;ofl_)CU3_qecI|cZy)%r1n(XQb!9*${(RO4hU> z{fA6#sQ?9`%vaz~@x?S+YAaFK{rF&~5k%biuDt zkRQC)3Zwx+)9pXQ8664#0FDCx0DyYn<&|>75xxKfCs7e)EqjRg{VY(zyb0 zMme!0W;N7|5dAd%8evu(*LGE)YF98$m>E?3X_ZBKVzY)cUGeN~qy)`Z;toBp_uI9- zn&iFx{?|LNin-!_!k4n_?e9Cgu)E6Eea&?xoIpF1uj>LIs{jr=U%w;v-}{pG-`pP1 zpNF{ZiGT5)-R+rs>z(?LoCg8ObGH1`%V9Ot@yQRVExDv?O`BL_i{|~n{?%djQ;_F5 zu1~nQzU8^@K0CbmUgxvsFCP7+xa4?<#4!3%$P!dC9jr6Or2G+*F(h%D<}rlyE;rIh z2G7l%O;3o$jqRin8EuS9D>@{Na@jG6L760)n)KHTZkBLcax!e5I};k0iFWNk?~>Jy zbQz2<3&~r>X)Gk&6Ja8J{kK?6X@(BTG5LRzMf#sBs z$~5sf;>;Gjw0*-IV&Z6_P|@)z59&z`%LRnY1;Q^d0Aa@a>2$6 zTbERczJ&yMexh%VW@Z6i%?|#D;HXPfn2lUP+%Z`_K)6XOA ziMK+|=knB?b3WPoiaVFOI4^TJi@rnK%kJTujOIBGH<{z5OUUoIn^cF_w7Qxhw6t_F z=jP8GUu$vP2mVvvAJf*(3tP9mv*P*AU!465o1SBdxbjbT8EvpL=Up*n{hWVsJb$+x zhuOD1ggx!;F8Jl1!v62KyK9#J0OthbFRp7NL3qd=W4Dk7L$r?p%zIzMCER_pZQEVh z!S;tG-!?)%N$mNnVwP4AU2>5}J!4_&TgK7`3PBr&JAc}q`Qz==+&;;U=F5_^?)%yK zKHu-I1_AnT+RMv3#3p5;7{CR;mmEY>`w2g{b9;#dmmD`M#CJa>X~J=R!F4=_-p1++ zDcTDYaufdDWDA?E23Y6s`+uA7UvB-*?4Cj%8=mcc^0t}Y4&b$}sXJ^N4!&>(T9djMK#V2sd#fSyUff~!TixkOgK}>hIYxd3m06E_M*+29? z(7EnAy4=}t*Rw>Hyq6KfOv7?PYrD)B^EXqD()RSbTy~dZw;wM*cXYCSM>oteE&bHd zQ#OenO`^I`{Bdu}!vs)SO8~L$(5xhs+ket&Btw`q%q=pmEu-YEu5aMuLrEtc5;XSr zcQ*)S608x?R2q51kyGQ2N0Xn5v*IkEnosJM@~5MIw)}ic4WcZGUHy z@DbnrU`SP->z9@WEUE#_1sRetdE7m($k{~PrxYxAT*Kp`Sw@JG_E1Pv#{m)-ZBvkyl)Q~Xm~4Iz|_CFp74>rNnGe@O(~W*!QGsRwmgf& z$MIdY#M>LIXy9?B$%4C_(0*+(6`hwKZ^v?${{T|9u+2W}+uJ`(>}67XzL@hcACzHx zBel4WKbht2C!3M(9_-{Hx7oy9OiYm4$9*Ds<^X^wXa=;$Cm%IEq+FCYNqchE_b^;@ zT(2Q)`=XEOm5B&j^0JByU2+_Q^L#Hm%<<9jA5lH6&}O;1a+1dty*4!#pp`Z2jy`?@ zZdZliV!VkRZd&37=57>JOsK#&m9uh1D~@K$@8STxNP zMhS~!QS^zxQeUH^O4OYHy2&G#lk%|Y3DNfXo(#^MiS)fPPpZ6yKA>Q zmzcJ_c$VgMDZ+?uZdZKP_7@k-h)oCQMj>!}f46%thTw1i0Hr1GC4I{B{9WfMX>PRo zd(KakuxC{{H4&JTi)+@nzu=;o;!EyU_t0K9l8zSJaW#~bEW5J`-~e^UIcp~~4<+>0 zao$;o!Jh3QX97_T%t{fn&of$L+q-z)Zqtypd0=np6K`9RfkVFSo5^v|Y2tCna-5JY zYs*{3CApX;%SVAKu@Z)XO$Z|_MST<^DR~Kgjwv@Xucr@?#~A1sjn&qeYgw)8W4b_D zNLZZKx%K&);VgdjzC3JKDdABw|3lzaeqmkc<&LGRJ1Q1M6k#etuvt*vsIoefo*FWau=~F zkLoX(Jn2nIQ?6J^)sER{tTyJVf8;d$aXDt)1=RDw`n z>ZKKqM{I_}ok7mH4Si<=Pq?jRRslNJiQ%ZOC>Vgu0;-X=gTw5@W;ar*SXA!le#}a@ zhyLOQU915JP=FQ5>z#2VsVci9+gX9zwMd}a23n8tOiJ+?SVS{1Eda|pusL!t!n%QPg%t)r;T8xU}jjfuXCA_6cU=LD$J{a`gJeH?@HkuDN zU$|oC87)*RBL1=8b&E}>9m|0Y*8H1fZKf?lmhB+E-VW);4d>{`nX);*~jX)!&8cS*hpvlUH2o696 z6(+b?WVev+ByL3|KqnwRW3D%{GQ6>Z)HJ9%XXBQ*n~$0Z+Ro}fMp(>htm-lwNEsT| zw8htBa}bNzxG5R^I?>Nzn(mL68l>}c>7 zGWvQp!J!IADP{e>`<2@s)_b?!Ts-~n{i*W({{X}G=XHBQ?@wsn#&0{v*iQGd?rU4C zRXp{ae@VyP2$iC`P#Q(5lg0KgyPw+cZhIH*O`jVD^``-8+IAhG!g3s)vw9q)c5mB} z!m-S*4CFZ_lqp&acBg&toPTt7XE)is-^Or=o$LfWBtaqbAQ@vV{av`{l{RxBI@v1UBXA!MGB3x z1c{QQ9Cx2@+;LKIT;~JHU-A6qmFzDX8(S;CM(V8HCIG61KaM+N`@jCw{mky4alf^% z?lZAHzwcYV(^%zBZ`f~ryI$fuK74agK+-sl4qDdvAebmE2Ey4gQ`p~s`y2ag?#}q) zz1a5d!<4(Z?8WT~Ip}7B);K*rUM_ZiG(#(@nzz)*C_pMSBHr8kH1`XOwUVOWm+t=T z?5;jUQdRp7Y?gOrdw%Vhdc#R z#3%x-sXa--+Sykuhw;FxM^BS~`9hefH3uxS)8&E%Xk`SAL0Ss`0K){Tfsm~L8VntC zKkmQ*38ixs@vZ?C^BNwM&bTYIfGEI|sM55i4s^=DKZpAOj)WhTGp+$XBQyIyGM|-y zjwn39>S`(}rg#h~L9V?sKd_4aC+CdwI36G8mI$V&_lyJhQ(xFg6`-yF`P1dDE7QlO zHS5zE9Q-OVQ?JkbvHUSng4tvax@p%HDnTn%S{k?f!{Ln^Uk<()&tHzeu$;{``1HXc zveZ{G_G99D{k|Ag0;HOc9I$Glppo}{FmxFmE8&W63IRIMobmaACrrNmFaha|v6=mb zgFk@y<3oZ7>0A?*dgg0FGsQpv`2N5RMM%e7yq|wx!vxm406J+};%W6v*_#sRwmU38$NOpW>L>vF`*>fB`hr z<$_qNMx{f*D6$&ncKop3h-lY+6dI2uE1m#iTD3XU^wyZ7ui2r+PDw#Z9LHP}BNeT7 zmNXS6nI5E}Rd+0wWhnM+U_C?CH7Q$zSyJh4%Jw|Bbj*)+YLi7HDBrvq zS0Y9`LN53I)xFjJ*`WUA?V;rT>65lOUexceX1krCwu%nd_Y>KE>4L&*z8d)YZ9Lq4 zwb+U#%tWlrPYvwnYxmE%-Sh3=xBRaqf7-px?rp`NCEh`=CEvC0tl+l`8s?sxEkL2f z6ZbP#E?O`lx(WeTB&ahv6y28N*RBT{z_Q#rT!NPpzS~<{I)d|0C3cDr=07Y+ecSxq zw<#|kA&Zrk-Tio@xhwjR$DVXG8?+cQ$_ZfG)zm*4$sXy{p`Q?3+3H4(Z}7L~?fTJ!T?uILfk#!k+bh_YdB~-_p^P|?t6{{j+@-OE)`np z?#llFi&h1BiO`CyKTzww4^lc6AkbsB zd#BysbMO0~+P?hZ?7Nq}tl;hFyN8nP{$qT0J*UUs$HdJm+1RecV|0)T66MBud-3nb zx_<5ZNq_CPw3hv^+FaGIYuon?w02yFF=Qj{jyH+sWVVhjPm#5aqV(?vDQCuPveXN0;t2 z;_q!BHr>&0+BS>y_BWQc_e4i1mNfxeZoXy=O>xY(w0wR1eW}1f$K2i6cNB3BP8)^X zF1db2;@BilEUmgqdTWxMNW*e{qi;8KSlnBbzvf=4U@zdJUaWDwLXi7_9}4MeRDqUw zjvL!Mm^pjRZEgqlmlQz?$Z$BDFnB1dfNL33tT z^C%fJYg}>mHx|=!ch->>wziH(wo6%d?^!0s&Y;BtJxngd-kpvA`@VajoQe-(3f@6Ga@hQyzgx|X40q4P00 z0hXA1E)%=wv5}BVbHZCh>j$S3P4C6~7NA;NNz7PptYZ%V-pym6^&L+fU8{$q&Kx%=OO?k;B4 zmTn)5VE3yv>P7{;lI-%wTK;iQ5(XOImE<_C3yyXz&09wCO@C!>nUsb{KA2em^E6eU z$1~jAcQZE|+7|9@Z8+QA@_$s4)YM(uyHHj}{JU$oV#AT=Z*5jydU>qjwr^97f$U?H zn_DK1wPZC6%}j|j#_l@fkL^x3wmBQlI`_5sn`iYB$^`W7Ia%)=kuDXMj#L3p^&DBv z%5o`vC7jbsa`H3AyvHFlh3%pMRFwR+3R;=vOm{~q-JR#mdq2c**K1?IaGkLd{{Tbg zEhIqp{IvI^ET&DwtdS^H6a?Zy+7|uI+_TwSTR(MoEZmefw|&8JaL0HOT*oh|6?3FY zJfP6(4{XESeD@3Ow`}q=TrX|dMJ!yc_j6sO3v(oQH@7H}MIxZ8LbmlF5xY!xKN;KC zTqh&{0Fccm)N`D~mlI8D;cF}~!xTa`j6^s3W<{o>7rYlU+l#Bx{OMQ%#o0PO`5GO2`6wpC`Kff(m-Ktqv?s==`qfrYeVzH`tkaOYTE{i zRA*0)3=&2ZN+`~&=27t;J|_iZV=oygV2lN25)-(qh121KExT0!tDz(?pruW6*RBjr z898U=BoH#l>7D>EtCr5B0YE_mXv0b(EPxUQ(YO+FE<8MNWLn6kqC{BtdSsmUQ#0`J z#Lln=LgXRNqztM}27^pFlG%4LVH;AcEBQkarWuOwl803#el$N0xI3aPAsI3)LF<^H z(Bel}SIfIFq!Uy8d@xHu!CP}0`Ditu9~=^}M0(VQsn5zcL!_xcQ3Z{_Lbi^+I-FQCf4o` zc6rWDJ1fx=z{%*_L%AMC0d^9`*R(rBm*zdy=D9vi__H5makreG8^l{%H9n`5=H*|e zA{|RbBNLr5Ezhp+%tryVpOua^Nno~)tMtIHmm)~dQBkHjpLDxTE_hGwKiMAFXO~tXY?>^4%7}T%TTTN>6ND+=tO%8av;yX^oEjaGz{?vWO{^(&&KZuOq zcO?CdJWb4YG2_~Du{J{>s0<&L@cN?`^xQjJZxu->`k! z?_SliwdQUF?Z-V$%Lws2YKWu$s+GpSjozwhfkh8S90t+kTWMT-#k{ix5A?{ ztv`M!Ymf2!@u)jmrj@7!d~n$xC^Bi&q5l9Z2Lz4X7!YVRBv%y5tl4Gp$l!;Jk}4}x znXaH>g^tdn;aqMR<+W-$b--52pws?txX%oK(kuAW6~UmbLHjF%u2}rPfyO}R@c#fj zH|FyVDN$dy_+o=0o?rTf58;9r$DyVPZ}Zn&R~a}CxzU9I13iD=fJJj6xzty!G4_8P zWsSo+Yh3j?YldcB*?=l4HrG8g_+^4rkd!-Cx_FEqg-=eOJQ5Cc%g0X)49WazG{6P4 zq38%W&bd(3{Bq9}*1dHfX&7O;r!$*o2>UQ2%C4Z-MXhsYW2Okyis#c5A8rV)v^tC^ zCqtMzW-_KJN>ZO~bHgzvxojgGd~r(D)5GVDDm6b~t`+G@*R4nUIrw1E zX`kVbnft$G03SMj@7OC^XQn+m`D?p%GuuoWk`F=Iw6EV>4Yg3U8)yxFCp;1u0DzYO5Lga?iuE`{<_I!GKgyz`KaK&RWBkO7sxXSg zkxCYzG|rS5003!G;6m3?{`@x39A{F0fyO}YJxMuy1~NNFSx|qL2n(OjI@iY(AdQ(4 zX}2sIWwiCkk`L~}s9oFj~NQef1DbARs-XP4t5#>xmV9k1#k?NVb+H~!vrl<=0#b}cADZM z@zAJ0rCJ51k`JF#k1)^>q-Rzdf=}Oo1$Ov#KMxFKJgb=I@{{Sa{Ct&-d!0=tw+1=sIDlB$f746q` zB(QN}b`B%Chtp+eCZOZ7z5C#}KK1OYUSpO$-E-~#01e+f*MIwA*<3FtB{}Xpw0O(= ztEesQ;=6e+BWr2~S}J)!82*pL+)dnf6UvuX{InBCEWET=(MQOw?C>v{9+DL?m8$Ha z6~(u0+i=lMac^jl%?qN&(F&9G0%MVSW2MqZbvo@y4AIYG?skG}=GvrHt6W;!@a3dpOI?{Lh$)sF0xh9J`h{sIaj!x%`Qk>fDAL423kf8321Z)?qDAqgK6UtF#_y1wxXZs%6}L!k z=i}K~N1Ej?2~e!M=b7=xAqRVLJdb?cODdbLQ;om1HXL~ySsLbOhw8adYfqWO`?1Ga z@*Ft&)5Z4fhZyRy-cKml3d|T0MI_X#b;}9P`;+eEvrWo&*E!yt7ahez$a8#Ew;gU` zX}EAX2%)(bKdh~As|Y}8N{sQ(_g}LezjNM~-r#VS9g)Q+ciq2ha^02Uy$ti zZ5nvumSIw|%PMUd)EbO-jkma#oL3Fn+`Z3k$u|?*Jd<+$=k8}~+^WTQ#q!+M!n(;c zGLYaMz|%~Bb-vm5_YK@u{^NIlwY`fvky15;TdXVT z#H_o67p6!1Gwz$Na>CEI`%C+3;rrX%3(2n553aw4=58O8KsH%{4$GY54y_bA98^eEUp66V09?pA~ER%)fc;9I|%eG!#KZILx zTTV^vc@|ZYX%{6M0M{3(!rWf*Tqi4caywJK<*>GvPjzf&Mf#NIc==GQa0m51o=_Bp z$w;m*J8A4kY4IJk3`;e?4{IMAXUSY_-8`^gMhC0C$tTQBPPn(??CjTx<82`h(mco4 zyn&SM5=`D=#0rox$8#2{jceNmTV&%Vi5%bZlORiZqBNN9qR|kTtFzNO>MZ zk-xmXA5AnVDUMsB^?QBNW+6Z*PLvq2J zZ}PH=$hR{O(qR}RdC!!1V!{_|&hhTfVl=qtZQA-sAmOB#5Vuf8H6uQoR4S!S1_R!` z(pVlV`gPs)rMaHw;`FB5duS<{dQen##f`r)+f4reUv#3$V7V6@?=qLib)(DbP(`5tCM=dFMfhk0&=MmT7GBTitOmqHx#O zaw#IlSy78dYf1t({Bdi49lhLA@tmd2lkhX%nC)-4$qgMZ_kLz1&!6Ou zZN~C4GXU{}QWi7>iUW=n?0vcJ9y{CK+Ll=CY*Ascm2gk=}FEf4w&BZbD2sq18P}4S3N%5t4S^IyP7!^ zrJOdhMmHjoKTyzlX%xN0FU&>F* z5!uA@1Y;;$SC4A~rixupN|FG^`{>CDJdH^#RBabZ`i6vPH%x=(t2(58$seNztGfXx>%B%+C zLS;^-y$%NzaSGYnMpa&Dqf~#WP$?o1Kbxi-VjF;`Fw94&DPJ<4zBr65UYj$af%enV zu8hEbHR<-@q+lnNhaAI*x{&D~TkK z7Ai-n>C`Yr$td4|&Y1OXE&Vtu2zL~bm9r(Au2j@vggGMsRpZPu(@@`r8rjganTogO zsLr|R^TdbLV8mBx>sphvpPm^OJ<1qAs=zQx15x9FaD)*R3w(Upus=TxGDrnv47-0Z zq0{Zchyj%!cc@6I+(v?#Vp(nAX%!A@8iEzsstGmfd~uu#vQ$;Zn_D6X_+nWV7DNCx zK-YN4flN1$YzzmcBQ-gAp9~p{C?HjDS zjZ~nh2AGr5R(0A)pfq8qC=bgz;mI3nNo8R{8xu+qjm+CfY7L^aI#)_3>PTV zdc*W6O%zUPnoyekhZlAYJ-q9C0J?%(5*pgw00fx-U(d>+Vg z{q6m$arU>}z1)_b(OE~^9nIJmox^H)Xglh7ElGYx(%4RCys>anWm2Gq*W8{Lh2S}# zb-SO~t`6$Q`*r^51=a2Mw*0SeTXItO2LbJk&t>v$duI3iTrTa(%3%^sRZ6H#y}9P# z{@?wf?%!wm{wgbJJEPfd7ykem?+aFrR^$0TN4P9)=aSt7XK83-Na50>7rljX+x@v^-IkoKj5B1WOQ~eH67CxnLdT{Xyf2nJuH}9? z_^J7avrnzMhGq=W&v8x8r(F8hxS-iebf#J2gKbVjDlf*E8Mycc`#f+Bb^CEj*9s|0 znij1^I#qBLLnCV;1b($L6zC7Y(;1R}X0!u9K3@zYwqZy)=cXFJl$z5#zE#Ix!vF$7 zu2s;S9od}}n(O2H^~G?)gUl(P&!#k{H+~p2>-z<2a`Px46+Sur{{XbpuDGGbS&#IC zjL0>}hCM#q423I}-3QAQ$d4STF>5mdNvk;V}LGc|afaw?%O{aLJLjM3VpA()pR0$0z z1rCO~;EGU%Cc0NTW6sOBX;r4XdV{CL;;-cbzMAFcW0qZX#&i|X9~@J40W>>*--DyJ zf`)+5>xv4B>(-c}8YmlR$INNhEPu^PS2}z!W6yCO2Rdhw@W5K8+SIWIr0baZ;-_!S zY4+d)LriC&B9zzTTxq(b(!DS#)2GA6rwip8XQ%HvV3qL|)9%4y2&pydG@+=$G^Tlc z#V|>(nPrv^fEw2?w-gzQ>s>R`10a5X16+EGP*s8*4Qg^U_+x9TPeK-6SE-;Kmjd~>aJ#B7cNW=J*aLCT*Ddxh*T zba7MPaenIicip@yypTyHwY1iJE8jtTx(Yv*L$H8B>yAD@V86MZMlX9mw0<^kY<3o+T0eN0Yp|*o$5WHciTQf@H{{Xvr?h&}ZaIQ?&_qO-(7^1cj zyIM^>no6oh)>jXfPq8^jPeG)OiVCQykQ1)pDTPg+Q!L@DG@r`HGVFCA z47I?OjHv{2DRn!y$|<-NQStcwnDl#=aj%frp9J4_iGZ7LJ?m8$q<9k21z1D-=PTqp=>fZL+-n<8I@`F8S^5!L_q%doIr6E#pHKwY~XZ<>LjwjxcksE&YS`=a{_Yy`%0FucGFH+GRg9@gSpNUJ2gNVqYr<4aW7 z;gpQVJAaqqIL<4C?Adq=J~xeiCi)ATI3QAGGBCC!Auy{5cBuz)n&*hPk#l=_z}O^8 z2O%3I+=n42o-qRo7L7ovusF1~fdhJyeKUGQE2>6=)D2TmR++VaS!1|9p4w@wxapZV z+o4tl?MIpv=vukd)|j=w?n4BBMaY53E&Fe{xVKE=f-#*~8Z|x`~;pAt~nlB+7WqVCI^r8 zWCHr}oq?8lWI2>!RvkuIySw9V;lIDQxRPE%>RL#6xaAvCD0fz@fa{Jv*NNjVFSuE+ zqyGTNuWaLKFE7xNXOc3xlmu2%I%AHv<9SOyKaYY_$#OTZZzQ~eSgQw`$|=iItSYBn zf~J_2_*rc4qq?}Y{+2mARsNaUd|vREJl7!74^rlLH8IeEjvnjV>2ABTz7vmCYfeUK zExTrGlW(>w*otluEQYUCG#H1D<)Cim<*hkNbKaffMaNr}a$AqFR6m#fVEAe)fz0!l zlbb78AG0O55$Bp9qa`Ns-ZF#S)ob!`SZstq5;eA5azjj`l!_|8AmREl-ByGD4x9%kAq4tVXm+wS+}V&UxUWtn;Sc}cA=IlHZtonat^ zc*zH10i^&Ki=6hmfkSmQ{{SG?jmmOYmr+eUvu!-Z=M6N*F(zff^r}eDJaL?_{zCjU z!Q)ojy8Djyc;R~kBd7HSEpdgS&gA-Zse4I(y zc2LYN`D=Sc;$-En9#Y@@+VY}E!A8*|qIt3;j9%JsQgJ*T zNnOvJf=KbewwJ!&lkcwYk-ZLUp5-kiytJAdXN)Dafo-wdtZeG38j7(~t}SEkZXb{5 z@A&wjnqFI*&-ILVy_*)B*)@%lBt?anEV> z3M@EyCuNGt8;H-I8#t7PdV#vDsK)_iYP@uO7bAHeF*VhU5(xzN3L&_L_exwG+z8pc>M+x#c3cx4N?8%iHr4M(Dw=!2)Y!QKb)*VWhQ^ zNiME#LaZ?c2#Q4ysZRl1Ub95ewUZdGW`u@oqQCn^ML`)Hjs&*+bEK$aiaRihMZ`tD zHj$~N271t94nD(`xPr@=v6|pnqHadJh~|usn5TlD;f4!&8w4fh3W~DttyxF$!o_UT zK_sG2`|~YtAaHj2k&gjSFNPsW<+$W;tMKHW2VJ8N10y=P720zOKQ?%optud`7I%$K@}YHEG8FqU zb86AIs0k@>poNVqLN^|?&k}${sYXy(XI2y>H;#C?i5bF4TIO@Em8XSiT494o@}*4_ zA$oNcu4m_f8i)rmoREfW}-B>Y#Z*{{So&cveK)n!19HK+%Wr#I5LhO}Wn1@+bwe3c3M_7{n>5 zV4|hESNPzgupj6&BWV;gBT9dOMm~}o)`eX{vnc>b7ykfmtzC2##5R*~_8fWbr2Qwf zslVjW=nS_&lczvF{3kzQY4tW!K+)c#F7}0~Kx9p8pWBPm#sMABXO?(q6)H_?2aRj+ z!dc&0MJ#hZ!y~9JyNxQWC_(9q4sV3+>psop_=&9fPTb@<6%zP_wlYx%81FQe!i ziw6kG>;W~zVn&ULV;K>WtTU-lNHovG9h2*StqaA5nQj{MtmMdK_NYHqn+ndw;eDKsXFHOufAPP`^!g>l< z;axFKxYzK-TQl}jn9EO&E4rNk=uccWRG}dJO)Hnf3#syt9-|TveB)q6q0c`v6Z?iL zXK0~br?ByJ#kF* z>7{-rELW$;EC2%iwD9=g0x4P#EiqgXU85?~I^}?%7prSbhfbIUg$N!#IH1cLdQzCE zuDGbiel`1X>Sa;Zr%wuLfTF%`v>iq%yF=gy?#Jdhh=wA%ii6U%IATB~2F&JksG-J* zLMyU@lmj=f%;|y;<*&o3r%V7e122w$99E-lpA+MZtMRXo!xY*`KQHsaGzOzgt4zTl z_>2~=-P#dfyJ*2&%>`?x;jRU1Q{!J8I_}!clphk=06r%kqm`&?2$>G?O!|Sm03H}kwW$2rYeS|8r3uli`D@`^DyU~9)!Lb;Ah6G< z%MZ8!A0PoF45?4@#b~T{?cP8wv?op7W5I(7ST<@~j&>#5HGE_J4U1FpCP)}#V8#SKrxKjDBn5l@)K0HE7U20xc*`VDfVF3KQjO+;htn-fYzp>har#$ z3P}9LshO@{3<2X`!v?)Sjw_y7<$^^#bL*d6WYwm*d^P_7jy@v?Gnb7I-HgpY40%)3 z)6_ZEH93bLfImEDGCl`0_I=n@%mQja{q#6wxslp3Hv%%wlvv{fKMc-0_mKAYzrDHk zlFrk!xjQb>=ewd?{{UVdayC1!`3s&pNTp$Lzoi^eN}wsL<(4@A0DZY`&Hmg!wD0bx zw|GA4?QRSEm-f@#d*0~wWBY^PJ9^edyzQTMdm2Kq+jkD)WrE?P1)AV0lS8|fmi836 z=DAK=k+|j>(NY^Id0VULE;$RkIN|`L61y2t64@HpIte=;xoy^7dIyG1Gn0W8k{Cg( zf=LHm26%omi&#a(7ao(&La>!dqSgK*GlOwGs7WP^877T^iOCfk4RXWXUUG2SBrr6J zrb?H6zF78kCm%~FQXw)k5P$ZN&@+N^tvoQ7A~hD!mW}+RLnLfJGLw-b$L+N$;Czu%OFxohcTec{_G{!wEVYZa3hegHXMI5-kkRn#@P0U2JRN``rVHq zBt}Ulk8=5kT(hP*u6x@%KE%J_`#qq$=6LSFnp@7+?wiE}>uznN-trS8*VIU))X)rj z8{9qfW9^rB`+eoQy)%7QyYm%&8=t-t8{jTC$ zjJ@ReZg-I3`^%c`>Ra$Uw+F+qKh#atngo!|N(WYI=bl)w_cPmWADQEBWbWv@3){|r zwCB6wt$90kzy6!ZFZGTzse&7DBx|dQ8C26UF=NE`pL}-@y?u$!_Xlh7-t2ZSEy~@$ zZ}1)4$j+0FXR|p5WVgR~6?QamvyrO`9mvCjwdXi~M~=LMxOm%( zsYy}7_HxJz!&hIjIv(TbBmPiotCPZ4L?-M`9k(RXFadZzLD zxvxL(mfF?`*w9Yo$_kv6?IRpCz14BXUte;(Ofzvdwg?P7I?7oiSz$Sv1zMAsB#bot zb+TV_oR#U}Om>ng$+AJZPC_UF4wM;g7`NbUFYYao(8n#swYoLS+(xCYgs9(`tq9&T z`QdGEWaZ-HqS!6%ISY44Zmy$3d?*LvWBDTk(+@xhHVEz0`%7xjo25%w|LRQUO-mp#q(8b1x4U9mw%$@%0o!B8B zOG|a=I-d&SK4oRGvbeHHZ7y2g*b~l6I|cOGKjx5=wxcnNXn9WKe%a^87QCI`6~S7< zc+MA%xsPhaa?exiGS!)S;?s=n`PYz&)^ePuc5-pYAFf=ZtP4&!^p19?^f1I-Axz^=*OWUp{#_B0AxUn3s>Kj2Q^2Xr#Kz5+e8e@g-Tc2;c zf3_`nr;^?d7UBywy|llQK&yEpLRvO64C>l^adE)dS(|>~yuG=+j`rOeYfEc(l-x*p zi6hD4V0|S(Gw{n0a+h31$8dcE#|@>b+WH(<6LSiBCJsK`{Io6R71ypVc}TcBD;{9Z zw)Rk3M;cs5VL1N)t$1Oe4iuWxFODUO{(d{T?YPJzlIyhsUUEl}p8lShxiN-RWphKg zQk2ENZ+8X99oSvB#Vj{BoDUf!7VzA0@zJ2PPqFww$!QMa+DE8N&L@wp&|u2B0dRK1@j!pvBKA%KK}@TV1Rzdna~s z{ls|%<065CnMA52iJL`58IwbaW9K*}~m&{C}rkIgfHIPI~2w ziw@f7J7foO1XC8>r_XRp#B!XZzgc(OHu5py zuWjZPnXP2FA(v|%pb}0w9^Sg+p3{o5=B~MU9@)&3a|Y|p8%rjHZYQosDja=vv_dqJ z?_x4pZu=3u`eBeq!{)0~v~*&uv!VN z(aBlVp)@WkX;GFqZsmsFK2pNo8RBbL+tiznxS43u-EH{mTv9uT;JvvrOA1EBTM*7p z?@@jyuUxSpD$=_y#}Snq7%!ESlR=gt5rQrjNg=y*QqQqon;hv{AI;F?b*|E{9URbk zGTTBr9I-mhUE9keL`G!kRIi2_B!vWXw&0*t?-Uz`Bd~3PNPS7FriMvQY8yQA%LD4! zUPLSaP)60vP<@));oaAP4SdcniHziP;gqKX$Q(hYhQuHS6q5Ev6ePz<5N|WFEvzUo(*FH zX9bw36m5pI3JE76*B*?K(NTi_>JdjOGXvv^XOR^o zP1_`2=06qdTH;E8lpt=AOC*4twv4_VaUHwC1hB}&84PGlZKh_O2AEWg$YW-zyW|CR zpcrP9)D>U}G$7L|Q?JJkRZUPmYBV1c*9Ip=72Qgy%DpH){4*GlqgSTZp#@0Hu^(s{ z`hf!dG-{*KKnX6#X=VeUIpfnYO)o6OMw3Rht+*k=%BHyikT|&D?;6qzUS{4~dw7D< zb)Pfqy>%eq@ZHRkaXfl*GDXUBc8R`0YF>~_6}x0h8UjAtM{n9b>~?L#+_L>OuQ2C| zx``RcB5CDKrcV>Pg9nW6n^kTy;wX$9*AH=T$?@-QAIp6O%p1_QVCPKfj_bGfxJ}%B z-|ZJ@-g}F6b!~ZT#^2j^M+GuRad%-9vn9>#%x>Z2wuxFoMHf&F@3sE&_s1vBd&m8~ z`*Fu{oUOkXXDmIr%=XN;7YW(P$Z8-_(`VzyJg;|@ht19^Z3mB$%*?jL3Fz4^#-*77eNoZ>vTHV_$>aQ=!=dZm#V zw_T&d5iAiRMIEGYM+{U*ZLJ|ii7oBp(_l~(3=F@=*^dlUW?m+l<1x44;fj+%*X}rS z4MAf=iVA7`HNn?2{wa*G500O*gj5=g@Jgt!%M~LpfYaxV58;gtE1h%#qdyGr>fTvX zBN-W2;QD?T)A7cH{OSBLko<9=!vHE8o2V;FX^q;~T6DyJ^ED60Jg{k{e`oIaVwFFC z!wI(|Lt15_r%ZhDOgTByfE_FH#+3g6jxz$Sp~wuit_3t15l_QBRGqX+*TjKPD11%< zDyxtbRs?`F(-ol(e6^)Ky5gO2PN3s=N>Kdu!TI8zI5pF!;f#UHEouBlHxW*QGH85t zINZl7(0Jjz4L^21H1gvy=C9$3>wo|No}}f4+6Z0SNN__Ip*31`!LQlVrE@3tRjIdA zl}?qxL?keea6bxHUx(~1)K}+P<(>xY>5#8X3K}UDrD>?}#>y`0R+a12{{SDJ0Y6~; zF!ky+AIYDN24IYd@jtMUTGE+*`~?(J06Vfderglqar#+-krmRqvg0P)rj>J(GLUPz zdU#`Geq^XSiut}({rEsYeXOht4EJI^R-k+i zqC+0jB?WfoO$|@sf>D@N zz&;eG`%Q7~FK9*V zTYqvrpL(`W#PVEZ*PQola+k%n@?E{maC`}d_SQhuj7{|dnbmRMTranO{Vd)q`#0^* zM~GV<<*@Ev<6i0SEp6^@ZT%{>tdfwbOCcZusOhN1 z>r2~~67Y9+@+!!aMCLlF-zkh8%0TH%Tv+n{)pNe>zZ@dIpMc|e3tlIJx3%Nb*5o-3 z<+>9(}MP=fbW~v>|WUIE4?v@)`J8R7}J*T%T- zwD$Ldo*3rsNq9eL_zpV7qUEgZqO~>_kIBkB^Ok0nno`7&+pCs6v-azbzPRT5Cd=E1 zc#ikHmt?ma8-Ezxqwla9`H;;{BHnG$Gp;X+C zR0)xfwXFH^1zAI?UQH9Tm;`%}=UnQsPJ|SUpJAxc=Tf*Rr-yljDv%zBvoan?CF9 z@2#@u#mhVp8E+S02GIEm1OEU-(xCOnBXPNTo14Qt(XvWf9xX0`Mk?A>l$!i;XT;iH zE#tgzDS6c;nkXTu^fxfAR1-`mCEAv}Y~8_fWU;chS+$EU|rS z%6BB(KXP`B{G4s|t+F5f=x^?$KTw{FEjL++v&ty>#>4nyyRSLVujMAc{5L+TU3x)}9_S6tc^4y0wtLU=f{08a_r|I_|@e zWxh+BNml2QhT=u_<(}fu!>+?K~ zrxn0E&OHt#V7#12qOqe)zGOZm;{L`S0&{J7C;ito%=0ZQ2_!BaDcCU~LH__wT`7xR z;^n!H1GTt$V7jv8C*&S0d!N$%D|xM2S|o@-1wqdj7jk!>A$!_?Oa~no7Xd`%IR=uF z-TKjwHsVIoGRrM6Z*9@|n+`(O4mxXl{#|9{C$xa=Tc|{hQ0^*QEU_&7bdd4f4-Lsi z{m;s!C!YM<8ng{KA&$@H0O#Ow$n*TEC$>3`M7_T!eLdQLRO8oT!N%BFz`=};{{WOT zS1mEa@?E>ZN5);*TF5Lou0#d~-sG0GxfaYx5)H+u#-kjijCXdNEw`Z(Uh zMtytOE#5O&K>(;Jmi^HW{txkig_;{LQBgaEk_xHjw_MvuIp|S z*S<-)YcA-yx#q2To;)w=UA?u;%3zT{_E^;``IO@KljQs2Yd%YfGFn_k##-9)Hu6Hv z>BS|Ls~}NA%2{f3KMZ!I`*Z7i+WosR?qSQ^#5kLq$t};R0Fn!8dWaUF$4XR#i!Nn& z`wO{v?(KuP`;5VAj+dN0l=ORRpHNgYEh@Fo?Z*q;9i#2%w=?nISsx>2PaAQ;_Y08w zo=5r#B3Y0RH6{gXnXWkt?k|h&tA0zf^zd*7X)0}x!$j56?Td#9#M{Nd%KIJy1QcB9~{#et~mL_R+0zKiV3Q=SV$N_ zZ1c%(_jfBVmPoQJRjZb`wd6Re8+&g~;@!%lq>fswjP59R;$Bu+EO_~Yujpx%4Op1ERlE3Je(rIONN#= zkj7%uXceLO;l!~I zR67Wy>9xO=x^%>Z`3Y^JgY%P~L#{n?)nm0lIvv8RTK+f@u-fCH6g2S|ODa{V=4}G0 z27n(N804<$GKY3$gJxI()9u3C$t%Pm4I>0#9!DalaTmdW2bXRb_RNO2-(fAZB+1btENYo>vNEkgaISdKyx+*9mgc z1?KpqX1;=+Wh!$pjI{$%i$8I!ZJ{#R+X%Tg*}7y6NK0LTsZw_a{Bbd}?OxIMF4oy2 zlbmi7myd+*&NI!hy@k7wWRj+org;AV=zZ*@Q2U8@?k5#@#q%AZ?!&{|)}52gq+)w( zTRw8-328zfEK5-A1x6t56uP_ibKL&L=cbNVN0Q@&w|iNjL&Nj7HvXTlV|0;D3}U<5 zj6edo?hkyunD?{TK6~8Hem&>A;eFce?(gmE&LfH9`+2zGb2aB=PdkT>;Z0Gkq)m)X zK=W4@&E7C}b)=Tuv)gd|uW8&|-NDQ89*~ss?PW0A0VJ>sQ<2D>aZ<&%0Kn5}q09;l zEoe$1jZHJyMq>c^s7BGLqLW>upV{N^7$uI}#VAPq*eDGNp$$T4E0Doj*J<0hXw>VC zaj43_zXFE3;0l0A86Kf?%Lu!xUo|LlsLwy1GtBJ*w5Ee9JzRD5O;I~H6#67@H&&7Ki`6M_-kCN`w8)<;efQMe3RgG zt`IPx-%<{>1L1?y?5ExPC`D_K&XoD!#jP{&$eL7OC^Zx$5`I1y*IdB|<>8F#e%xkA zTI8$zP6J4M%4z_wK}_k5Nv%lEnvD(z#+^0mo(^;erGJUTvDp^NP!m%1G$d2!jcI~6 zRP^&4h5@BbJ|B%RMQKA?cy!F*isk_{`3js8d})lne|8Vd<844Su6PU7Fe08kakzt1 z+c^Zo3ax}mph^D)|)lf94{rJ>rT6M3(8D?rfehPq2;#Q?t zZ0Y7-B7b2mfs_+g0XXgXzD z55pC%d%uo4emKZvMs%m{ILzjHWr77L4!`NAi0h2V`%ZZBfZjRjgXO18s&Ul#ftm5o z8XuYHNx~lj0Y4fZxH+v!BzcB^c0ED)>q;Fe2Zjha02ksIe>DyO0mv#zV6{<0OwKoS ztuS}U2yDOyN_f)(tC7*GT8@N)f)Po2R4B@(v?EWm061jdQURgs`93*d*B}8J*E;1u zFvH9|wcK(5RMAxU_~n3Iv)xfjRMMbV=YUjXgx7HuAdiLtJ#zypiXONolpxbO98ey) z_4(Hu3++3D1_4b{l%{wFgJ@!7J~>ly7)2mZ1|K?tbHzxmXERgN0+qC<&kx^5CBeemEwh zb5M??fvLbGXI~%TjPt4T^%eNwBT$AFrlN;UFa~Nsp%o^hOOd_GpPRnALol3nkzubqr-`L;xjb~^stvmk!+it}7tGRu(7q>;8=3emPdpm{X z_>d&>v6>_6MoPKb3X|Kv*(d)1OgsD3_aC_Z&b8#ZuJP?I!MFCl%eE7`duqXD;qUMTIm0z2;9zPZ@ADmQpKvk0bYP*j(fK_ z`{CSN_aVdboX>am^~$8vcJC8&eJZr}bMD@YGcTMiGOk@Q?!PC?TDU!-*}V147gtVV zv$f%PH)}pOi{qvXktg#$+O~%s>+ctJ^Vi&8xLt+a!;t>kc%C3fXK7^@aQBlz+%}w* zf{NTtaqC>LxUE9Eij48>Bliz2VZ&J6`*%M)ovq0?uWtV2-lS!%;pS7%G&j)zuWm^J zvjZLL?T;obc)s`HyO)CZXWO38<}cLRduz?{JdxzBZEX1)pVe6Ti7jd%Vn9*lpg4>B z$Mw56fmpbXQeOA=Yup*`KC|6B*tod(9zxfNc%C}WFFkg!g-a{Y8XehxZ$8O!{Bm|> zue1KzUVEADbHJ@>KNs7VJZJSr?Yq|$Fa%aug+)orJa)f!aGX4F`(w)X70S!ruv~f# z2)IzT)|W9=e^%L5w!1EboLg}{pTv$suy{#tk$bB8X0yEIJGN3~ntncGMv^zKY%-`l z4lny+;bpLbOQ@vozG5pAB+ndI8>6?{G*AFRDAby3i`y_4Dj1!%%(VAr-&a5%U{{Tz7mhZRiE(~8+^-XPfM0&&| zQV#YtR(_So8~*^0_SBc0CwtEgk11_(%9r+$Sb~ptZ7yauM6P?N`r`ipjK7u`EI7M~ zWwPZgRlQj!e1un%CVrweM!p!}?FSJrDc`-*X*w8eVrF@GmaV)JPji@$YS7f2KM}?i zxI*o6@J&xsh`hKyt=yMad4~fqDlm==ceg&5>BgY5-k~%^X3$X6Tw70hXAPZ?7bn#E zHZBXpFwNdghw}`x`QmQoxVN_D<>hR|JZ~LQ6~PbyBresV6wep@tTs^g=N&vCED~J? zxW8)cF)g)dn~p|;wATxHYr=9l^fSOiVZPq z+kM^HT>k*Lyj$Khd}Y=3x39-pTgLls%*!YwDvAJ3XRbM^xK7@(<+$sM80O(P-am97 z(`OQHNE$hOs;CK~kC@j7obH|@x~_ZAjb;9`5V^g0Z0{}>vBV=3jbbMyrX#xKSBv4e z3#kXuc3qJYUUL%6zM|Z6In){e$L+@r#aQrsKQ(s*b6#=w-cRW~6F~l)x7>uY4W-nI zpjw}^9L@FD6U+0QrGzmxm6sVbsc&)um3gBtAdMpjAXMf$Vk=n!_Kn0BEpqmkPG`M_ z=29YWP>sXWWhc%~+>Uu-_mAN_rrgLTo9a8qlW1b#_&bmZK_}D|Ln7D9(41R8cXB-6 zfA?P%9L*;$#|iG@x{gSUpxVJ=L}eL4DhHH|wZ*;HWcEF!=Lqv!`lpkM7%palN=YLv zvn^L)P#CjRHX1+vhy33?al!7lw}CE2j}K!ks6$&z6pX7PF1iqL-Mkd>4kwYA-NSdo zEsW7L*Y|Dd!jQc-pG?q-8Vyc3YxLx}cjY*_p|F$dF5$F_dq+DYvS?(;FamgT5%TFLT{^iWCp74RY~4P(7JUPzU4cj+mhh8;3BwwK*IBgBvVL~yd=<{ zDx9f~N0qzc-e$R-M0e0G%Gm)=s4LLZGmD-xiQ=Wbw`ihEFI-mKrD-k1&iM*vcKk7M z!goST#PCJqo-SG$_s8`+w?l8bJb>|0pvw^pvexnPs}YWcw(=w`5_6cPX;5(mmm_T( zUUAdAagtpzlIjW5D9jQcHgp&;y}+}(SKi*WHp>!{0e3mq;nM^`D%{Cx3kjhh$GOIl zu+Jb4nB>a3gDft$3ehaVMF=cZjJ_vau{+yb8|!E-3#2Oa-7)hu0M9efV&xx?dDoMj zW11O3^=;+cLca*@><#)m~tm z)i+UC>Q_IW5=rARJzU&EGhv=vY2i^!AJt??L8|N?qpnoi!z$OI!JaL~26D0G`mFlq z26XvhCYk_aQ?xMyeMs=v2Hr^HU9QX|sN4a}517jrQkh=jZ%v*#4Rz5$2c{>tWg=0( zbjrXT;~=8C8s+l9u;lHU8ST?#cS^L5k&nx~=ZUS1j^eaku2}0w-Umuz52)l0YLb8jMW=Kp|3A zHUgANXHs>~4jx@t=S=r&kUw^KGe#O{w-VbxC346Sg_a}xaIUXOitCg>8zGu}&Scia>JDrYtX( z&-!O7@C%MgKu4tTGSg^H8nqhZxVtZww2sc-o3muIx(XR)svVE%xFcP0++0nhZ^Lsu zWz)QW)7ncz$RL;1wYf!F0!;zSt~*!TAMZ!n>-%2Axw_*mF8$Q@%HnPVy*opfvVm;4 z{`^I^3i8it;U$;{JBIAB#$JDSJeO*Jb)VZ`_kG>GFFouR_l0f5aoyKz$IS#1gp-oC zl*qQ6gc3^%@>WiuZL4hnW$p*|RsGO#a(g-NMzFKKb^}_A=J; z@tj8tQ;6r}iFOOgrB=9%&>+s3?N4fZ{{Y;p@9qu`0_gUY-@Lp;LC0@$(agGT_D*$V50 zpsj0Pfa&`@rj!R!mTOIMQLkL;LC5N8r5 z3hVk+6?LM>;2et%l)^CFT#CCBk?;TvR}3p%$E9-n@IGS)Ci}9$r(6uHU-4Y9G}i!s zCZ%#Av(CRhxbqr+ul`)JsK!GhpNCJ6>@2i2rGC>vmH|}{;9P!I86Bs?3hkR0+6g^r zU+{6Zl^_b_bvQMz!v>e!hnAa@sSnNJuq_uy3p&4 z>9l60X}q_A_+SC~O*d(+bH+z1jH&R}ry5e26s9O-WdqIS8qijU0J748nRWZ|ToLfV zu7Z`OW||rfzbpZ%If{R$#Mc!4lr^E&hK8L*apoi%g{WMp4nT}a7i@q8%WFJg1#BoOnFTJG@vKUb*3p@&dS6l zq-F8IBW`O}AL*&cXYi$Ql@Ey>et00)O+GmCd37B#q4?u7Qhp~=d@-~Q+qWN^Q!H+0 z1QIBr>OiK1V5t6PH2Dm$5Q>PZdV)du;DrFFBr1wzbgOvcji`OuR+J{E3_^+>ejYn; z>q2~|%)n$uW|%KTQb7QJF%>ii<4n#2I+6uw1$y{X1xiUlN}h(k7!CudMW9{u(9{yY z8sIGe19o%((v>wh2nN!dkHC%8t|+;RROU4r(*m>^1E{Tfj2eD8(RzRzFn-ULD8`4w zT)YK7{01QP_~ z1wx=uS2lS7{LO}azzXCQv>0aHxmidGYH8HB&!z_Dpj`KLAfC7azE&fqw9E|t{A~+L zQF9q&Fe*A?nNz6CG1Oop62Xm4ep-GQr$hJrF+doTKuZb$JTOk0-Hz#`LgR@j(7r~cvRqgzlp}+ zKFb{zt|C0B}96-wVQW_7+^@&Q2wGHO`)iw;SY+Tyx3}clMwWZ~iNOl8&Pgq|Xxs26 zx}0-dNnPBnjH2~o0*uiqC7LJd0LX!b;jOK9!$}p6+BjFqw=wXh5if5e7WSKcXy$KG zDM|=dx*UhYELvDt(hK<{635KNuzyd1PU(E+nLdXW+_j%HyolqaiW}}iIpbTXRz{@L z+x5vwB#eLJeTXOF_*)3&y5;$+q8pL!3Ee4h-n-Q80GbNpisE^WO5)$M`1fU&N~xA> zn8ANn)oO|c9ZfXHdvMkkFv?e1rlo(a;~Tedj+pX%Mi#quny2vZvrRH-ytQysNexr?!X+CqLM zLyd9$p>~fqcWHCSc6a{(OlPD#k(a+P6@*W{Ya-dv7T{{WtO3vfL<9x5P2a$MUT2&O|9v)XZ7P367a zU!4iW#uKj@}Q<#4m;_@SAi9&f8Qr(L^z12Yn(^H|L6gumU(&0FwZKdS99$s6J zy}5N%vV=ufNZGOyF*y-JF?QFt4Hs?LyvcPu=<6pl%1RF4jNh7%LKy3aIqutzvTg!mMcuSVh5KraAynhfH$Bu*b zZ#idyr@LEePSyHHN}o`T6k~$oZEo&3z89Jk%gbD(&3nwxcN+SU$9WaaL~Acq_|p?h zbk=cL2;+V_WN5AVE;7Pa*ddlRrq-KPrg`OyM4EZP;}l(9eO>E2nI#*W(?&Yz0L70f zf6fjnH{>pVr1tPI!#62qDgOZE{$ag@gU94C#NJp*#CApXtXERsaMyQ{jv&J_A4=kD zX>x0VasXj(<=}XZf42qo#h)YITt%uwEwc)k#J0w(Ayee?3|(1A~env=0DAKH3nmO~t1lB#-5i zRe7jND*05Qp%f!r3l+15D_cg6bduWK+{u1tLF=HL3W2`08e#(?UkQ1Qv0Cx=w#IwQ znWZ)K52Od96)Cx@2007PV}-Ee?8B$0VG~>2a(tX=6D_A36G{L@lW;P6judNL@xq^R zy}7jD_^TT!@2$I5U!lF>yN8fikJ4W9lX2_>{*a;cS!wY&<9QAnxg_l_ZeBN${-TM- zrSAhj6LB-V5L(CPNKK+~YlKbA2Bpadj+mEw!i;$LctxK~Y&fK~OQn^ON#WUc)JlZe^@(?Qaxr zQE72?#@|M(BgW17i5q~arX=T=hL+W*`feW5)-gL;+L<5pIeCGh^#ub~mB(&hO<`qp zJ9cNXySA~pBar4L4OO|ex>6iCp=RCTmM*z#%L%`vrwGv}70X;1lIBZDyn$4Tmo%#q z39dL!Lz9v1Y&m#tV4B74JqYccT!N^LUEl$(RmUj&OFTShsXUh7P`;eE5WK9zZB0|) z4uY8F_-;abq`J46C${bD$pn&e(wE3vO4U09QlT@ZC++@cj*`cn;bF3u>24M?Z)%1i zG%?xA2bp%2BZe-17X0QD=&nz^N7(HnBT&~4f)&8A#G@hs1K}=iomvgkY(l*PR zc++GN+b@zhp$GFZ{Kc^q%n>@qRYOF|Lm@hfegd92xp{5m^pNa^Ff(cz@7i?)b-|uE zn1Xh>X&KdnMoSW| zG!izSEIhErf!jm7bxA&LzdT4LRRt8rLnr{q0NR!6E0!c!;sK1TmB~xw128`hhXPZ& zrNJ!Qww0xSKVOy__Sgv~MFG_)NC&At2SbKePfdkO04O8_O}`J1TnXa}P$)F03?E-G zI4DR^DoPVTNz*^yh>>CiVXb!m05VfO!|=}pl44c_GE#t0RcVG`<(F|IA}T52^ZYQX zNkph5(xBx`@iHl@C{i7ULVz(ldS%lR6k4NyH7E$`P9z!Rb}3p45_CTUOciL@QBS5o ztr+ApJvG6ynU+ZEud#s)WQ^`w9Yr<8yw^9gywOCq(Zs-P3I#nUBT#x`+11)Bt4C0_ z?z@0iAbCj2nN(x6eaPaik}gggJ8O%Z_>eSrFhoPkX>lH4mZ1QVmOimNF4u-gRKv+} z@a{`~LH2pB5TE*6^aLx_-(N`|(POZKj{|R~xI5 zr98)Ipsi_!DtzdFCTDee1B18`o`$_M#^d3y-(2xgxcHHu*l@;~4;s^2Ws0%a;5vM< zK~DqXE08!?^>iSev?hnBuiuElD%mpvqs&8i;-ZF!FON_#D&A(+zshva@xWT-q!PzH z^dFb;#8St#*cD|{kf0fcU&M@6j@{a4T91&ysHRoRT471@?HOsGT9M<5=eCu|n%CfP zpgy{023`jRS)bDs+RK|BhjtZ0)mEg)KQPNGoEWOrZQg46vmFS+sWcz}8+K(>Py&2# zc2H84(W{<+ehHwdKfggtP>Ox{{G;GbWOS&*It|a61IMii>GQ#2q_`{@TC~Vl%6ijN zgb@xBnk?jHEV;kg#nEkrmQ@ESSCZb z<(&xo{up4!nQAlptAYUmhLq~Q7~00GriU}B$YXOH_X269E7G_NOn}XfRc{ZD2nSA? zY13R9{$X6Ho_b=YrkW3d6vnhaXYs%!5VaIK@6R>|6e582uAdw%gwilFlAwlSXaJ|j z44JYQ-U(si7-jPq+_AAJMJb>;C^hSWQI#_!(xtT`oiIws2HF6mklTiI-QlJ@%Fg?0 zV_H>cGX}qg0Vq?!PG>yx!1-YR0H-?Xh}<^Eft8W7$g6Gwod%Wh#l5%p7upi=z0qXS zayB0G_P-@D7t!~M_fjJZcrPN{zLH7lRd;m5@)0q%S(=urG_$g zNwg^^Ls8ba9CsrcN{nPfcO+_MoSLe}%-NL&l^ErtftW=>2Kw62+A_m<~g z;?3fw+;SK*IOmaYJ*?za!fB9jRyAc22Q0+@EqN ze(t|x#U^HBRB<>>M{Mg`!mP(pDo(_R~7F606X3sHyz1A97DZhxw|v)Jf|CQ3vY=c zDr+DfIQJ{xuJ+@-e&N3FOCHDIxQS+dM}+3Pme+@tSncoB?ybZL5p)}Ns8gOj$L^l- zw6^6ru4ns0zT)PJZYC}hk5%G6lj~w6M)62o2$MA`*<<^B_E~4*IS%mmf8TD; z_aBetIHqK8rkk@RmUa4>s?5Jm-kfZF%}QmCBec6IF1e2TvHtXUZ+bX;2lXw}w>xXJ zh0hbn_pH+zp=eC%n@9!AhOSuu0L;Ca-^O;|dvV>H-X8JyhjT`}HOFJ}J&)Ws{AK;e zYXIzSWWR_kmk#B2jKyj({mt^cmoYDA{{U=$$e!Gnx48>wMVwvp-o2w{XXQC-4Z~!g zQE;dyk%s0Zj9v0w*W2>;{{Xjr(e2Hjc=lIlcNC4ra~;jxvHB2lyudcWWVS?%PPXPM zNI|I69k1V=m)kwF!f;%amff4%Hyr0R$#Qe>vqLeodrL&UCP~~dEDL6O?{m|PFZ<^wUF zSQZD6xPyy?%GMAARlQjjqT7%_ZjvF~%Ev(2 z0C4=Rhi@6JIWq?pb8W~-&3$t=-%d$vt#{lzvcUway;)iyu|#EZgSGXWr}#AQ6;Ar7alTzNTmSF zJnn|0Gv%mCg;*E1KNena%Q>yr7iFo;;o7}qwdw(GZ{3gu0u?B2PbkrsMzrrDQg9X8*wTyQ4sX)t*xZ;?vEg7DV`#^u;qBZ-DVBz zFE?mshm5y1k@oB-e549cjENZJ*M#G(`8ZPQ8+o~IMauHGcXEm>{6umOExM6fk~O9| zPS54|j`F&D((8SD$E~;=e6D;;Y#_G7BR8N-6yN{$|kE7P64yNR&smK|-?AbBO-{<*!kMM@bQkGaycq=%BHwP<*wykNV3{b#9Y=`XMkk|&9U%M4(|+cynGu(*OUD;2qfH&(w`(F z{rHEAy|xy&*3*#;)lq7;qH++5DuAkz{-CI%Z>>pO0PD3WR6RaG{g`;# z;jR^kWz+QPX_tT*Wr-Ry`PG%W22yj&!xN;+Rmk$XS9D|yl}Cmy6|Kd@iU3v*Qdq9+ zhuxMvJ*bM!x6)%(5(B6Qu3BM%MEycZZMvFgKr!k-3ohV6Igr3@Y>!MaGza72GU&>~ZIjO`6Cj&9#>~U* z2OO_&Og&yhjIz7Cu^BVU7Bbra_#FVn(i>!k<{P=Kt|OI02xPaDX34{5RMRnw@@_6q zwI1PHj@tKQw`6Iqc#cbgyM3w5&0>`cErXFX6QJCH5n3F7X}rg{UC+t;P1$|b+}yWp z`+5EN`!&v4_b(4;H!~aFb{zco4$QYQTcn8A*TzzI83O+RIT-ebyt~KUuXMfT_h;EI z2LAxt=J#)z z<6I2Rqpm-j!!0pP{@eiBZpg4XQk4Mxy>Zn@05Y8a0Q8ab8sKO6d@vXgb0E|nne@S7 zuAMq{7@(nUv>kfqftQa_;elF!PDYqupdGJT)M?-ez&~|#ug3tkWDsZ$N5+R9p?XrZ zUDY)3rWo{EpTeUDO8D#d5rAu-g%9t>SOLKm>NWeY#OPOV+ws8of1Pnk=nhr;Nfi5OO+h0{R5y>^fGDI>Eo+MH&+y0PU&jFL z*E5}PNEwq_Ymvs|n*1wYw-u!;r|hkNjVY}L8G&VCo<@R{)B}Lpk@<#|KR>qs=uLX& z27?DGg1=zTrXM1-azWE9se!bSpNSv42XNIWPmW$VMl=Sw5;Hke*1a^t25JqX)1cY_ z85$pthCeV+SO9d+v=~MtpE}lqDrQY-iqvV9PJasFD_9gw!LXe;Ie?E({Te@Wu_=TVzn3_0sA*LKv1oEWvIm~uzYg!w+w}N?&xl8@eogtmXc$@nFCD-fqI!AS$Go7|H`R6nTipzn|H6dUubo7hcSB z_pRI4JRfU&Z`z}t<2wc~O8&=*;5(@i?~{)yv16U(E?Gz+PT*gV;du+EvAJMZ*Fg~t zT1#$GGdpQe^r0UdTiwGWpQe^M8!DFDC0GXVI%AIR8z{X?4u6ifoF>|%n9`0E^`~CA zZbP_jLt3kH$=f-!V$IXj1Hy;Kqu8txRra=oc*-8Mbt1?2o!H{bWs zw;yTAUBa~flgC?K5G9N-q;9LU>s)YM!)tFhBrh+P+Q*K(h=q7%kb2j175@O-3R(SA z;g5B>w!ON!y!La3<2}N$;;cxxh?t$8+6xKYa3i*X_0EyPeJ1JpTZB+R=Ib6SVsx<+#4b;^(%MY>GEA#E%+u2P{J` zb6rp0-r)N|&Gu!qe57)VP7|~H1DUv6E-E{FJs$2=Wmz)I9p%jSpAm1&dspnYyWPFs zJgthkwdVVyy!+F(Z~J&~+$=MGla*Vhly204fbV^hV}Nj<^j=8v}dd%irIem?tz zwIFfg*3LSBy8$(N)~6R77S2X19#Q058?zqtTglp@&7EttYxZLIgudhFhE68ZcyBKz zCMAYdi0ylMI?!ea$1E;v9VEKrnJs5BH>Yz1E9AOmKbk_yC~;%Uy`OM$ea0-)jD*8# z?Z#Bj`jCNXUW_+l=5tk<=mvvdW`9dspc@< z-t+y#7j7OpYAdC!s_edy%D`(fbdTs-XY)NV&Gz?k-}gr=&0iR&<n`W51Z{kGeiUrpqY*ESln{f@$QwhiMJY0P5KNZgjc^!?8$kuijh^1z@Z_B(kWb_9dS0lp?*zNA; z=P%;=p4n){*86r6CB?wLPbqY&ngL4VmAK~aX10Trx-7}hvOj^7n5Dvy`1+Oe9L`7P3Xe3IOFa6OO9Dh)u>qgMG*S2)6!TXk$??B zpO-vbcIS0n#oJw}W5OOuF0G|V<>fAJuL8?!adUA-AV*L(oUtwQPj43!c0Ex94BUj0 zz?B>15tHX8gk|G}?j9U*ou%7X78`EwxtNPwJ+-*oG_gl0@|G1S26^J^x%+Fo2;*$# zTT7?mhxGPdQM2_fwh+Y1at4`JqY}?$$z5|V$rz)tn&Xp}d(JXB2oXRbW0#31TyuOa z=Q$PS)VGnx$NxGPtFwu zBa)vJjvffOrH8hAp;{g;;v2}^Lh`QY;v^bv@PD%w+;0Y64nwtKpOC${x^oNZT%SM?;f5+BY5GOh*p z3(8E~&E>?aFy*bxH#}5OniFoPHI+_|GNv{^D|vk?+B=!%Z&)#nNNFUHoS2c9!x8e* z>~(??{->!KDFTwLHdj;f_~P%8<56xA*s9yRWRB$2{Pj4uX=8PehF3(*cK`rTM~F2z zj@il&S-nW#aV-@^e=zHdM7D#~dsI;{mZeImp>ju60;3H%qE+{jawt3r@TW{$dW*6v zsf1~8i&7S}GUwni$nl(4H}wx@(MplAGPo|goU;t8(1DP|)(>qw@)9>(ZK6fH!MYy| zMq+|f2mqNnWVI_!xo3@qKvon4{%ZJB?!uuVM6s^ZwzXmTnu8xv942v?Xz5(Q{J?4c z>_>Wp+rHx_;~T zB?q{-oRrsb@)E(@oaD|H3H^AMYiMI#tiT4yk&ZLDxX*Suk8!=D?@q?;*Wf#Iz58y@ zwSCI$&g9_a`dKeKrVcaoC5rYhPt%qx-D0a|aL)9r9qpAo?VAb?mBmNlj|>kTawinf zABoEu;i#p{4X3RX>Tp3)2&U0PQ}M!os=JUUYh%tC@i1wV!l*@A09;atCO@Ww)dwcA>abitr1 zJS&&%5w2`J=l=jsmHZAWQjL~i>(G--WKNXu!)7{u7&P(eiuC>e04x#W2>Dm80?_4C zUY;WYqc5`?NEPs=8iP>O(*;{HNYhWp1Qt`5LM!8z0I1w@G$4S-Mavn1UbQs+*dl~d zg9U&zQJ>1aD}qjjq=G&;$Yd*u{AqxJ*R5HF0Uz(c3`sdI^#gB+AIBdY2>`1AXby~e z1MnCO!0!NqKsIZZUuG&As@c$dbih&24SaAn(Nr9(`Et}}hXRZ|I%I1_!r>Fe#M)8dvbaVm5=N2l%Eu!#wMs#?Z;COA<84~7{0Mrb+3<6CBMnHcY4X)dDU|82&!22=fB##`&TZPdNY?IyG`M z9ylPJsa*B1LHn=`N5}2R;HafO+W7Uulc8d3mYU-_6H4TIeVC}zJnBsd$o<$1bS8qC z=YZiJj%i9}4KpXl$e{@(gkZ5n3_l|lPc>_hra13ty^!u6 z(YWr;>+T6Vqmbjh%=ZJ6;dz)X`!3F2;Qmw2aO*pAH~N>5|+-O`LNZ6uJqau}v<%#2R8H}A*HWVu&(;j=(? zC4e4B1!}6rs7mXLrV!C0-|8?7KqK>1>EJQgyxjTQ@LkPw!`_fiRx*SVX56jZ{iF=o6R>j+g7}Rk@#8rj_R<< zcXexZoBCyoZPJ+HAonlaPD8yp=ew7Xx#9Txg9mDM+|!?0dj>4TF=A<^MjL0zqV6Ij`2Y zT8p_>2%-)$6{6$Vt||#F`*)3QYWfc5~B{9kKx zwgheZc?@1jBo*CEd!gPv>FD2b-sAhD?9X!cEcbkX5#N`4rseMAjFVM{VGsyEDw=7J z?c&$Bxy0uCqr2>RZgbvF8hbRlwS%|#ySVQ7!30tYS+>F!2}L(+j>q;hj=iw&zBAk| z`HzC{u14+J@49(;dxm)vkKtgN0v0O{9?_9q`Zm%|J@Nj|curq{li1&FxGsMG0Np$O zABl!ucZTiABzV%tw#tM0)6}~$1n#SK$F&~C`&-F(KO4tE+`Wa{y}JCx?YQQ-9?b2# zuUhJRSRzp(R7Rpztqnmq_a@Vvw(VGbr}pcy++0NO^u^l6a%~3ooo5t-41fd8$TaBVB8bK0Z$J`-ZWK zk2f=KZx#8Gf&EK}w;jkOH6)Dl!}m91au6i8+>NEhyu8$MLmw-750*Q5E+thlsiB|% z?yfm5Hva&H<2e52S1M&pUBee{@O)P0FSESmug++T)nEn)L0icknVht;c zE-QgN&E1z7$Z>aF#l^cl<6K!>#x5g;8fq~?TC{p&j)pEziS6DN!f||-az=RWV~lG# zJw~64jw)^pE9xgsmBpUk7UQ}g4HcaHw;?^icfC>;+8ZROMn)pC&}eaYZE?p73+t<| zqnh7_vXWovoN^D+1b{+0pe6Lg_P0&SN;o^ad;b9G{H3c$FElOX5?k5AvM3}u6gzQs z-aWb5++QWj{SBl&(Q9iW*;?9K#U|)$Z4TI_nQP`?yOdNMTXXiD7Yk<>CM|6vw7I*t zlJfPQfUSz9PW1%SD&x5~Op)8hBIjhdwSY}~1j^z|i+3WVvYd`NJ8oCBd0%k2D5B$e z_`83&F71rmRQF$=_l~x?K6M+aR0@HXF1gwG2@h~V1W|C@bbODeaBMEr2&`?3AJBzK z0mhiJo08*scSJs?urSjat#(i{^mF_N4i7}|D1;#+oVYz6y3%l+MmFY{xOvXLE;_Q2Sled1xN%_uwmf}Rc3a@_HjQv(c@V<4E*bgZnyDs=oXZkB0p ztZpm+09r=!4Op-`D6S_QOm`MmvXu)H7LFo8t4kRjTAKJ_VSBf5WDFN}tiqc8xGGq@ zp}$E7?;$K6QB8d~LMWmGCsv5pkm4;r^8* zSP6^`vjdr_&u>~{@_8eQ8GfPA&JYGN2B8u#%=mara_x8b7? z`e8*_bqDOkOfD_aX;UuXloZIYIO8sb%vU#eL5ygO9L5}FPYMjiD& z2ra}=ZA-NDkTT8_Yum^vr-mb%0ra!LQNqLx11a6*8RLuYdt@?l{h*M%@&Q>|*cwcv z4CzeHIa_O15zdH$32EkzW*fw$>z+d$v+Y!$+fQU&_usQzj9u;AUD&*D)^lCSarDHu zIdyRnO^t5K83S$~1Bm1@$|o^KzO<1GHrTcMT0i2=K*Cb;ex>Uxpn)r8@lZ8Jns90FEl#Yg*Kb)QvO6 zO)2oy=cvYiaIVqwrnsumQ%e5;0fClZXWN1floYNu(@)`+2&HO%Rr_#!Xe*sZ+50s( z1Q10)`00iDfB8644nR=rtue4Q0L=Vpo?k2|2O~kJ#<-`3Ro5>J6G{paPM^z4;4zF) zHFpu27;034!>ORIU2s?m`Ke7c`1~+K3a|iY=3)+gI%1?ybgxt4OdO3o0Y7CiN_=WT z2k*w8jt_ya?fXaIYmX0}GjH(dQio?qwt0u|i5gO<>S14YJ%@5NH9TJF#8(;1q4zYI{CT^vf@v zJvyJvet8;G0B!(v14{J5oxz4?jhF-)0x$uP9mbR*nhNEDq>2^LeEd!d43zZv(+UYS zrD(CrlX zeDHSojQ(9P&h*WW(OQkVY10gZP*I5)eng=s6w7YF)m)T}CUv9oh+ zDk=f3bggUn;3`*W&=4{m06z|xU|a}^o=n97|f z0MeL7In_>{Cln+SK3{H_sUQ!I2gFw_Pyk4-OhEnk^DPF2b0D9X#9o-C7i~pzuA#9S z=m{ddGt99#0o6$!WB{554OJ%tVjbmym#V1Hu>Sxo63W@rIsgp?F+>#&nQowwo_N_p z!k`j*^*M|UznF~(72H3Y8^B`wYu7QEt}0l9GH0a#Uj{$rg2K5qH)rNGpaH&F;j|{S zp{WFGo)yrCed07i%jvB=;4p0%k&(x1e|10GUn$((yZzVpS2M--<{-D&WHH(upK0!A6KBuySFhZ@<9EjkWgVPyO&AhCZ}B!3-dO(tO{f#00=VL??d|Tz zV~8$kyP#mKaup1rb$ zf#;Qc;haT0StY?Mx*R**q?c`{)mRuNLq|4MKSLmFKNbI_qS{NVBD{;p44$S?;jXAe$})|yNjIW z;kzU}<*&IJQd`0*OkbFsdkc5SuN?iK*;ifd%lEfvCCkrc&39av4<{ayJO_Qjj7YB3 zIj%cz+n&+ydl|WZetQbzy*rNXc9iz77sp>(HTAa%#(K={43Q+1NSKDMgG_rlXFnZr z-h6kkoc{o9@OJ&D#@ouAr*_T3-bp`jT})>RCVOxJUYmN?9`^TlDckoy)4#YLBGvmU zR^+(*JD+JgKZl8IWQ&`)m(6a^KG76$A21~J$8~>j94}`{-+uGo_oqAAe#`cCq$8M; z0ma?R$6xdJ(-K5Bw)YWBESCW0pj6OeUwXa7=ePU){pU&&&K<*bXjFKdd++a`J;+N3W&Z$ezSMF3yVAc9-ACYOH$Gts~-CSgR zFM7|zTsMDn$qHSbM~mcanPXMFx)pCCtHmsSUr6s5vMo4P;W>`viDg#g zd5_mrr@kg+%lc$!UYpBE=^f#bhaI(*1G*?gPO?Agyk4nSpy0W3nme6A3in>*> zP+V;J*to$-8g0>e|kc66Im7!DyglprD zVdl6l{C5Wg*4Fmaj@Vx`gM9LZ)`Zo(dSSWlN$sq=cem_dvbyBMq@$7M9B$hSjP?c~ zY}A@#fa0t#%98`RPCKi+d%EezBYEvl3cMwkt2&D=X85~WvZ&FcaT={x z%hcnxVef01;l8=CTL$D~b$PCDo=OSjI#p4D57dnD+`L~a$Y0#I32$XJw>uLwOLGm% z52v-fjQK=>%3 zGjC{0%t{={V4zfMTyou`!SR=o&D=>6+@-wI-59wU8`4-1x0*PVjm0t);X!ANg(4yibH%@N zyz^VEmro6%)ug)a8;OaI-7eBE$Pi`HfC^&T@9FP3zE_7!=jJ^NR=To^JGjg+*;>rQ zXn6Mm-y@62o3-1H;_T#-?!xJ`ecx8EN4>7gc-9ylDoCqHiSaa^L409mBuXO5m% zm4Nin5looSVspuI91Q-L4w9_VOC^v8bX%E8JF*#Llj1EPu$!5z)p|pxAF8GVCLU2~$ZQ+pZ%zjS{ush6R{ej$^g?-glL_wt#VB%3!N+Z0a3-ZJ(FR(CLTnt6mX# zOLw_Wixw7IFe1JEE}W(2~t_8-7G)HN=wJMQMDt8`R#KOK)<=UsV#h5NI>5 zF6?H48SJ?QRfgf-?FJW(Y2}lOx`f|c z+uK)r%U}Cw?j_JD{&DlJE-l1Gk}^zJNLF`Y28I{=6?jk8dyG^fv|B)N^NWUgS9sro4{p-nOs6{yGRN&f(C8f}g^ z!1B6g9Vyd6OdFXlO#*-dAr7?aYmxB6ZQRP#F}jSx(;{n&k68r5O2qL6&7!BAR1Ubl zv5wiA*@1gt{$Hh#`P2e)%REVT*EEz(u_!jNM>|$9HdQ!lZc~VPx3vLTHV_$W zxB-^lqL^5Ww|;`g1O}v#7a)t&Q%p#+HROd!^tUqr1uB7v)|m9(`{|>0qA}f4PfB6q z7appKM#imo(=t>X&UlV{7^0E)sPu~+>}gfle|Y&|{dJ+Yo&3y-!Kt)Xrx!5oMUt$+ z8Aq1N*?l(>oTaHIR9cK*<0eKP+!~mKay_TuAD%?*gFs{{WUHc`qVW zxTK~4Y9^44qHu$yC@YDA?pUL3tTzFG#4q#MZ?=#vh9-U>f(P!!Vo&Nl>mL1_bZ-@oaTEU`;&{ZCgtsWHuHqJ-AZ@ZWz-yWW1UiM_TU zO9aq2m6AmVEEY%JyRvSKpwJp(m8DH#Z;V0)SKQ#RV};uth~$lmzQT zoiNKvzSEQbQSn+}({@drbs6Y>cnW^~G#?CYuYs+9hA1`aE1d?kIQ4+22WG-N`et&# z^5u}w_~Ro&em`y*N}8PaX-|Q{1o&f9_~Nvu;et9E>++^prk~@);DP(At}4Km@i`x} z6jh}&%c_jAmyd}CzY&einwpKZt#a#J9|1}NP80?tF$B}$;aqxqiBk0jndT@m!m1W? zwA7JYtCn~mf>dfi>M&_cg>%&Bpcpl;Um9zQVy8~D@au+Phi!bP;7teZ(Eb>%f|bkn z;+6fGe~-TzW%9*YsZr+n48hOD;A%lMmpTOl^3Zj`3@F6qokr22uf$+4QCj`E;+=38 zQ(RQ&Y4&(yOo!e_#}4Gt4K}IimUPBcDrrNHv=6+~3Y5*X`)~ji$b(Gj^TDYae6^+t z6rrY{Dt^kGR{`s(r7#$IshtKgJ3-Sd{`_yR^ATU3b;p=zl}Q4zKjn%6p!@6b!KlkB3=WjYe5r!+FaTtAsU%PUG3rReXv#JJ07~>E`1}R{->K5N znrV&OiLDJj+T~nVu37xF!Ycm&Ga8)$psu+7Y_-=c9O+!M`1H>ocl)s4n{lmnfuJ?( zfE9trS3yHgqZt~H9-n41IS(AGiV`W*WmDsd3Xh%&*J*5vZ6<@L%f|s}R8h1F8q%}} zJZ;XRy)qQ0Rr_!sr4Qpn{4hYE6{R{41C4^4iKdx~W`A}H{{S;+4q9P$)`19>XelK{ z2{>4aF7*o3QhhQ104Z+}Kg$v_W@M=}Am?6~4xV>n!^@(b)ZLyVs1#$hl;=+z4@B&0 zRG)$P_~0p~nf!cC0U2wRFd5v?$f}?X0W_flrAV$8iZ+qvV0`3LAhh{oa+i_|Q z0jG9$ulG8=zsCESCB&T6GH^EZkt);FEbJ2i_- z&+<1Hph*(DttDc?K6vg=aHWrhu{`Tf1&I+|ljlvc01B&`oV|$E5c__cw9zw$DKFTrq9) zfNC`-9_rX}treeWb`QK9KWTRF4IQIJbtvK|i;3o@7EviOo3w@IVg_K-9_jMdb~b&* z6~A$C5&Ly%#&+fHha16NTH1*CUf$*=5Ugk%QKyuj%AlPw%HQ%FI@x!hwVu!SU8gx0 zW?!pt+!u8BKXk2?&o9m0*%gE#f!ok&DIum+$FiQ;_E&4&@qM>t?N=FP&)q$u*g?#8 zrvqwkIbK5l03gg~y1J4`+DfqzsjV^3ao)`H_x$%O-2UY9e$;zUD%`l6gat}?WPf?xeQVl^Gt)@%*$HkmN^@4-Ja!aF0Tc>@wzNkE<^fh4(@@DTWg3p4oco@cC_P@jkw|wXnEsv zAc_=K(8jDmFRd{T8exZ??fy5obKC=Q z)`{txS?1(+Y&OzFzyOhqfElsV9GsUtv|L9gbJ><1mwLwCBE96{hV>`7baWfpL#bAJ z(w%X6VzzdEw=3J6G)c?FDSJCk4~qco5I6qwiq*z=;b4m1amJSxwvBX;J0w@J(QZlSjmN_iWh>O4@)i&HFh^9>Z^i;mlkpOxna5S_;_A#pi36Y;mRx^by8xub