baseguard 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.baseguardrc.example.json +63 -63
- package/.eslintrc.json +24 -24
- package/.prettierrc +7 -7
- package/CHANGELOG.md +195 -195
- package/DEPLOYMENT.md +624 -624
- package/DEPLOYMENT_CHECKLIST.md +239 -239
- package/DEPLOYMENT_SUMMARY_v1.0.2.md +202 -202
- package/QUICK_START.md +134 -134
- package/README.md +488 -488
- package/RELEASE_NOTES_v1.0.2.md +434 -434
- package/bin/base.js +628 -613
- package/dist/ai/fix-manager.d.ts.map +1 -1
- package/dist/ai/fix-manager.js +1 -1
- package/dist/ai/fix-manager.js.map +1 -1
- package/dist/ai/gemini-analyzer.d.ts.map +1 -1
- package/dist/ai/gemini-analyzer.js +29 -35
- package/dist/ai/gemini-analyzer.js.map +1 -1
- package/dist/ai/gemini-code-fixer.d.ts.map +1 -1
- package/dist/ai/gemini-code-fixer.js +58 -58
- package/dist/ai/gemini-code-fixer.js.map +1 -1
- package/dist/ai/jules-implementer.d.ts +3 -0
- package/dist/ai/jules-implementer.d.ts.map +1 -1
- package/dist/ai/jules-implementer.js +63 -32
- package/dist/ai/jules-implementer.js.map +1 -1
- package/dist/ai/unified-code-fixer.js.map +1 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +1 -1
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/config.js +2 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/fix.d.ts.map +1 -1
- package/dist/commands/fix.js +44 -15
- package/dist/commands/fix.js.map +1 -1
- package/dist/core/api-key-manager.js +2 -2
- package/dist/core/api-key-manager.js.map +1 -1
- package/dist/core/baseguard.d.ts +1 -0
- package/dist/core/baseguard.d.ts.map +1 -1
- package/dist/core/baseguard.js +13 -10
- package/dist/core/baseguard.js.map +1 -1
- package/dist/core/baseline-checker.d.ts.map +1 -1
- package/dist/core/baseline-checker.js +2 -1
- package/dist/core/baseline-checker.js.map +1 -1
- package/dist/core/configuration-recovery.d.ts.map +1 -1
- package/dist/core/configuration-recovery.js +1 -1
- package/dist/core/configuration-recovery.js.map +1 -1
- package/dist/core/debug-logger.d.ts.map +1 -1
- package/dist/core/debug-logger.js +1 -1
- package/dist/core/debug-logger.js.map +1 -1
- package/dist/core/error-handler.d.ts.map +1 -1
- package/dist/core/error-handler.js +2 -1
- package/dist/core/error-handler.js.map +1 -1
- package/dist/core/gitignore-manager.js +5 -5
- package/dist/core/graceful-degradation-manager.d.ts.map +1 -1
- package/dist/core/graceful-degradation-manager.js +16 -16
- package/dist/core/graceful-degradation-manager.js.map +1 -1
- package/dist/core/lazy-loader.d.ts.map +1 -1
- package/dist/core/lazy-loader.js +9 -2
- package/dist/core/lazy-loader.js.map +1 -1
- package/dist/core/memory-manager.d.ts +0 -3
- package/dist/core/memory-manager.d.ts.map +1 -1
- package/dist/core/memory-manager.js.map +1 -1
- package/dist/core/parser-worker.d.ts +2 -0
- package/dist/core/parser-worker.d.ts.map +1 -0
- package/dist/core/parser-worker.js +19 -0
- package/dist/core/parser-worker.js.map +1 -0
- package/dist/core/startup-optimizer.d.ts +2 -0
- package/dist/core/startup-optimizer.d.ts.map +1 -1
- package/dist/core/startup-optimizer.js +19 -12
- package/dist/core/startup-optimizer.js.map +1 -1
- package/dist/core/system-error-handler.d.ts.map +1 -1
- package/dist/core/system-error-handler.js +18 -11
- package/dist/core/system-error-handler.js.map +1 -1
- package/dist/git/automation-engine.d.ts.map +1 -1
- package/dist/git/automation-engine.js +5 -4
- package/dist/git/automation-engine.js.map +1 -1
- package/dist/git/github-manager.d.ts.map +1 -1
- package/dist/git/github-manager.js.map +1 -1
- package/dist/git/hook-manager.js +5 -5
- package/dist/git/hook-manager.js.map +1 -1
- package/dist/parsers/parser-manager.d.ts.map +1 -1
- package/dist/parsers/parser-manager.js +1 -1
- package/dist/parsers/parser-manager.js.map +1 -1
- package/dist/parsers/svelte-parser.js +1 -1
- package/dist/parsers/svelte-parser.js.map +1 -1
- package/dist/parsers/vanilla-parser.d.ts.map +1 -1
- package/dist/parsers/vanilla-parser.js.map +1 -1
- package/dist/parsers/vue-parser.d.ts.map +1 -1
- package/dist/parsers/vue-parser.js.map +1 -1
- package/dist/ui/components.d.ts +1 -1
- package/dist/ui/components.d.ts.map +1 -1
- package/dist/ui/components.js +11 -11
- package/dist/ui/components.js.map +1 -1
- package/dist/ui/terminal-header.js +14 -14
- package/package.json +105 -105
- package/src/ai/__tests__/gemini-analyzer.test.ts +180 -180
- package/src/ai/agentkit-orchestrator.ts +533 -533
- package/src/ai/fix-manager.ts +362 -362
- package/src/ai/gemini-analyzer.ts +665 -671
- package/src/ai/gemini-code-fixer.ts +539 -540
- package/src/ai/index.ts +3 -3
- package/src/ai/jules-implementer.ts +504 -460
- package/src/ai/unified-code-fixer.ts +347 -347
- package/src/commands/automation.ts +343 -343
- package/src/commands/check.ts +298 -299
- package/src/commands/config.ts +584 -583
- package/src/commands/fix.ts +264 -238
- package/src/commands/index.ts +6 -6
- package/src/commands/init.ts +155 -155
- package/src/commands/status.ts +306 -306
- package/src/core/api-key-manager.ts +298 -298
- package/src/core/baseguard.ts +757 -756
- package/src/core/baseline-checker.ts +564 -563
- package/src/core/cache-manager.ts +271 -271
- package/src/core/configuration-recovery.ts +672 -673
- package/src/core/configuration.ts +595 -595
- package/src/core/debug-logger.ts +590 -590
- package/src/core/directory-filter.ts +420 -420
- package/src/core/error-handler.ts +518 -517
- package/src/core/file-processor.ts +337 -337
- package/src/core/gitignore-manager.ts +168 -168
- package/src/core/graceful-degradation-manager.ts +596 -596
- package/src/core/index.ts +16 -16
- package/src/core/lazy-loader.ts +317 -307
- package/src/core/memory-manager.ts +290 -295
- package/src/core/parser-worker.ts +33 -0
- package/src/core/startup-optimizer.ts +246 -243
- package/src/core/system-error-handler.ts +755 -750
- package/src/git/automation-engine.ts +361 -361
- package/src/git/github-manager.ts +190 -192
- package/src/git/hook-manager.ts +210 -210
- package/src/git/index.ts +3 -3
- package/src/index.ts +7 -7
- package/src/parsers/feature-validator.ts +558 -558
- package/src/parsers/index.ts +7 -7
- package/src/parsers/parser-manager.ts +418 -419
- package/src/parsers/parser.ts +25 -25
- package/src/parsers/react-parser-optimized.ts +160 -160
- package/src/parsers/react-parser.ts +358 -358
- package/src/parsers/svelte-parser.ts +510 -510
- package/src/parsers/vanilla-parser.ts +685 -686
- package/src/parsers/vue-parser.ts +476 -478
- package/src/types/index.ts +95 -95
- package/src/ui/components.ts +567 -567
- package/src/ui/help.ts +192 -192
- package/src/ui/index.ts +3 -3
- package/src/ui/prompts.ts +680 -680
- package/src/ui/terminal-header.ts +58 -58
- package/test-build.js +40 -40
- package/test-config-commands.js +55 -55
- package/test-header-simple.js +32 -32
- package/test-terminal-header.js +11 -11
- package/test-ui.js +28 -28
- package/tests/e2e/baseguard.e2e.test.ts +515 -515
- package/tests/e2e/cross-platform.e2e.test.ts +419 -419
- package/tests/e2e/git-integration.e2e.test.ts +486 -486
- package/tests/fixtures/react-project/package.json +13 -13
- package/tests/fixtures/react-project/src/App.css +75 -75
- package/tests/fixtures/react-project/src/App.tsx +76 -76
- package/tests/fixtures/svelte-project/package.json +10 -10
- package/tests/fixtures/svelte-project/src/App.svelte +368 -368
- package/tests/fixtures/vanilla-project/index.html +75 -75
- package/tests/fixtures/vanilla-project/script.js +330 -330
- package/tests/fixtures/vanilla-project/styles.css +358 -358
- package/tests/fixtures/vue-project/package.json +11 -11
- package/tests/fixtures/vue-project/src/App.vue +215 -215
- package/tsconfig.json +34 -34
- package/vitest.config.ts +11 -11
- package/dist/terminal-header.d.ts +0 -12
- package/dist/terminal-header.js +0 -45
|
@@ -1,563 +1,564 @@
|
|
|
1
|
-
import type { BrowserTarget, Violation, DetectedFeature, CompatibilityResult } from '../types/index.js';
|
|
2
|
-
import { LazyLoader } from './lazy-loader.js';
|
|
3
|
-
import { MemoryManager } from './memory-manager.js';
|
|
4
|
-
|
|
5
|
-
// Comprehensive feature mapping dictionary for ALL web platform features
|
|
6
|
-
const FEATURE_ID_MAP: Record<string, string> = {
|
|
7
|
-
// CSS Properties
|
|
8
|
-
'container-type': 'container-queries',
|
|
9
|
-
'container-name': 'container-queries',
|
|
10
|
-
'container': 'container-queries',
|
|
11
|
-
'aspect-ratio': 'aspect-ratio',
|
|
12
|
-
'gap': 'grid-gap',
|
|
13
|
-
'row-gap': 'grid-gap',
|
|
14
|
-
'column-gap': 'grid-gap',
|
|
15
|
-
'scroll-behavior': 'scroll-behavior',
|
|
16
|
-
'backdrop-filter': 'backdrop-filter',
|
|
17
|
-
'color-scheme': 'color-scheme',
|
|
18
|
-
'accent-color': 'accent-color',
|
|
19
|
-
'overscroll-behavior': 'overscroll-behavior',
|
|
20
|
-
'scroll-snap-type': 'scroll-snap-type',
|
|
21
|
-
'scroll-snap-align': 'scroll-snap-align',
|
|
22
|
-
'object-fit': 'object-fit',
|
|
23
|
-
'object-position': 'object-position',
|
|
24
|
-
'place-items': 'place-items',
|
|
25
|
-
'place-content': 'place-content',
|
|
26
|
-
'place-self': 'place-self',
|
|
27
|
-
'inset': 'inset',
|
|
28
|
-
'inset-block': 'inset',
|
|
29
|
-
'inset-inline': 'inset',
|
|
30
|
-
'block-size': 'logical-properties',
|
|
31
|
-
'inline-size': 'logical-properties',
|
|
32
|
-
'margin-block': 'logical-properties',
|
|
33
|
-
'margin-inline': 'logical-properties',
|
|
34
|
-
'padding-block': 'logical-properties',
|
|
35
|
-
'padding-inline': 'logical-properties',
|
|
36
|
-
'border-block': 'logical-properties',
|
|
37
|
-
'border-inline': 'logical-properties',
|
|
38
|
-
|
|
39
|
-
// CSS Selectors
|
|
40
|
-
':has()': 'has',
|
|
41
|
-
':is()': 'is',
|
|
42
|
-
':where()': 'where',
|
|
43
|
-
':focus-visible': 'focus-visible',
|
|
44
|
-
':focus-within': 'focus-within',
|
|
45
|
-
':target-within': 'target-within',
|
|
46
|
-
'::backdrop': 'backdrop',
|
|
47
|
-
'::marker': 'marker',
|
|
48
|
-
'::part()': 'part',
|
|
49
|
-
'::slotted()': 'slotted',
|
|
50
|
-
|
|
51
|
-
// CSS At-rules
|
|
52
|
-
'@supports': 'supports',
|
|
53
|
-
'@container': 'container-queries',
|
|
54
|
-
'@layer': 'cascade-layers',
|
|
55
|
-
'@scope': 'scope',
|
|
56
|
-
|
|
57
|
-
// JavaScript Web APIs
|
|
58
|
-
'HTMLDialogElement.showModal': 'dialog',
|
|
59
|
-
'HTMLDialogElement.show': 'dialog',
|
|
60
|
-
'HTMLDialogElement.close': 'dialog',
|
|
61
|
-
'structuredClone': 'structured-clone',
|
|
62
|
-
'Array.prototype.at': 'array-at',
|
|
63
|
-
'Array.prototype.findLast': 'array-find-last',
|
|
64
|
-
'Array.prototype.findLastIndex': 'array-find-last',
|
|
65
|
-
'String.prototype.at': 'string-at',
|
|
66
|
-
'String.prototype.replaceAll': 'string-replace-all',
|
|
67
|
-
'Object.hasOwn': 'object-has-own',
|
|
68
|
-
'ResizeObserver': 'resize-observer',
|
|
69
|
-
'IntersectionObserver': 'intersection-observer',
|
|
70
|
-
'MutationObserver': 'mutation-observer',
|
|
71
|
-
'PerformanceObserver': 'performance-observer',
|
|
72
|
-
'BroadcastChannel': 'broadcast-channel',
|
|
73
|
-
'MessageChannel': 'message-channel',
|
|
74
|
-
'SharedArrayBuffer': 'shared-array-buffer',
|
|
75
|
-
'Atomics': 'atomics',
|
|
76
|
-
'BigInt': 'bigint',
|
|
77
|
-
'WeakRef': 'weak-ref',
|
|
78
|
-
'FinalizationRegistry': 'finalization-registry',
|
|
79
|
-
|
|
80
|
-
// Canvas and WebGL APIs
|
|
81
|
-
'CanvasRenderingContext2D.filter': 'canvas-filter',
|
|
82
|
-
'CanvasRenderingContext2D.reset': 'canvas-reset',
|
|
83
|
-
'WebGL2RenderingContext': 'webgl2',
|
|
84
|
-
'OffscreenCanvas': 'offscreen-canvas',
|
|
85
|
-
'OffscreenCanvasRenderingContext2D': 'offscreen-canvas',
|
|
86
|
-
'ImageBitmap': 'image-bitmap',
|
|
87
|
-
'createImageBitmap': 'image-bitmap',
|
|
88
|
-
'Path2D': 'path2d',
|
|
89
|
-
|
|
90
|
-
// WebRTC APIs
|
|
91
|
-
'RTCPeerConnection': 'webrtc',
|
|
92
|
-
'getUserMedia': 'getusermedia',
|
|
93
|
-
'RTCDataChannel': 'rtc-data-channel',
|
|
94
|
-
'RTCRtpTransceiver': 'rtc-rtp-transceiver',
|
|
95
|
-
'RTCStatsReport': 'rtc-stats',
|
|
96
|
-
'RTCIceCandidate': 'rtc-ice-candidate',
|
|
97
|
-
'RTCSessionDescription': 'rtc-session-description',
|
|
98
|
-
|
|
99
|
-
// Service Workers and PWA
|
|
100
|
-
'ServiceWorker': 'service-workers',
|
|
101
|
-
'navigator.serviceWorker': 'service-workers',
|
|
102
|
-
'ServiceWorkerRegistration': 'service-workers',
|
|
103
|
-
'Cache': 'cache-api',
|
|
104
|
-
'CacheStorage': 'cache-api',
|
|
105
|
-
'PushManager': 'push-api',
|
|
106
|
-
'PushSubscription': 'push-api',
|
|
107
|
-
'NotificationEvent': 'notification-api',
|
|
108
|
-
'BackgroundSync': 'background-sync',
|
|
109
|
-
'PaymentRequest': 'payment-request',
|
|
110
|
-
'PaymentResponse': 'payment-request',
|
|
111
|
-
|
|
112
|
-
// WebAssembly
|
|
113
|
-
'WebAssembly': 'webassembly',
|
|
114
|
-
'WebAssembly.instantiate': 'webassembly',
|
|
115
|
-
'WebAssembly.compile': 'webassembly',
|
|
116
|
-
'WebAssembly.Module': 'webassembly',
|
|
117
|
-
'WebAssembly.Instance': 'webassembly',
|
|
118
|
-
'WebAssembly.Memory': 'webassembly',
|
|
119
|
-
'WebAssembly.Table': 'webassembly',
|
|
120
|
-
|
|
121
|
-
// JavaScript Syntax (ECMAScript features)
|
|
122
|
-
'optional-chaining': 'optional-chaining',
|
|
123
|
-
'nullish-coalescing': 'nullish-coalescing',
|
|
124
|
-
'private-fields': 'private-fields',
|
|
125
|
-
'private-methods': 'private-methods',
|
|
126
|
-
'static-class-fields': 'static-class-fields',
|
|
127
|
-
'top-level-await': 'top-level-await',
|
|
128
|
-
'import-assertions': 'import-assertions',
|
|
129
|
-
'import-meta': 'import-meta',
|
|
130
|
-
'dynamic-import': 'dynamic-import',
|
|
131
|
-
'async-iteration': 'async-iteration',
|
|
132
|
-
'for-await-of': 'async-iteration',
|
|
133
|
-
'destructuring': 'destructuring',
|
|
134
|
-
'rest-spread': 'rest-spread',
|
|
135
|
-
'template-literals': 'template-literals',
|
|
136
|
-
'arrow-functions': 'arrow-functions',
|
|
137
|
-
'const-let': 'const-let',
|
|
138
|
-
'default-parameters': 'default-parameters',
|
|
139
|
-
|
|
140
|
-
// HTML Elements and Attributes
|
|
141
|
-
'dialog': 'dialog',
|
|
142
|
-
'details': 'details-summary',
|
|
143
|
-
'summary': 'details-summary',
|
|
144
|
-
'loading="lazy"': 'loading-lazy',
|
|
145
|
-
'decoding="async"': 'image-decoding',
|
|
146
|
-
'input[type="date"]': 'input-date',
|
|
147
|
-
'input[type="time"]': 'input-time',
|
|
148
|
-
'input[type="datetime-local"]': 'input-datetime-local',
|
|
149
|
-
'input[type="month"]': 'input-month',
|
|
150
|
-
'input[type="week"]': 'input-week',
|
|
151
|
-
'input[type="color"]': 'input-color',
|
|
152
|
-
'input[type="range"]': 'input-range',
|
|
153
|
-
'input[type="search"]': 'input-search',
|
|
154
|
-
'input[type="tel"]': 'input-tel',
|
|
155
|
-
'input[type="url"]': 'input-url',
|
|
156
|
-
'input[type="email"]': 'input-email',
|
|
157
|
-
'input[type="number"]': 'input-number',
|
|
158
|
-
'datalist': 'datalist',
|
|
159
|
-
'output': 'output',
|
|
160
|
-
'progress': 'progress',
|
|
161
|
-
'meter': 'meter',
|
|
162
|
-
'picture': 'picture',
|
|
163
|
-
'source': 'picture',
|
|
164
|
-
'track': 'track',
|
|
165
|
-
'slot': 'slot',
|
|
166
|
-
'template': 'template',
|
|
167
|
-
|
|
168
|
-
// DOM APIs
|
|
169
|
-
'DOMMatrix': 'geometry-interfaces',
|
|
170
|
-
'DOMPoint': 'geometry-interfaces',
|
|
171
|
-
'DOMRect': 'geometry-interfaces',
|
|
172
|
-
'DOMQuad': 'geometry-interfaces',
|
|
173
|
-
'AbortController': 'abort-controller',
|
|
174
|
-
'AbortSignal': 'abort-controller',
|
|
175
|
-
'FormData': 'form-data',
|
|
176
|
-
'URLSearchParams': 'url-search-params',
|
|
177
|
-
'URL': 'url',
|
|
178
|
-
'URLPattern': 'url-pattern',
|
|
179
|
-
'Blob': 'blob',
|
|
180
|
-
'File': 'file',
|
|
181
|
-
'FileReader': 'file-reader',
|
|
182
|
-
'FileList': 'file-list',
|
|
183
|
-
'DataTransfer': 'data-transfer',
|
|
184
|
-
'ClipboardAPI': 'clipboard-api',
|
|
185
|
-
'navigator.clipboard': 'clipboard-api',
|
|
186
|
-
'Permissions': 'permissions-api',
|
|
187
|
-
'navigator.permissions': 'permissions-api',
|
|
188
|
-
'Geolocation': 'geolocation',
|
|
189
|
-
'navigator.geolocation': 'geolocation',
|
|
190
|
-
'DeviceOrientationEvent': 'device-orientation',
|
|
191
|
-
'DeviceMotionEvent': 'device-motion',
|
|
192
|
-
'Vibration': 'vibration',
|
|
193
|
-
'navigator.vibrate': 'vibration',
|
|
194
|
-
'Battery': 'battery-status',
|
|
195
|
-
'navigator.getBattery': 'battery-status',
|
|
196
|
-
'NetworkInformation': 'network-information',
|
|
197
|
-
'navigator.connection': 'network-information',
|
|
198
|
-
'MediaDevices': 'media-devices',
|
|
199
|
-
'navigator.mediaDevices': 'media-devices',
|
|
200
|
-
'MediaStream': 'media-stream',
|
|
201
|
-
'MediaRecorder': 'media-recorder',
|
|
202
|
-
'SpeechSynthesis': 'speech-synthesis',
|
|
203
|
-
'SpeechRecognition': 'speech-recognition',
|
|
204
|
-
'Gamepad': 'gamepad',
|
|
205
|
-
'navigator.getGamepads': 'gamepad',
|
|
206
|
-
'PointerEvent': 'pointer-events',
|
|
207
|
-
'TouchEvent': 'touch-events',
|
|
208
|
-
'WheelEvent': 'wheel-events',
|
|
209
|
-
'KeyboardEvent': 'keyboard-events',
|
|
210
|
-
'MouseEvent': 'mouse-events',
|
|
211
|
-
'FocusEvent': 'focus-events',
|
|
212
|
-
'InputEvent': 'input-events',
|
|
213
|
-
'CompositionEvent': 'composition-events',
|
|
214
|
-
'CustomEvent': 'custom-events',
|
|
215
|
-
'EventTarget': 'event-target',
|
|
216
|
-
'addEventListener': 'event-listeners',
|
|
217
|
-
'removeEventListener': 'event-listeners',
|
|
218
|
-
'dispatchEvent': 'event-dispatch',
|
|
219
|
-
'requestAnimationFrame': 'request-animation-frame',
|
|
220
|
-
'cancelAnimationFrame': 'request-animation-frame',
|
|
221
|
-
'requestIdleCallback': 'request-idle-callback',
|
|
222
|
-
'cancelIdleCallback': 'request-idle-callback',
|
|
223
|
-
'setTimeout': 'timers',
|
|
224
|
-
'setInterval': 'timers',
|
|
225
|
-
'clearTimeout': 'timers',
|
|
226
|
-
'clearInterval': 'timers',
|
|
227
|
-
'queueMicrotask': 'queue-microtask',
|
|
228
|
-
'fetch': 'fetch',
|
|
229
|
-
'Request': 'fetch',
|
|
230
|
-
'Response': 'fetch',
|
|
231
|
-
'Headers': 'fetch',
|
|
232
|
-
'XMLHttpRequest': 'xhr',
|
|
233
|
-
'EventSource': 'server-sent-events',
|
|
234
|
-
'WebSocket': 'websockets',
|
|
235
|
-
'History': 'history-api',
|
|
236
|
-
'history.pushState': 'history-api',
|
|
237
|
-
'history.replaceState': 'history-api',
|
|
238
|
-
'Location': 'location',
|
|
239
|
-
'Navigator': 'navigator',
|
|
240
|
-
'Screen': 'screen',
|
|
241
|
-
'Window': 'window',
|
|
242
|
-
'Document': 'document',
|
|
243
|
-
'Element': 'element',
|
|
244
|
-
'Node': 'node',
|
|
245
|
-
'DocumentFragment': 'document-fragment',
|
|
246
|
-
'ShadowRoot': 'shadow-dom',
|
|
247
|
-
'customElements': 'custom-elements',
|
|
248
|
-
'HTMLElement': 'html-element',
|
|
249
|
-
'SVGElement': 'svg-element'
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
export class BaselineChecker {
|
|
253
|
-
private webFeatures: any = null;
|
|
254
|
-
private featureCache = new Map<string, any>();
|
|
255
|
-
private initialized = false;
|
|
256
|
-
|
|
257
|
-
constructor() {
|
|
258
|
-
// Don't load web-features immediately - use lazy loading
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Initialize web-features data lazily
|
|
263
|
-
*/
|
|
264
|
-
private async ensureInitialized(): Promise<void> {
|
|
265
|
-
if (this.initialized) {
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
this.webFeatures = await LazyLoader.getWebFeatures();
|
|
271
|
-
this.initialized = true;
|
|
272
|
-
} catch (error) {
|
|
273
|
-
console.warn('Failed to load web-features data:', error);
|
|
274
|
-
this.webFeatures = { features: {}, browsers: {}, groups: {} };
|
|
275
|
-
this.initialized = true;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Get feature status from web-features package with caching
|
|
281
|
-
*/
|
|
282
|
-
private async getFeatureStatus(featureId: string): Promise<any> {
|
|
283
|
-
await this.ensureInitialized();
|
|
284
|
-
|
|
285
|
-
// Check cache first
|
|
286
|
-
if (this.featureCache.has(featureId)) {
|
|
287
|
-
return this.featureCache.get(featureId);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const feature = this.webFeatures?.features?.[featureId] || this.webFeatures?.[featureId];
|
|
291
|
-
|
|
292
|
-
// Cache the result to avoid repeated lookups
|
|
293
|
-
if (feature) {
|
|
294
|
-
// Optimize the feature data to reduce memory usage
|
|
295
|
-
const optimized = MemoryManager.optimizeObject({
|
|
296
|
-
name: feature.name,
|
|
297
|
-
status: feature.status,
|
|
298
|
-
support: feature.support,
|
|
299
|
-
baseline: feature.baseline
|
|
300
|
-
});
|
|
301
|
-
this.featureCache.set(featureId, optimized);
|
|
302
|
-
return optimized;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Cache null results too to avoid repeated failed lookups
|
|
306
|
-
this.featureCache.set(featureId, null);
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Map detected feature to web-features ID
|
|
312
|
-
*/
|
|
313
|
-
private mapFeatureToId(feature: string): string | null {
|
|
314
|
-
// Direct mapping
|
|
315
|
-
if (FEATURE_ID_MAP[feature]) {
|
|
316
|
-
return FEATURE_ID_MAP[feature];
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Try to find partial matches for complex features
|
|
320
|
-
for (const [key, value] of Object.entries(FEATURE_ID_MAP)) {
|
|
321
|
-
if (feature.includes(key) || key.includes(feature)) {
|
|
322
|
-
return value;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Check if the feature exists directly in web-features
|
|
327
|
-
if (this.webFeatures[feature]) {
|
|
328
|
-
return feature;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Check if a version is supported based on browser support data
|
|
336
|
-
*/
|
|
337
|
-
private isVersionSupported(browserSupport: any, minVersion: string): boolean {
|
|
338
|
-
if (!browserSupport) return false;
|
|
339
|
-
|
|
340
|
-
// If browser support is true, it's supported
|
|
341
|
-
if (browserSupport === true) return true;
|
|
342
|
-
|
|
343
|
-
// If browser support is false or null, it's not supported
|
|
344
|
-
if (!browserSupport) return false;
|
|
345
|
-
|
|
346
|
-
// If it's a version string, compare versions
|
|
347
|
-
if (typeof browserSupport === 'string') {
|
|
348
|
-
return this.compareVersions(browserSupport, minVersion) >= 0;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// If it's an object with version info, extract the version
|
|
352
|
-
if (typeof browserSupport === 'object' && browserSupport.version) {
|
|
353
|
-
return this.compareVersions(browserSupport.version, minVersion) >= 0;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Compare two version strings
|
|
361
|
-
*/
|
|
362
|
-
private compareVersions(version1: string, version2: string): number {
|
|
363
|
-
const v1Parts = version1.split('.').map(Number);
|
|
364
|
-
const v2Parts = version2.split('.').map(Number);
|
|
365
|
-
|
|
366
|
-
const maxLength = Math.max(v1Parts.length, v2Parts.length);
|
|
367
|
-
|
|
368
|
-
for (let i = 0; i < maxLength; i++) {
|
|
369
|
-
const v1Part = v1Parts[i] || 0;
|
|
370
|
-
const v2Part = v2Parts[i] || 0;
|
|
371
|
-
|
|
372
|
-
if (v1Part > v2Part) return 1;
|
|
373
|
-
if (v1Part < v2Part) return -1;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return 0;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Map browser name to web-features format
|
|
381
|
-
*/
|
|
382
|
-
private mapBrowserName(browser: string): string {
|
|
383
|
-
const browserMap: Record<string, string> = {
|
|
384
|
-
'chrome': 'chrome',
|
|
385
|
-
'firefox': 'firefox',
|
|
386
|
-
'safari': 'safari',
|
|
387
|
-
'edge': 'edge',
|
|
388
|
-
'opera': 'opera',
|
|
389
|
-
'samsung': 'samsung_android'
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
return browserMap[browser] || browser;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Create a violation object
|
|
397
|
-
*/
|
|
398
|
-
private createViolation(
|
|
399
|
-
detectedFeature: DetectedFeature,
|
|
400
|
-
target: BrowserTarget,
|
|
401
|
-
featureData: any,
|
|
402
|
-
featureId: string
|
|
403
|
-
): Violation {
|
|
404
|
-
const browserKey = this.mapBrowserName(target.browser);
|
|
405
|
-
const support = featureData?.status?.support;
|
|
406
|
-
const browserSupport = support ? (support as any)[browserKey] : undefined;
|
|
407
|
-
const baselineStatus = featureData?.status?.baseline;
|
|
408
|
-
|
|
409
|
-
let actual: string | false = false;
|
|
410
|
-
if (browserSupport === true) {
|
|
411
|
-
actual = 'supported';
|
|
412
|
-
} else if (typeof browserSupport === 'string') {
|
|
413
|
-
actual = browserSupport;
|
|
414
|
-
} else if (typeof browserSupport === 'object' && browserSupport?.version) {
|
|
415
|
-
actual = browserSupport.version;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
let reason = '';
|
|
419
|
-
if (target.minVersion === 'baseline' || target.minVersion === 'baseline-newly') {
|
|
420
|
-
if (baselineStatus === false) {
|
|
421
|
-
reason = 'Feature is not part of Baseline (not supported across all major browsers)';
|
|
422
|
-
} else if (baselineStatus === 'limited') {
|
|
423
|
-
reason = 'Feature has limited Baseline support';
|
|
424
|
-
}
|
|
425
|
-
} else {
|
|
426
|
-
reason = `Feature requires ${target.browser} ${actual || 'unknown'} but target is ${target.minVersion}`;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return {
|
|
430
|
-
feature: detectedFeature.feature,
|
|
431
|
-
featureId,
|
|
432
|
-
file: detectedFeature.file || 'unknown',
|
|
433
|
-
line: detectedFeature.line,
|
|
434
|
-
column: detectedFeature.column,
|
|
435
|
-
context: detectedFeature.context,
|
|
436
|
-
browser: target.browser,
|
|
437
|
-
required: target.minVersion,
|
|
438
|
-
actual,
|
|
439
|
-
baselineStatus: baselineStatus === false ? 'false' : baselineStatus === true ? 'widely' : String(baselineStatus || 'unknown'),
|
|
440
|
-
reason
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Check compatibility of a detected feature against browser targets
|
|
446
|
-
*/
|
|
447
|
-
async checkCompatibility(detectedFeature: DetectedFeature, targets: BrowserTarget[]): Promise<CompatibilityResult> {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
1
|
+
import type { BrowserTarget, Violation, DetectedFeature, CompatibilityResult } from '../types/index.js';
|
|
2
|
+
import { LazyLoader } from './lazy-loader.js';
|
|
3
|
+
import { MemoryManager } from './memory-manager.js';
|
|
4
|
+
|
|
5
|
+
// Comprehensive feature mapping dictionary for ALL web platform features
|
|
6
|
+
const FEATURE_ID_MAP: Record<string, string> = {
|
|
7
|
+
// CSS Properties
|
|
8
|
+
'container-type': 'container-queries',
|
|
9
|
+
'container-name': 'container-queries',
|
|
10
|
+
'container': 'container-queries',
|
|
11
|
+
'aspect-ratio': 'aspect-ratio',
|
|
12
|
+
'gap': 'grid-gap',
|
|
13
|
+
'row-gap': 'grid-gap',
|
|
14
|
+
'column-gap': 'grid-gap',
|
|
15
|
+
'scroll-behavior': 'scroll-behavior',
|
|
16
|
+
'backdrop-filter': 'backdrop-filter',
|
|
17
|
+
'color-scheme': 'color-scheme',
|
|
18
|
+
'accent-color': 'accent-color',
|
|
19
|
+
'overscroll-behavior': 'overscroll-behavior',
|
|
20
|
+
'scroll-snap-type': 'scroll-snap-type',
|
|
21
|
+
'scroll-snap-align': 'scroll-snap-align',
|
|
22
|
+
'object-fit': 'object-fit',
|
|
23
|
+
'object-position': 'object-position',
|
|
24
|
+
'place-items': 'place-items',
|
|
25
|
+
'place-content': 'place-content',
|
|
26
|
+
'place-self': 'place-self',
|
|
27
|
+
'inset': 'inset',
|
|
28
|
+
'inset-block': 'inset',
|
|
29
|
+
'inset-inline': 'inset',
|
|
30
|
+
'block-size': 'logical-properties',
|
|
31
|
+
'inline-size': 'logical-properties',
|
|
32
|
+
'margin-block': 'logical-properties',
|
|
33
|
+
'margin-inline': 'logical-properties',
|
|
34
|
+
'padding-block': 'logical-properties',
|
|
35
|
+
'padding-inline': 'logical-properties',
|
|
36
|
+
'border-block': 'logical-properties',
|
|
37
|
+
'border-inline': 'logical-properties',
|
|
38
|
+
|
|
39
|
+
// CSS Selectors
|
|
40
|
+
':has()': 'has',
|
|
41
|
+
':is()': 'is',
|
|
42
|
+
':where()': 'where',
|
|
43
|
+
':focus-visible': 'focus-visible',
|
|
44
|
+
':focus-within': 'focus-within',
|
|
45
|
+
':target-within': 'target-within',
|
|
46
|
+
'::backdrop': 'backdrop',
|
|
47
|
+
'::marker': 'marker',
|
|
48
|
+
'::part()': 'part',
|
|
49
|
+
'::slotted()': 'slotted',
|
|
50
|
+
|
|
51
|
+
// CSS At-rules
|
|
52
|
+
'@supports': 'supports',
|
|
53
|
+
'@container': 'container-queries',
|
|
54
|
+
'@layer': 'cascade-layers',
|
|
55
|
+
'@scope': 'scope',
|
|
56
|
+
|
|
57
|
+
// JavaScript Web APIs
|
|
58
|
+
'HTMLDialogElement.showModal': 'dialog',
|
|
59
|
+
'HTMLDialogElement.show': 'dialog',
|
|
60
|
+
'HTMLDialogElement.close': 'dialog',
|
|
61
|
+
'structuredClone': 'structured-clone',
|
|
62
|
+
'Array.prototype.at': 'array-at',
|
|
63
|
+
'Array.prototype.findLast': 'array-find-last',
|
|
64
|
+
'Array.prototype.findLastIndex': 'array-find-last',
|
|
65
|
+
'String.prototype.at': 'string-at',
|
|
66
|
+
'String.prototype.replaceAll': 'string-replace-all',
|
|
67
|
+
'Object.hasOwn': 'object-has-own',
|
|
68
|
+
'ResizeObserver': 'resize-observer',
|
|
69
|
+
'IntersectionObserver': 'intersection-observer',
|
|
70
|
+
'MutationObserver': 'mutation-observer',
|
|
71
|
+
'PerformanceObserver': 'performance-observer',
|
|
72
|
+
'BroadcastChannel': 'broadcast-channel',
|
|
73
|
+
'MessageChannel': 'message-channel',
|
|
74
|
+
'SharedArrayBuffer': 'shared-array-buffer',
|
|
75
|
+
'Atomics': 'atomics',
|
|
76
|
+
'BigInt': 'bigint',
|
|
77
|
+
'WeakRef': 'weak-ref',
|
|
78
|
+
'FinalizationRegistry': 'finalization-registry',
|
|
79
|
+
|
|
80
|
+
// Canvas and WebGL APIs
|
|
81
|
+
'CanvasRenderingContext2D.filter': 'canvas-filter',
|
|
82
|
+
'CanvasRenderingContext2D.reset': 'canvas-reset',
|
|
83
|
+
'WebGL2RenderingContext': 'webgl2',
|
|
84
|
+
'OffscreenCanvas': 'offscreen-canvas',
|
|
85
|
+
'OffscreenCanvasRenderingContext2D': 'offscreen-canvas',
|
|
86
|
+
'ImageBitmap': 'image-bitmap',
|
|
87
|
+
'createImageBitmap': 'image-bitmap',
|
|
88
|
+
'Path2D': 'path2d',
|
|
89
|
+
|
|
90
|
+
// WebRTC APIs
|
|
91
|
+
'RTCPeerConnection': 'webrtc',
|
|
92
|
+
'getUserMedia': 'getusermedia',
|
|
93
|
+
'RTCDataChannel': 'rtc-data-channel',
|
|
94
|
+
'RTCRtpTransceiver': 'rtc-rtp-transceiver',
|
|
95
|
+
'RTCStatsReport': 'rtc-stats',
|
|
96
|
+
'RTCIceCandidate': 'rtc-ice-candidate',
|
|
97
|
+
'RTCSessionDescription': 'rtc-session-description',
|
|
98
|
+
|
|
99
|
+
// Service Workers and PWA
|
|
100
|
+
'ServiceWorker': 'service-workers',
|
|
101
|
+
'navigator.serviceWorker': 'service-workers',
|
|
102
|
+
'ServiceWorkerRegistration': 'service-workers',
|
|
103
|
+
'Cache': 'cache-api',
|
|
104
|
+
'CacheStorage': 'cache-api',
|
|
105
|
+
'PushManager': 'push-api',
|
|
106
|
+
'PushSubscription': 'push-api',
|
|
107
|
+
'NotificationEvent': 'notification-api',
|
|
108
|
+
'BackgroundSync': 'background-sync',
|
|
109
|
+
'PaymentRequest': 'payment-request',
|
|
110
|
+
'PaymentResponse': 'payment-request',
|
|
111
|
+
|
|
112
|
+
// WebAssembly
|
|
113
|
+
'WebAssembly': 'webassembly',
|
|
114
|
+
'WebAssembly.instantiate': 'webassembly',
|
|
115
|
+
'WebAssembly.compile': 'webassembly',
|
|
116
|
+
'WebAssembly.Module': 'webassembly',
|
|
117
|
+
'WebAssembly.Instance': 'webassembly',
|
|
118
|
+
'WebAssembly.Memory': 'webassembly',
|
|
119
|
+
'WebAssembly.Table': 'webassembly',
|
|
120
|
+
|
|
121
|
+
// JavaScript Syntax (ECMAScript features)
|
|
122
|
+
'optional-chaining': 'optional-chaining',
|
|
123
|
+
'nullish-coalescing': 'nullish-coalescing',
|
|
124
|
+
'private-fields': 'private-fields',
|
|
125
|
+
'private-methods': 'private-methods',
|
|
126
|
+
'static-class-fields': 'static-class-fields',
|
|
127
|
+
'top-level-await': 'top-level-await',
|
|
128
|
+
'import-assertions': 'import-assertions',
|
|
129
|
+
'import-meta': 'import-meta',
|
|
130
|
+
'dynamic-import': 'dynamic-import',
|
|
131
|
+
'async-iteration': 'async-iteration',
|
|
132
|
+
'for-await-of': 'async-iteration',
|
|
133
|
+
'destructuring': 'destructuring',
|
|
134
|
+
'rest-spread': 'rest-spread',
|
|
135
|
+
'template-literals': 'template-literals',
|
|
136
|
+
'arrow-functions': 'arrow-functions',
|
|
137
|
+
'const-let': 'const-let',
|
|
138
|
+
'default-parameters': 'default-parameters',
|
|
139
|
+
|
|
140
|
+
// HTML Elements and Attributes
|
|
141
|
+
'dialog': 'dialog',
|
|
142
|
+
'details': 'details-summary',
|
|
143
|
+
'summary': 'details-summary',
|
|
144
|
+
'loading="lazy"': 'loading-lazy',
|
|
145
|
+
'decoding="async"': 'image-decoding',
|
|
146
|
+
'input[type="date"]': 'input-date',
|
|
147
|
+
'input[type="time"]': 'input-time',
|
|
148
|
+
'input[type="datetime-local"]': 'input-datetime-local',
|
|
149
|
+
'input[type="month"]': 'input-month',
|
|
150
|
+
'input[type="week"]': 'input-week',
|
|
151
|
+
'input[type="color"]': 'input-color',
|
|
152
|
+
'input[type="range"]': 'input-range',
|
|
153
|
+
'input[type="search"]': 'input-search',
|
|
154
|
+
'input[type="tel"]': 'input-tel',
|
|
155
|
+
'input[type="url"]': 'input-url',
|
|
156
|
+
'input[type="email"]': 'input-email',
|
|
157
|
+
'input[type="number"]': 'input-number',
|
|
158
|
+
'datalist': 'datalist',
|
|
159
|
+
'output': 'output',
|
|
160
|
+
'progress': 'progress',
|
|
161
|
+
'meter': 'meter',
|
|
162
|
+
'picture': 'picture',
|
|
163
|
+
'source': 'picture',
|
|
164
|
+
'track': 'track',
|
|
165
|
+
'slot': 'slot',
|
|
166
|
+
'template': 'template',
|
|
167
|
+
|
|
168
|
+
// DOM APIs
|
|
169
|
+
'DOMMatrix': 'geometry-interfaces',
|
|
170
|
+
'DOMPoint': 'geometry-interfaces',
|
|
171
|
+
'DOMRect': 'geometry-interfaces',
|
|
172
|
+
'DOMQuad': 'geometry-interfaces',
|
|
173
|
+
'AbortController': 'abort-controller',
|
|
174
|
+
'AbortSignal': 'abort-controller',
|
|
175
|
+
'FormData': 'form-data',
|
|
176
|
+
'URLSearchParams': 'url-search-params',
|
|
177
|
+
'URL': 'url',
|
|
178
|
+
'URLPattern': 'url-pattern',
|
|
179
|
+
'Blob': 'blob',
|
|
180
|
+
'File': 'file',
|
|
181
|
+
'FileReader': 'file-reader',
|
|
182
|
+
'FileList': 'file-list',
|
|
183
|
+
'DataTransfer': 'data-transfer',
|
|
184
|
+
'ClipboardAPI': 'clipboard-api',
|
|
185
|
+
'navigator.clipboard': 'clipboard-api',
|
|
186
|
+
'Permissions': 'permissions-api',
|
|
187
|
+
'navigator.permissions': 'permissions-api',
|
|
188
|
+
'Geolocation': 'geolocation',
|
|
189
|
+
'navigator.geolocation': 'geolocation',
|
|
190
|
+
'DeviceOrientationEvent': 'device-orientation',
|
|
191
|
+
'DeviceMotionEvent': 'device-motion',
|
|
192
|
+
'Vibration': 'vibration',
|
|
193
|
+
'navigator.vibrate': 'vibration',
|
|
194
|
+
'Battery': 'battery-status',
|
|
195
|
+
'navigator.getBattery': 'battery-status',
|
|
196
|
+
'NetworkInformation': 'network-information',
|
|
197
|
+
'navigator.connection': 'network-information',
|
|
198
|
+
'MediaDevices': 'media-devices',
|
|
199
|
+
'navigator.mediaDevices': 'media-devices',
|
|
200
|
+
'MediaStream': 'media-stream',
|
|
201
|
+
'MediaRecorder': 'media-recorder',
|
|
202
|
+
'SpeechSynthesis': 'speech-synthesis',
|
|
203
|
+
'SpeechRecognition': 'speech-recognition',
|
|
204
|
+
'Gamepad': 'gamepad',
|
|
205
|
+
'navigator.getGamepads': 'gamepad',
|
|
206
|
+
'PointerEvent': 'pointer-events',
|
|
207
|
+
'TouchEvent': 'touch-events',
|
|
208
|
+
'WheelEvent': 'wheel-events',
|
|
209
|
+
'KeyboardEvent': 'keyboard-events',
|
|
210
|
+
'MouseEvent': 'mouse-events',
|
|
211
|
+
'FocusEvent': 'focus-events',
|
|
212
|
+
'InputEvent': 'input-events',
|
|
213
|
+
'CompositionEvent': 'composition-events',
|
|
214
|
+
'CustomEvent': 'custom-events',
|
|
215
|
+
'EventTarget': 'event-target',
|
|
216
|
+
'addEventListener': 'event-listeners',
|
|
217
|
+
'removeEventListener': 'event-listeners',
|
|
218
|
+
'dispatchEvent': 'event-dispatch',
|
|
219
|
+
'requestAnimationFrame': 'request-animation-frame',
|
|
220
|
+
'cancelAnimationFrame': 'request-animation-frame',
|
|
221
|
+
'requestIdleCallback': 'request-idle-callback',
|
|
222
|
+
'cancelIdleCallback': 'request-idle-callback',
|
|
223
|
+
'setTimeout': 'timers',
|
|
224
|
+
'setInterval': 'timers',
|
|
225
|
+
'clearTimeout': 'timers',
|
|
226
|
+
'clearInterval': 'timers',
|
|
227
|
+
'queueMicrotask': 'queue-microtask',
|
|
228
|
+
'fetch': 'fetch',
|
|
229
|
+
'Request': 'fetch',
|
|
230
|
+
'Response': 'fetch',
|
|
231
|
+
'Headers': 'fetch',
|
|
232
|
+
'XMLHttpRequest': 'xhr',
|
|
233
|
+
'EventSource': 'server-sent-events',
|
|
234
|
+
'WebSocket': 'websockets',
|
|
235
|
+
'History': 'history-api',
|
|
236
|
+
'history.pushState': 'history-api',
|
|
237
|
+
'history.replaceState': 'history-api',
|
|
238
|
+
'Location': 'location',
|
|
239
|
+
'Navigator': 'navigator',
|
|
240
|
+
'Screen': 'screen',
|
|
241
|
+
'Window': 'window',
|
|
242
|
+
'Document': 'document',
|
|
243
|
+
'Element': 'element',
|
|
244
|
+
'Node': 'node',
|
|
245
|
+
'DocumentFragment': 'document-fragment',
|
|
246
|
+
'ShadowRoot': 'shadow-dom',
|
|
247
|
+
'customElements': 'custom-elements',
|
|
248
|
+
'HTMLElement': 'html-element',
|
|
249
|
+
'SVGElement': 'svg-element'
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export class BaselineChecker {
|
|
253
|
+
private webFeatures: any = null;
|
|
254
|
+
private featureCache = new Map<string, any>();
|
|
255
|
+
private initialized = false;
|
|
256
|
+
|
|
257
|
+
constructor() {
|
|
258
|
+
// Don't load web-features immediately - use lazy loading
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Initialize web-features data lazily
|
|
263
|
+
*/
|
|
264
|
+
private async ensureInitialized(): Promise<void> {
|
|
265
|
+
if (this.initialized) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
this.webFeatures = await LazyLoader.getWebFeatures();
|
|
271
|
+
this.initialized = true;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.warn('Failed to load web-features data:', error);
|
|
274
|
+
this.webFeatures = { features: {}, browsers: {}, groups: {} };
|
|
275
|
+
this.initialized = true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get feature status from web-features package with caching
|
|
281
|
+
*/
|
|
282
|
+
private async getFeatureStatus(featureId: string): Promise<any> {
|
|
283
|
+
await this.ensureInitialized();
|
|
284
|
+
|
|
285
|
+
// Check cache first
|
|
286
|
+
if (this.featureCache.has(featureId)) {
|
|
287
|
+
return this.featureCache.get(featureId);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const feature = this.webFeatures?.features?.[featureId] || this.webFeatures?.[featureId];
|
|
291
|
+
|
|
292
|
+
// Cache the result to avoid repeated lookups
|
|
293
|
+
if (feature) {
|
|
294
|
+
// Optimize the feature data to reduce memory usage
|
|
295
|
+
const optimized = MemoryManager.optimizeObject({
|
|
296
|
+
name: feature.name,
|
|
297
|
+
status: feature.status,
|
|
298
|
+
support: feature.support,
|
|
299
|
+
baseline: feature.baseline
|
|
300
|
+
});
|
|
301
|
+
this.featureCache.set(featureId, optimized);
|
|
302
|
+
return optimized;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Cache null results too to avoid repeated failed lookups
|
|
306
|
+
this.featureCache.set(featureId, null);
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Map detected feature to web-features ID
|
|
312
|
+
*/
|
|
313
|
+
private mapFeatureToId(feature: string): string | null {
|
|
314
|
+
// Direct mapping
|
|
315
|
+
if (FEATURE_ID_MAP[feature]) {
|
|
316
|
+
return FEATURE_ID_MAP[feature];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Try to find partial matches for complex features
|
|
320
|
+
for (const [key, value] of Object.entries(FEATURE_ID_MAP)) {
|
|
321
|
+
if (feature.includes(key) || key.includes(feature)) {
|
|
322
|
+
return value;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Check if the feature exists directly in web-features
|
|
327
|
+
if (this.webFeatures?.[feature] || this.webFeatures?.features?.[feature]) {
|
|
328
|
+
return feature;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Check if a version is supported based on browser support data
|
|
336
|
+
*/
|
|
337
|
+
private isVersionSupported(browserSupport: any, minVersion: string): boolean {
|
|
338
|
+
if (!browserSupport) return false;
|
|
339
|
+
|
|
340
|
+
// If browser support is true, it's supported
|
|
341
|
+
if (browserSupport === true) return true;
|
|
342
|
+
|
|
343
|
+
// If browser support is false or null, it's not supported
|
|
344
|
+
if (!browserSupport) return false;
|
|
345
|
+
|
|
346
|
+
// If it's a version string, compare versions
|
|
347
|
+
if (typeof browserSupport === 'string') {
|
|
348
|
+
return this.compareVersions(browserSupport, minVersion) >= 0;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// If it's an object with version info, extract the version
|
|
352
|
+
if (typeof browserSupport === 'object' && browserSupport.version) {
|
|
353
|
+
return this.compareVersions(browserSupport.version, minVersion) >= 0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Compare two version strings
|
|
361
|
+
*/
|
|
362
|
+
private compareVersions(version1: string, version2: string): number {
|
|
363
|
+
const v1Parts = version1.split('.').map(Number);
|
|
364
|
+
const v2Parts = version2.split('.').map(Number);
|
|
365
|
+
|
|
366
|
+
const maxLength = Math.max(v1Parts.length, v2Parts.length);
|
|
367
|
+
|
|
368
|
+
for (let i = 0; i < maxLength; i++) {
|
|
369
|
+
const v1Part = v1Parts[i] || 0;
|
|
370
|
+
const v2Part = v2Parts[i] || 0;
|
|
371
|
+
|
|
372
|
+
if (v1Part > v2Part) return 1;
|
|
373
|
+
if (v1Part < v2Part) return -1;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Map browser name to web-features format
|
|
381
|
+
*/
|
|
382
|
+
private mapBrowserName(browser: string): string {
|
|
383
|
+
const browserMap: Record<string, string> = {
|
|
384
|
+
'chrome': 'chrome',
|
|
385
|
+
'firefox': 'firefox',
|
|
386
|
+
'safari': 'safari',
|
|
387
|
+
'edge': 'edge',
|
|
388
|
+
'opera': 'opera',
|
|
389
|
+
'samsung': 'samsung_android'
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
return browserMap[browser] || browser;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Create a violation object
|
|
397
|
+
*/
|
|
398
|
+
private createViolation(
|
|
399
|
+
detectedFeature: DetectedFeature,
|
|
400
|
+
target: BrowserTarget,
|
|
401
|
+
featureData: any,
|
|
402
|
+
featureId: string
|
|
403
|
+
): Violation {
|
|
404
|
+
const browserKey = this.mapBrowserName(target.browser);
|
|
405
|
+
const support = featureData?.status?.support;
|
|
406
|
+
const browserSupport = support ? (support as any)[browserKey] : undefined;
|
|
407
|
+
const baselineStatus = featureData?.status?.baseline;
|
|
408
|
+
|
|
409
|
+
let actual: string | false = false;
|
|
410
|
+
if (browserSupport === true) {
|
|
411
|
+
actual = 'supported';
|
|
412
|
+
} else if (typeof browserSupport === 'string') {
|
|
413
|
+
actual = browserSupport;
|
|
414
|
+
} else if (typeof browserSupport === 'object' && browserSupport?.version) {
|
|
415
|
+
actual = browserSupport.version;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let reason = '';
|
|
419
|
+
if (target.minVersion === 'baseline' || target.minVersion === 'baseline-newly') {
|
|
420
|
+
if (baselineStatus === false) {
|
|
421
|
+
reason = 'Feature is not part of Baseline (not supported across all major browsers)';
|
|
422
|
+
} else if (baselineStatus === 'limited') {
|
|
423
|
+
reason = 'Feature has limited Baseline support';
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
reason = `Feature requires ${target.browser} ${actual || 'unknown'} but target is ${target.minVersion}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
feature: detectedFeature.feature,
|
|
431
|
+
featureId,
|
|
432
|
+
file: detectedFeature.file || 'unknown',
|
|
433
|
+
line: detectedFeature.line,
|
|
434
|
+
column: detectedFeature.column,
|
|
435
|
+
context: detectedFeature.context,
|
|
436
|
+
browser: target.browser,
|
|
437
|
+
required: target.minVersion,
|
|
438
|
+
actual,
|
|
439
|
+
baselineStatus: baselineStatus === false ? 'false' : baselineStatus === true ? 'widely' : String(baselineStatus || 'unknown'),
|
|
440
|
+
reason
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Check compatibility of a detected feature against browser targets
|
|
446
|
+
*/
|
|
447
|
+
async checkCompatibility(detectedFeature: DetectedFeature, targets: BrowserTarget[]): Promise<CompatibilityResult> {
|
|
448
|
+
await this.ensureInitialized();
|
|
449
|
+
const featureId = this.mapFeatureToId(detectedFeature.feature);
|
|
450
|
+
|
|
451
|
+
if (!featureId) {
|
|
452
|
+
// Feature not found in mapping, assume it's compatible
|
|
453
|
+
return {
|
|
454
|
+
violations: [],
|
|
455
|
+
featureData: null
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const featureData = await this.getFeatureStatus(featureId);
|
|
460
|
+
|
|
461
|
+
if (!featureData) {
|
|
462
|
+
// Feature not found in web-features, assume it's compatible
|
|
463
|
+
return {
|
|
464
|
+
violations: [],
|
|
465
|
+
featureData: null
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const violations: Violation[] = [];
|
|
470
|
+
|
|
471
|
+
for (const target of targets) {
|
|
472
|
+
if (target.minVersion === 'baseline' || target.minVersion === 'baseline-newly') {
|
|
473
|
+
// Check baseline status
|
|
474
|
+
const baselineStatus = featureData.status?.baseline;
|
|
475
|
+
|
|
476
|
+
if (baselineStatus === false) {
|
|
477
|
+
violations.push(this.createViolation(detectedFeature, target, featureData, featureId));
|
|
478
|
+
} else if (target.minVersion === 'baseline-newly' && baselineStatus === 'low') {
|
|
479
|
+
violations.push(this.createViolation(detectedFeature, target, featureData, featureId));
|
|
480
|
+
}
|
|
481
|
+
} else {
|
|
482
|
+
// Check specific version - map browser names to web-features format
|
|
483
|
+
const browserKey = this.mapBrowserName(target.browser);
|
|
484
|
+
const support = featureData.status?.support;
|
|
485
|
+
const browserSupport = support ? (support as any)[browserKey] : undefined;
|
|
486
|
+
|
|
487
|
+
if (!this.isVersionSupported(browserSupport, target.minVersion)) {
|
|
488
|
+
violations.push(this.createViolation(detectedFeature, target, featureData, featureId));
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
violations,
|
|
495
|
+
featureData
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Check multiple features against browser targets with memory optimization
|
|
501
|
+
*/
|
|
502
|
+
async checkMultipleFeatures(detectedFeatures: DetectedFeature[], targets: BrowserTarget[]): Promise<Violation[]> {
|
|
503
|
+
const violationTracker = MemoryManager.createViolationTracker();
|
|
504
|
+
|
|
505
|
+
// Process in batches to manage memory usage
|
|
506
|
+
await MemoryManager.processBatches(
|
|
507
|
+
detectedFeatures,
|
|
508
|
+
async (batch) => {
|
|
509
|
+
const batchResults = await Promise.all(
|
|
510
|
+
batch.map(feature => this.checkCompatibility(feature, targets))
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
for (const result of batchResults) {
|
|
514
|
+
for (const violation of result.violations) {
|
|
515
|
+
violationTracker.addViolation(violation);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return [];
|
|
520
|
+
},
|
|
521
|
+
50 // Process 50 features at a time
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
return violationTracker.getViolations();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Get available feature IDs for debugging/testing
|
|
529
|
+
*/
|
|
530
|
+
async getAvailableFeatureIds(): Promise<string[]> {
|
|
531
|
+
await this.ensureInitialized();
|
|
532
|
+
return Object.keys(this.webFeatures?.features || this.webFeatures || {});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Clear caches to free memory
|
|
537
|
+
*/
|
|
538
|
+
clearCache(): void {
|
|
539
|
+
this.featureCache.clear();
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Get cache statistics
|
|
544
|
+
*/
|
|
545
|
+
getCacheStats(): {
|
|
546
|
+
cacheSize: number;
|
|
547
|
+
memoryEstimate: string;
|
|
548
|
+
} {
|
|
549
|
+
const cacheSize = this.featureCache.size;
|
|
550
|
+
const memoryEstimate = `${Math.round(cacheSize * 500 / 1024)}KB`; // Rough estimate
|
|
551
|
+
|
|
552
|
+
return {
|
|
553
|
+
cacheSize,
|
|
554
|
+
memoryEstimate
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Get feature mapping for debugging/testing
|
|
560
|
+
*/
|
|
561
|
+
getFeatureMapping(): Record<string, string> {
|
|
562
|
+
return { ...FEATURE_ID_MAP };
|
|
563
|
+
}
|
|
564
|
+
}
|