@pwfabric/runtime 0.0.0-stage → 1.0.0-rc.1
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/LICENSE +90 -0
- package/README.md +43 -2
- package/dist/a11y/announcer.d.ts +65 -0
- package/dist/a11y/announcer.js +140 -0
- package/dist/a11y/announcer.js.map +1 -0
- package/dist/a11y/contrast.d.ts +54 -0
- package/dist/a11y/contrast.js +93 -0
- package/dist/a11y/contrast.js.map +1 -0
- package/dist/a11y/focus.d.ts +123 -0
- package/dist/a11y/focus.js +282 -0
- package/dist/a11y/focus.js.map +1 -0
- package/dist/a11y/index.d.ts +21 -0
- package/dist/a11y/index.js +18 -0
- package/dist/a11y/index.js.map +1 -0
- package/dist/a11y/keyboard.d.ts +144 -0
- package/dist/a11y/keyboard.js +317 -0
- package/dist/a11y/keyboard.js.map +1 -0
- package/dist/adapters/default-image.d.ts +25 -0
- package/dist/adapters/default-image.js +37 -0
- package/dist/adapters/default-image.js.map +1 -0
- package/dist/adapters/index.d.ts +27 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/next-image.d.ts +38 -0
- package/dist/adapters/next-image.js +124 -0
- package/dist/adapters/next-image.js.map +1 -0
- package/dist/adapters/types.d.ts +61 -0
- package/dist/adapters/types.js +11 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/analytics/index.d.ts +14 -0
- package/dist/analytics/index.js +15 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/tracker.d.ts +65 -0
- package/dist/analytics/tracker.js +251 -0
- package/dist/analytics/tracker.js.map +1 -0
- package/dist/analytics/types.d.ts +130 -0
- package/dist/analytics/types.js +11 -0
- package/dist/analytics/types.js.map +1 -0
- package/dist/auth/index.d.ts +15 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/provider.d.ts +132 -0
- package/dist/auth/provider.js +473 -0
- package/dist/auth/provider.js.map +1 -0
- package/dist/auth/types.d.ts +117 -0
- package/dist/auth/types.js +9 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/binding/collection.d.ts +190 -0
- package/dist/binding/collection.js +358 -0
- package/dist/binding/collection.js.map +1 -0
- package/dist/binding/datasource.d.ts +319 -0
- package/dist/binding/datasource.js +556 -0
- package/dist/binding/datasource.js.map +1 -0
- package/dist/binding/engine.d.ts +65 -0
- package/dist/binding/engine.js +268 -0
- package/dist/binding/engine.js.map +1 -0
- package/dist/binding/expression.d.ts +150 -0
- package/dist/binding/expression.js +787 -0
- package/dist/binding/expression.js.map +1 -0
- package/dist/binding/index.d.ts +19 -0
- package/dist/binding/index.js +14 -0
- package/dist/binding/index.js.map +1 -0
- package/dist/binding/two-way.d.ts +149 -0
- package/dist/binding/two-way.js +462 -0
- package/dist/binding/two-way.js.map +1 -0
- package/dist/binding/types.d.ts +119 -0
- package/dist/binding/types.js +9 -0
- package/dist/binding/types.js.map +1 -0
- package/dist/blocks/component-types.d.ts +81 -0
- package/dist/blocks/component-types.js +11 -0
- package/dist/blocks/component-types.js.map +1 -0
- package/dist/blocks/composition-rules.d.ts +168 -0
- package/dist/blocks/composition-rules.js +391 -0
- package/dist/blocks/composition-rules.js.map +1 -0
- package/dist/blocks/hooks.d.ts +31 -0
- package/dist/blocks/hooks.js +42 -0
- package/dist/blocks/hooks.js.map +1 -0
- package/dist/blocks/index.d.ts +14 -0
- package/dist/blocks/index.js +23 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/registry.d.ts +84 -0
- package/dist/blocks/registry.js +136 -0
- package/dist/blocks/registry.js.map +1 -0
- package/dist/blocks/rule-engine.d.ts +131 -0
- package/dist/blocks/rule-engine.js +321 -0
- package/dist/blocks/rule-engine.js.map +1 -0
- package/dist/blocks/surface-event-context.d.ts +13 -0
- package/dist/blocks/surface-event-context.js +14 -0
- package/dist/blocks/surface-event-context.js.map +1 -0
- package/dist/blocks/surface-id-context.d.ts +13 -0
- package/dist/blocks/surface-id-context.js +15 -0
- package/dist/blocks/surface-id-context.js.map +1 -0
- package/dist/bus.d.ts +41 -0
- package/dist/bus.js +158 -0
- package/dist/bus.js.map +1 -0
- package/dist/cache/index.d.ts +9 -0
- package/dist/cache/index.js +10 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/memory.d.ts +32 -0
- package/dist/cache/memory.js +138 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/types.d.ts +62 -0
- package/dist/cache/types.js +9 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/capability/index.d.ts +72 -0
- package/dist/capability/index.js +87 -0
- package/dist/capability/index.js.map +1 -0
- package/dist/capability/kernel-capabilities.d.ts +31 -0
- package/dist/capability/kernel-capabilities.js +36 -0
- package/dist/capability/kernel-capabilities.js.map +1 -0
- package/dist/capability/platform-capabilities.d.ts +71 -0
- package/dist/capability/platform-capabilities.js +267 -0
- package/dist/capability/platform-capabilities.js.map +1 -0
- package/dist/capability/type-mapping.d.ts +70 -0
- package/dist/capability/type-mapping.js +107 -0
- package/dist/capability/type-mapping.js.map +1 -0
- package/dist/capability/validation.d.ts +46 -0
- package/dist/capability/validation.js +54 -0
- package/dist/capability/validation.js.map +1 -0
- package/dist/connectors/declared-connector.d.ts +48 -0
- package/dist/connectors/declared-connector.js +333 -0
- package/dist/connectors/declared-connector.js.map +1 -0
- package/dist/connectors/engine-registry.d.ts +50 -0
- package/dist/connectors/engine-registry.js +135 -0
- package/dist/connectors/engine-registry.js.map +1 -0
- package/dist/connectors/index.d.ts +14 -0
- package/dist/connectors/index.js +14 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/module-loader.d.ts +70 -0
- package/dist/connectors/module-loader.js +143 -0
- package/dist/connectors/module-loader.js.map +1 -0
- package/dist/connectors/types.d.ts +174 -0
- package/dist/connectors/types.js +23 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/context.d.ts +27 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/data/DataCapabilityProvider.d.ts +39 -0
- package/dist/data/DataCapabilityProvider.js +58 -0
- package/dist/data/DataCapabilityProvider.js.map +1 -0
- package/dist/data/index.d.ts +17 -0
- package/dist/data/index.js +18 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/manager.d.ts +40 -0
- package/dist/data/manager.js +442 -0
- package/dist/data/manager.js.map +1 -0
- package/dist/data/types.d.ts +218 -0
- package/dist/data/types.js +2 -0
- package/dist/data/types.js.map +1 -0
- package/dist/entity/aggregate.d.ts +8 -0
- package/dist/entity/aggregate.js +24 -0
- package/dist/entity/aggregate.js.map +1 -0
- package/dist/entity/apply-query.d.ts +58 -0
- package/dist/entity/apply-query.js +264 -0
- package/dist/entity/apply-query.js.map +1 -0
- package/dist/entity/behavior.d.ts +94 -0
- package/dist/entity/behavior.js +78 -0
- package/dist/entity/behavior.js.map +1 -0
- package/dist/entity/connector.d.ts +141 -0
- package/dist/entity/connector.js +89 -0
- package/dist/entity/connector.js.map +1 -0
- package/dist/entity/connectors/index.d.ts +12 -0
- package/dist/entity/connectors/index.js +11 -0
- package/dist/entity/connectors/index.js.map +1 -0
- package/dist/entity/connectors/static.d.ts +51 -0
- package/dist/entity/connectors/static.js +245 -0
- package/dist/entity/connectors/static.js.map +1 -0
- package/dist/entity/entity-datasource.d.ts +50 -0
- package/dist/entity/entity-datasource.js +33 -0
- package/dist/entity/entity-datasource.js.map +1 -0
- package/dist/entity/facade.d.ts +70 -0
- package/dist/entity/facade.js +70 -0
- package/dist/entity/facade.js.map +1 -0
- package/dist/entity/http-connector.d.ts +123 -0
- package/dist/entity/http-connector.js +295 -0
- package/dist/entity/http-connector.js.map +1 -0
- package/dist/entity/index.d.ts +38 -0
- package/dist/entity/index.js +31 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/entity/postgres-connector.d.ts +64 -0
- package/dist/entity/postgres-connector.js +239 -0
- package/dist/entity/postgres-connector.js.map +1 -0
- package/dist/entity/registry.d.ts +59 -0
- package/dist/entity/registry.js +90 -0
- package/dist/entity/registry.js.map +1 -0
- package/dist/entity/store.d.ts +66 -0
- package/dist/entity/store.js +349 -0
- package/dist/entity/store.js.map +1 -0
- package/dist/entity/types.d.ts +180 -0
- package/dist/entity/types.js +10 -0
- package/dist/entity/types.js.map +1 -0
- package/dist/execution-context.d.ts +88 -0
- package/dist/execution-context.js +146 -0
- package/dist/execution-context.js.map +1 -0
- package/dist/executor.d.ts +27 -0
- package/dist/executor.js +33 -0
- package/dist/executor.js.map +1 -0
- package/dist/forms/form-entity-bridge.d.ts +94 -0
- package/dist/forms/form-entity-bridge.js +96 -0
- package/dist/forms/form-entity-bridge.js.map +1 -0
- package/dist/forms/form-write-target.d.ts +34 -0
- package/dist/forms/form-write-target.js +81 -0
- package/dist/forms/form-write-target.js.map +1 -0
- package/dist/forms/index.d.ts +22 -0
- package/dist/forms/index.js +25 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/named-control-values.d.ts +27 -0
- package/dist/forms/named-control-values.js +42 -0
- package/dist/forms/named-control-values.js.map +1 -0
- package/dist/forms/processor.d.ts +55 -0
- package/dist/forms/processor.js +154 -0
- package/dist/forms/processor.js.map +1 -0
- package/dist/forms/types.d.ts +174 -0
- package/dist/forms/types.js +33 -0
- package/dist/forms/types.js.map +1 -0
- package/dist/forms/validator.d.ts +61 -0
- package/dist/forms/validator.js +218 -0
- package/dist/forms/validator.js.map +1 -0
- package/dist/graph/index.d.ts +10 -0
- package/dist/graph/index.js +9 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/resolver.d.ts +66 -0
- package/dist/graph/resolver.js +243 -0
- package/dist/graph/resolver.js.map +1 -0
- package/dist/graph/types.d.ts +125 -0
- package/dist/graph/types.js +9 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/hooks/useInstalledAtoms.d.ts +96 -0
- package/dist/hooks/useInstalledAtoms.js +92 -0
- package/dist/hooks/useInstalledAtoms.js.map +1 -0
- package/dist/i18n/index.d.ts +15 -0
- package/dist/i18n/index.js +14 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/translator.d.ts +76 -0
- package/dist/i18n/translator.js +357 -0
- package/dist/i18n/translator.js.map +1 -0
- package/dist/i18n/types.d.ts +93 -0
- package/dist/i18n/types.js +9 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +47 -0
- package/dist/kernel.js +258 -0
- package/dist/kernel.js.map +1 -0
- package/dist/perf/budget.d.ts +92 -0
- package/dist/perf/budget.js +122 -0
- package/dist/perf/budget.js.map +1 -0
- package/dist/perf/index.d.ts +16 -0
- package/dist/perf/index.js +13 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/render-timer.d.ts +90 -0
- package/dist/perf/render-timer.js +136 -0
- package/dist/perf/render-timer.js.map +1 -0
- package/dist/perf/reporter.d.ts +126 -0
- package/dist/perf/reporter.js +241 -0
- package/dist/perf/reporter.js.map +1 -0
- package/dist/perf/web-vitals.d.ts +111 -0
- package/dist/perf/web-vitals.js +268 -0
- package/dist/perf/web-vitals.js.map +1 -0
- package/dist/registry.d.ts +63 -0
- package/dist/registry.js +126 -0
- package/dist/registry.js.map +1 -0
- package/dist/render/BehaviorSurfaceProvider.d.ts +65 -0
- package/dist/render/BehaviorSurfaceProvider.js +185 -0
- package/dist/render/BehaviorSurfaceProvider.js.map +1 -0
- package/dist/render/BlockErrorBoundary.d.ts +26 -0
- package/dist/render/BlockErrorBoundary.js +41 -0
- package/dist/render/BlockErrorBoundary.js.map +1 -0
- package/dist/render/DataBoundSurfaceRenderer.d.ts +78 -0
- package/dist/render/DataBoundSurfaceRenderer.js +998 -0
- package/dist/render/DataBoundSurfaceRenderer.js.map +1 -0
- package/dist/render/DynamicBlockWrapper.d.ts +23 -0
- package/dist/render/DynamicBlockWrapper.js +117 -0
- package/dist/render/DynamicBlockWrapper.js.map +1 -0
- package/dist/render/appearance-resolver.d.ts +50 -0
- package/dist/render/appearance-resolver.js +78 -0
- package/dist/render/appearance-resolver.js.map +1 -0
- package/dist/render/capability-gate.d.ts +69 -0
- package/dist/render/capability-gate.js +94 -0
- package/dist/render/capability-gate.js.map +1 -0
- package/dist/render/conditional-states-css.d.ts +41 -0
- package/dist/render/conditional-states-css.js +82 -0
- package/dist/render/conditional-states-css.js.map +1 -0
- package/dist/render/connections-context.d.ts +31 -0
- package/dist/render/connections-context.js +34 -0
- package/dist/render/connections-context.js.map +1 -0
- package/dist/render/dark-theme-css.d.ts +36 -0
- package/dist/render/dark-theme-css.js +67 -0
- package/dist/render/dark-theme-css.js.map +1 -0
- package/dist/render/data-binding-types.d.ts +191 -0
- package/dist/render/data-binding-types.js +11 -0
- package/dist/render/data-binding-types.js.map +1 -0
- package/dist/render/data-cache.d.ts +30 -0
- package/dist/render/data-cache.js +37 -0
- package/dist/render/data-cache.js.map +1 -0
- package/dist/render/derive-dark-theme.d.ts +17 -0
- package/dist/render/derive-dark-theme.js +18 -0
- package/dist/render/derive-dark-theme.js.map +1 -0
- package/dist/render/dynamic-artifact-context.d.ts +77 -0
- package/dist/render/dynamic-artifact-context.js +89 -0
- package/dist/render/dynamic-artifact-context.js.map +1 -0
- package/dist/render/dynamic-block-loader.d.ts +80 -0
- package/dist/render/dynamic-block-loader.js +197 -0
- package/dist/render/dynamic-block-loader.js.map +1 -0
- package/dist/render/endpoint.d.ts +18 -0
- package/dist/render/endpoint.js +109 -0
- package/dist/render/endpoint.js.map +1 -0
- package/dist/render/entity-facade-context.d.ts +34 -0
- package/dist/render/entity-facade-context.js +37 -0
- package/dist/render/entity-facade-context.js.map +1 -0
- package/dist/render/group-states-css.d.ts +41 -0
- package/dist/render/group-states-css.js +73 -0
- package/dist/render/group-states-css.js.map +1 -0
- package/dist/render/hooks/useSurfaceEvents.d.ts +8 -0
- package/dist/render/hooks/useSurfaceEvents.js +9 -0
- package/dist/render/hooks/useSurfaceEvents.js.map +1 -0
- package/dist/render/hooks/useSurfaceId.d.ts +8 -0
- package/dist/render/hooks/useSurfaceId.js +9 -0
- package/dist/render/hooks/useSurfaceId.js.map +1 -0
- package/dist/render/index.d.ts +48 -0
- package/dist/render/index.js +61 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/interaction-rule-engine.d.ts +12 -0
- package/dist/render/interaction-rule-engine.js +47 -0
- package/dist/render/interaction-rule-engine.js.map +1 -0
- package/dist/render/keyframes-css.d.ts +31 -0
- package/dist/render/keyframes-css.js +44 -0
- package/dist/render/keyframes-css.js.map +1 -0
- package/dist/render/query-state.d.ts +3 -0
- package/dist/render/query-state.js +70 -0
- package/dist/render/query-state.js.map +1 -0
- package/dist/render/renderer.d.ts +44 -0
- package/dist/render/renderer.js +937 -0
- package/dist/render/renderer.js.map +1 -0
- package/dist/render/resolve-expressions.d.ts +37 -0
- package/dist/render/resolve-expressions.js +309 -0
- package/dist/render/resolve-expressions.js.map +1 -0
- package/dist/render/responsive-style-css.d.ts +42 -0
- package/dist/render/responsive-style-css.js +123 -0
- package/dist/render/responsive-style-css.js.map +1 -0
- package/dist/render/responsive.d.ts +49 -0
- package/dist/render/responsive.js +99 -0
- package/dist/render/responsive.js.map +1 -0
- package/dist/render/scoped-css.d.ts +71 -0
- package/dist/render/scoped-css.js +102 -0
- package/dist/render/scoped-css.js.map +1 -0
- package/dist/render/shell-selection.d.ts +69 -0
- package/dist/render/shell-selection.js +117 -0
- package/dist/render/shell-selection.js.map +1 -0
- package/dist/render/states-css.d.ts +32 -0
- package/dist/render/states-css.js +63 -0
- package/dist/render/states-css.js.map +1 -0
- package/dist/render/surface-data-context.d.ts +18 -0
- package/dist/render/surface-data-context.js +21 -0
- package/dist/render/surface-data-context.js.map +1 -0
- package/dist/render/surface-event-bus.d.ts +19 -0
- package/dist/render/surface-event-bus.js +68 -0
- package/dist/render/surface-event-bus.js.map +1 -0
- package/dist/render/surface-event-context.d.ts +11 -0
- package/dist/render/surface-event-context.js +12 -0
- package/dist/render/surface-event-context.js.map +1 -0
- package/dist/render/surface-id-context.d.ts +8 -0
- package/dist/render/surface-id-context.js +9 -0
- package/dist/render/surface-id-context.js.map +1 -0
- package/dist/render/theme-utils.d.ts +14 -0
- package/dist/render/theme-utils.js +15 -0
- package/dist/render/theme-utils.js.map +1 -0
- package/dist/render/types.d.ts +282 -0
- package/dist/render/types.js +9 -0
- package/dist/render/types.js.map +1 -0
- package/dist/render/visibility-css.d.ts +42 -0
- package/dist/render/visibility-css.js +119 -0
- package/dist/render/visibility-css.js.map +1 -0
- package/dist/render/width-scope.d.ts +34 -0
- package/dist/render/width-scope.js +44 -0
- package/dist/render/width-scope.js.map +1 -0
- package/dist/render/write-only-sources.d.ts +33 -0
- package/dist/render/write-only-sources.js +99 -0
- package/dist/render/write-only-sources.js.map +1 -0
- package/dist/routing/index.d.ts +14 -0
- package/dist/routing/index.js +15 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/router.d.ts +42 -0
- package/dist/routing/router.js +218 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/routing/types.d.ts +124 -0
- package/dist/routing/types.js +9 -0
- package/dist/routing/types.js.map +1 -0
- package/dist/seo/generator.d.ts +39 -0
- package/dist/seo/generator.js +236 -0
- package/dist/seo/generator.js.map +1 -0
- package/dist/seo/index.d.ts +14 -0
- package/dist/seo/index.js +15 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/types.d.ts +181 -0
- package/dist/seo/types.js +9 -0
- package/dist/seo/types.js.map +1 -0
- package/dist/state/index.d.ts +15 -0
- package/dist/state/index.js +16 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/manager.d.ts +33 -0
- package/dist/state/manager.js +81 -0
- package/dist/state/manager.js.map +1 -0
- package/dist/state/store.d.ts +40 -0
- package/dist/state/store.js +145 -0
- package/dist/state/store.js.map +1 -0
- package/dist/state/types.d.ts +107 -0
- package/dist/state/types.js +9 -0
- package/dist/state/types.js.map +1 -0
- package/dist/surface/expression.d.ts +65 -0
- package/dist/surface/expression.js +179 -0
- package/dist/surface/expression.js.map +1 -0
- package/dist/surface/index.d.ts +32 -0
- package/dist/surface/index.js +25 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/surface/interpreter.d.ts +96 -0
- package/dist/surface/interpreter.js +231 -0
- package/dist/surface/interpreter.js.map +1 -0
- package/dist/surface/layout.d.ts +54 -0
- package/dist/surface/layout.js +288 -0
- package/dist/surface/layout.js.map +1 -0
- package/dist/surface/output.d.ts +90 -0
- package/dist/surface/output.js +104 -0
- package/dist/surface/output.js.map +1 -0
- package/dist/surface/primitives.d.ts +180 -0
- package/dist/surface/primitives.js +339 -0
- package/dist/surface/primitives.js.map +1 -0
- package/dist/surface/renderer.d.ts +96 -0
- package/dist/surface/renderer.js +190 -0
- package/dist/surface/renderer.js.map +1 -0
- package/dist/surface/schemas.d.ts +181 -0
- package/dist/surface/schemas.js +142 -0
- package/dist/surface/schemas.js.map +1 -0
- package/dist/surface/transforms.d.ts +144 -0
- package/dist/surface/transforms.js +120 -0
- package/dist/surface/transforms.js.map +1 -0
- package/dist/surface/types.d.ts +160 -0
- package/dist/surface/types.js +51 -0
- package/dist/surface/types.js.map +1 -0
- package/dist/surface/validation.d.ts +123 -0
- package/dist/surface/validation.js +147 -0
- package/dist/surface/validation.js.map +1 -0
- package/dist/validator.d.ts +55 -0
- package/dist/validator.js +100 -0
- package/dist/validator.js.map +1 -0
- package/package.json +106 -3
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Metric Reporter
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates Web Vitals collection, render timing, and budget
|
|
5
|
+
* checking into a unified reporting pipeline. Supports periodic
|
|
6
|
+
* automatic reporting or manual on-demand reports.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/perf
|
|
9
|
+
*/
|
|
10
|
+
import { WebVitalsCollector } from './web-vitals.js';
|
|
11
|
+
import { RenderTimer } from './render-timer.js';
|
|
12
|
+
import { BudgetChecker } from './budget.js';
|
|
13
|
+
// ============================================================
|
|
14
|
+
// DEFAULTS
|
|
15
|
+
// ============================================================
|
|
16
|
+
const DEFAULT_SLOWEST_COUNT = 5;
|
|
17
|
+
// ============================================================
|
|
18
|
+
// REPORTER
|
|
19
|
+
// ============================================================
|
|
20
|
+
/**
|
|
21
|
+
* Orchestrates performance monitoring components and generates
|
|
22
|
+
* unified performance reports.
|
|
23
|
+
*
|
|
24
|
+
* Manages a WebVitalsCollector, RenderTimer, and BudgetChecker
|
|
25
|
+
* internally. Supports automatic periodic reporting or manual
|
|
26
|
+
* on-demand report generation.
|
|
27
|
+
*/
|
|
28
|
+
export class PerformanceReporter {
|
|
29
|
+
destination;
|
|
30
|
+
onReportCallback;
|
|
31
|
+
interval;
|
|
32
|
+
includeRenderMetrics;
|
|
33
|
+
includeWebVitals;
|
|
34
|
+
webVitals;
|
|
35
|
+
renderTimer;
|
|
36
|
+
budgetChecker;
|
|
37
|
+
intervalId;
|
|
38
|
+
/**
|
|
39
|
+
* @param config - Reporter configuration
|
|
40
|
+
*/
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.destination = config.destination;
|
|
43
|
+
this.onReportCallback = config.onReport;
|
|
44
|
+
this.interval = config.interval ?? 0;
|
|
45
|
+
this.includeRenderMetrics = config.includeRenderMetrics ?? true;
|
|
46
|
+
this.includeWebVitals = config.includeWebVitals ?? true;
|
|
47
|
+
// Initialize sub-components
|
|
48
|
+
if (this.includeWebVitals) {
|
|
49
|
+
this.webVitals = new WebVitalsCollector();
|
|
50
|
+
}
|
|
51
|
+
if (this.includeRenderMetrics) {
|
|
52
|
+
this.renderTimer = new RenderTimer();
|
|
53
|
+
}
|
|
54
|
+
if (config.budget) {
|
|
55
|
+
this.budgetChecker = new BudgetChecker(config.budget);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Start periodic reporting and Web Vitals collection.
|
|
60
|
+
*
|
|
61
|
+
* If an interval is configured, reports will be generated
|
|
62
|
+
* automatically at that interval. Web Vitals collection
|
|
63
|
+
* starts immediately.
|
|
64
|
+
*/
|
|
65
|
+
start() {
|
|
66
|
+
// Start web vitals collection
|
|
67
|
+
this.webVitals?.startCollecting();
|
|
68
|
+
// Set up periodic reporting
|
|
69
|
+
if (this.interval > 0) {
|
|
70
|
+
this.intervalId = setInterval(() => {
|
|
71
|
+
void this.report();
|
|
72
|
+
}, this.interval);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate and deliver a performance report.
|
|
77
|
+
*
|
|
78
|
+
* Collects metrics from all active sub-components, runs
|
|
79
|
+
* budget checks if configured, and delivers the report
|
|
80
|
+
* to the configured destination.
|
|
81
|
+
*
|
|
82
|
+
* @returns The generated performance report
|
|
83
|
+
*/
|
|
84
|
+
async report() {
|
|
85
|
+
const performanceReport = {
|
|
86
|
+
timestamp: Date.now(),
|
|
87
|
+
webVitals: this.buildWebVitalsSection(),
|
|
88
|
+
renderMetrics: this.buildRenderMetricsSection(),
|
|
89
|
+
budgetCheck: this.buildBudgetCheckSection(),
|
|
90
|
+
};
|
|
91
|
+
await this.deliverReport(performanceReport);
|
|
92
|
+
return performanceReport;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Stop periodic reporting.
|
|
96
|
+
*
|
|
97
|
+
* Does not stop Web Vitals collection or clear history.
|
|
98
|
+
* Use `dispose()` for full cleanup.
|
|
99
|
+
*/
|
|
100
|
+
stop() {
|
|
101
|
+
if (this.intervalId !== undefined) {
|
|
102
|
+
clearInterval(this.intervalId);
|
|
103
|
+
this.intervalId = undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get the internal WebVitalsCollector.
|
|
108
|
+
*
|
|
109
|
+
* Useful for registering custom metric callbacks.
|
|
110
|
+
*
|
|
111
|
+
* @returns The collector, or undefined if Web Vitals are disabled
|
|
112
|
+
*/
|
|
113
|
+
getWebVitals() {
|
|
114
|
+
return this.webVitals;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get the internal RenderTimer.
|
|
118
|
+
*
|
|
119
|
+
* Useful for tracking surface render timing externally.
|
|
120
|
+
*
|
|
121
|
+
* @returns The timer, or undefined if render metrics are disabled
|
|
122
|
+
*/
|
|
123
|
+
getRenderTimer() {
|
|
124
|
+
return this.renderTimer;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Stop reporting and release all resources.
|
|
128
|
+
*/
|
|
129
|
+
dispose() {
|
|
130
|
+
this.stop();
|
|
131
|
+
this.webVitals?.dispose();
|
|
132
|
+
this.renderTimer?.clear();
|
|
133
|
+
this.webVitals = undefined;
|
|
134
|
+
this.renderTimer = undefined;
|
|
135
|
+
this.budgetChecker = undefined;
|
|
136
|
+
}
|
|
137
|
+
// ----------------------------------------------------------
|
|
138
|
+
// Private: report building
|
|
139
|
+
// ----------------------------------------------------------
|
|
140
|
+
buildWebVitalsSection() {
|
|
141
|
+
if (!this.includeWebVitals || !this.webVitals) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
return this.webVitals.getReport();
|
|
145
|
+
}
|
|
146
|
+
buildRenderMetricsSection() {
|
|
147
|
+
if (!this.includeRenderMetrics || !this.renderTimer) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const history = this.renderTimer.getHistory();
|
|
151
|
+
return {
|
|
152
|
+
averageRenderTime: this.renderTimer.getAverageRenderTime(),
|
|
153
|
+
totalRenders: history.length,
|
|
154
|
+
slowestRenders: this.renderTimer.getSlowest(DEFAULT_SLOWEST_COUNT),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
buildBudgetCheckSection() {
|
|
158
|
+
if (!this.budgetChecker) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
// Collect all available numeric metrics for budget checking
|
|
162
|
+
const metrics = {};
|
|
163
|
+
// Web Vitals
|
|
164
|
+
if (this.webVitals) {
|
|
165
|
+
const vitalsReport = this.webVitals.getReport();
|
|
166
|
+
if (vitalsReport.lcp)
|
|
167
|
+
metrics['lcp'] = vitalsReport.lcp.value;
|
|
168
|
+
if (vitalsReport.fid)
|
|
169
|
+
metrics['fid'] = vitalsReport.fid.value;
|
|
170
|
+
if (vitalsReport.cls)
|
|
171
|
+
metrics['cls'] = vitalsReport.cls.value;
|
|
172
|
+
if (vitalsReport.fcp)
|
|
173
|
+
metrics['fcp'] = vitalsReport.fcp.value;
|
|
174
|
+
if (vitalsReport.ttfb)
|
|
175
|
+
metrics['ttfb'] = vitalsReport.ttfb.value;
|
|
176
|
+
}
|
|
177
|
+
// Render metrics
|
|
178
|
+
if (this.renderTimer) {
|
|
179
|
+
const avg = this.renderTimer.getAverageRenderTime();
|
|
180
|
+
if (avg > 0) {
|
|
181
|
+
metrics['maxRenderTime'] = avg;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return this.budgetChecker.check(metrics);
|
|
185
|
+
}
|
|
186
|
+
// ----------------------------------------------------------
|
|
187
|
+
// Private: report delivery
|
|
188
|
+
// ----------------------------------------------------------
|
|
189
|
+
async deliverReport(performanceReport) {
|
|
190
|
+
switch (this.destination) {
|
|
191
|
+
case 'console':
|
|
192
|
+
this.logToConsole(performanceReport);
|
|
193
|
+
break;
|
|
194
|
+
case 'custom':
|
|
195
|
+
if (this.onReportCallback) {
|
|
196
|
+
await this.onReportCallback(performanceReport);
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
logToConsole(performanceReport) {
|
|
202
|
+
if (typeof globalThis.console === 'undefined') {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const log = globalThis.console;
|
|
206
|
+
const group = typeof log.group === 'function' ? log.group.bind(log) : undefined;
|
|
207
|
+
const groupEnd = typeof log.groupEnd === 'function' ? log.groupEnd.bind(log) : undefined;
|
|
208
|
+
if (group) {
|
|
209
|
+
group('[PWFabric Perf Report]');
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
log.warn('[PWFabric Perf Report]');
|
|
213
|
+
}
|
|
214
|
+
if (performanceReport.webVitals) {
|
|
215
|
+
log.warn('Web Vitals:', performanceReport.webVitals);
|
|
216
|
+
}
|
|
217
|
+
if (performanceReport.renderMetrics) {
|
|
218
|
+
log.warn('Render Metrics:', performanceReport.renderMetrics);
|
|
219
|
+
}
|
|
220
|
+
if (performanceReport.budgetCheck) {
|
|
221
|
+
const status = performanceReport.budgetCheck.passed ? 'PASS' : 'FAIL';
|
|
222
|
+
log.warn(`Budget Check: ${status}`, performanceReport.budgetCheck.violations);
|
|
223
|
+
}
|
|
224
|
+
if (groupEnd) {
|
|
225
|
+
groupEnd();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// ============================================================
|
|
230
|
+
// FACTORY
|
|
231
|
+
// ============================================================
|
|
232
|
+
/**
|
|
233
|
+
* Create a new PerformanceReporter instance.
|
|
234
|
+
*
|
|
235
|
+
* @param config - Reporter configuration
|
|
236
|
+
* @returns A new PerformanceReporter
|
|
237
|
+
*/
|
|
238
|
+
export function createPerformanceReporter(config) {
|
|
239
|
+
return new PerformanceReporter(config);
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/perf/reporter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Performance Metric Reporter\n *\n * Orchestrates Web Vitals collection, render timing, and budget\n * checking into a unified reporting pipeline. Supports periodic\n * automatic reporting or manual on-demand reports.\n *\n * @module @pwfabric/runtime/perf\n */\n\nimport type { WebVitalsReport } from './web-vitals.js'\nimport type { RenderMetrics } from './render-timer.js'\nimport type { PerformanceBudget, BudgetCheckResult } from './budget.js'\nimport { WebVitalsCollector } from './web-vitals.js'\nimport { RenderTimer } from './render-timer.js'\nimport { BudgetChecker } from './budget.js'\n\n// ============================================================\n// TYPES\n// ============================================================\n\n/** Where to send performance reports */\nexport type ReportDestination = 'console' | 'custom'\n\n/** Reporter configuration */\nexport interface ReporterConfig {\n /** Report destination */\n readonly destination: ReportDestination\n /** Custom report handler (required when destination is 'custom') */\n readonly onReport?: (report: PerformanceReport) => void | Promise<void>\n /** Reporting interval in ms (0 = manual only, default: 0) */\n readonly interval?: number\n /** Whether to include render metrics (default: true) */\n readonly includeRenderMetrics?: boolean\n /** Whether to include Web Vitals (default: true) */\n readonly includeWebVitals?: boolean\n /** Performance budget to check against */\n readonly budget?: PerformanceBudget\n}\n\n/** A complete performance report */\nexport interface PerformanceReport {\n /** Timestamp when the report was generated */\n readonly timestamp: number\n /** Web Vitals snapshot */\n readonly webVitals?: WebVitalsReport | undefined\n /** Render timing summary */\n readonly renderMetrics?:\n | {\n readonly averageRenderTime: number\n readonly totalRenders: number\n readonly slowestRenders: readonly RenderMetrics[]\n }\n | undefined\n /** Budget check result */\n readonly budgetCheck?: BudgetCheckResult | undefined\n}\n\n// ============================================================\n// DEFAULTS\n// ============================================================\n\nconst DEFAULT_SLOWEST_COUNT = 5\n\n// ============================================================\n// REPORTER\n// ============================================================\n\n/**\n * Orchestrates performance monitoring components and generates\n * unified performance reports.\n *\n * Manages a WebVitalsCollector, RenderTimer, and BudgetChecker\n * internally. Supports automatic periodic reporting or manual\n * on-demand report generation.\n */\nexport class PerformanceReporter {\n private readonly destination: ReportDestination\n private readonly onReportCallback?:\n | ((report: PerformanceReport) => void | Promise<void>)\n | undefined\n private readonly interval: number\n private readonly includeRenderMetrics: boolean\n private readonly includeWebVitals: boolean\n\n private webVitals?: WebVitalsCollector | undefined\n private renderTimer?: RenderTimer | undefined\n private budgetChecker?: BudgetChecker | undefined\n private intervalId?: ReturnType<typeof setInterval> | undefined\n\n /**\n * @param config - Reporter configuration\n */\n constructor(config: ReporterConfig) {\n this.destination = config.destination\n this.onReportCallback = config.onReport\n this.interval = config.interval ?? 0\n this.includeRenderMetrics = config.includeRenderMetrics ?? true\n this.includeWebVitals = config.includeWebVitals ?? true\n\n // Initialize sub-components\n if (this.includeWebVitals) {\n this.webVitals = new WebVitalsCollector()\n }\n\n if (this.includeRenderMetrics) {\n this.renderTimer = new RenderTimer()\n }\n\n if (config.budget) {\n this.budgetChecker = new BudgetChecker(config.budget)\n }\n }\n\n /**\n * Start periodic reporting and Web Vitals collection.\n *\n * If an interval is configured, reports will be generated\n * automatically at that interval. Web Vitals collection\n * starts immediately.\n */\n start(): void {\n // Start web vitals collection\n this.webVitals?.startCollecting()\n\n // Set up periodic reporting\n if (this.interval > 0) {\n this.intervalId = setInterval(() => {\n void this.report()\n }, this.interval)\n }\n }\n\n /**\n * Generate and deliver a performance report.\n *\n * Collects metrics from all active sub-components, runs\n * budget checks if configured, and delivers the report\n * to the configured destination.\n *\n * @returns The generated performance report\n */\n async report(): Promise<PerformanceReport> {\n const performanceReport: PerformanceReport = {\n timestamp: Date.now(),\n webVitals: this.buildWebVitalsSection(),\n renderMetrics: this.buildRenderMetricsSection(),\n budgetCheck: this.buildBudgetCheckSection(),\n }\n\n await this.deliverReport(performanceReport)\n\n return performanceReport\n }\n\n /**\n * Stop periodic reporting.\n *\n * Does not stop Web Vitals collection or clear history.\n * Use `dispose()` for full cleanup.\n */\n stop(): void {\n if (this.intervalId !== undefined) {\n clearInterval(this.intervalId)\n this.intervalId = undefined\n }\n }\n\n /**\n * Get the internal WebVitalsCollector.\n *\n * Useful for registering custom metric callbacks.\n *\n * @returns The collector, or undefined if Web Vitals are disabled\n */\n getWebVitals(): WebVitalsCollector | undefined {\n return this.webVitals\n }\n\n /**\n * Get the internal RenderTimer.\n *\n * Useful for tracking surface render timing externally.\n *\n * @returns The timer, or undefined if render metrics are disabled\n */\n getRenderTimer(): RenderTimer | undefined {\n return this.renderTimer\n }\n\n /**\n * Stop reporting and release all resources.\n */\n dispose(): void {\n this.stop()\n this.webVitals?.dispose()\n this.renderTimer?.clear()\n this.webVitals = undefined\n this.renderTimer = undefined\n this.budgetChecker = undefined\n }\n\n // ----------------------------------------------------------\n // Private: report building\n // ----------------------------------------------------------\n\n private buildWebVitalsSection(): WebVitalsReport | undefined {\n if (!this.includeWebVitals || !this.webVitals) {\n return undefined\n }\n return this.webVitals.getReport()\n }\n\n private buildRenderMetricsSection(): PerformanceReport['renderMetrics'] | undefined {\n if (!this.includeRenderMetrics || !this.renderTimer) {\n return undefined\n }\n\n const history = this.renderTimer.getHistory()\n return {\n averageRenderTime: this.renderTimer.getAverageRenderTime(),\n totalRenders: history.length,\n slowestRenders: this.renderTimer.getSlowest(DEFAULT_SLOWEST_COUNT),\n }\n }\n\n private buildBudgetCheckSection(): BudgetCheckResult | undefined {\n if (!this.budgetChecker) {\n return undefined\n }\n\n // Collect all available numeric metrics for budget checking\n const metrics: Record<string, number> = {}\n\n // Web Vitals\n if (this.webVitals) {\n const vitalsReport = this.webVitals.getReport()\n if (vitalsReport.lcp) metrics['lcp'] = vitalsReport.lcp.value\n if (vitalsReport.fid) metrics['fid'] = vitalsReport.fid.value\n if (vitalsReport.cls) metrics['cls'] = vitalsReport.cls.value\n if (vitalsReport.fcp) metrics['fcp'] = vitalsReport.fcp.value\n if (vitalsReport.ttfb) metrics['ttfb'] = vitalsReport.ttfb.value\n }\n\n // Render metrics\n if (this.renderTimer) {\n const avg = this.renderTimer.getAverageRenderTime()\n if (avg > 0) {\n metrics['maxRenderTime'] = avg\n }\n }\n\n return this.budgetChecker.check(metrics)\n }\n\n // ----------------------------------------------------------\n // Private: report delivery\n // ----------------------------------------------------------\n\n private async deliverReport(performanceReport: PerformanceReport): Promise<void> {\n switch (this.destination) {\n case 'console':\n this.logToConsole(performanceReport)\n break\n\n case 'custom':\n if (this.onReportCallback) {\n await this.onReportCallback(performanceReport)\n }\n break\n }\n }\n\n private logToConsole(performanceReport: PerformanceReport): void {\n if (typeof globalThis.console === 'undefined') {\n return\n }\n\n const log = globalThis.console\n const group = typeof log.group === 'function' ? log.group.bind(log) : undefined\n const groupEnd = typeof log.groupEnd === 'function' ? log.groupEnd.bind(log) : undefined\n\n if (group) {\n group('[PWFabric Perf Report]')\n } else {\n log.warn('[PWFabric Perf Report]')\n }\n\n if (performanceReport.webVitals) {\n log.warn('Web Vitals:', performanceReport.webVitals)\n }\n\n if (performanceReport.renderMetrics) {\n log.warn('Render Metrics:', performanceReport.renderMetrics)\n }\n\n if (performanceReport.budgetCheck) {\n const status = performanceReport.budgetCheck.passed ? 'PASS' : 'FAIL'\n log.warn(`Budget Check: ${status}`, performanceReport.budgetCheck.violations)\n }\n\n if (groupEnd) {\n groupEnd()\n }\n }\n}\n\n// ============================================================\n// FACTORY\n// ============================================================\n\n/**\n * Create a new PerformanceReporter instance.\n *\n * @param config - Reporter configuration\n * @returns A new PerformanceReporter\n */\nexport function createPerformanceReporter(config: ReporterConfig): PerformanceReporter {\n return new PerformanceReporter(config)\n}\n"]}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Web Vitals Collection
|
|
3
|
+
*
|
|
4
|
+
* Collects Core Web Vitals (LCP, FID, CLS, FCP, TTFB, INP) using
|
|
5
|
+
* PerformanceObserver. Provides real-time metric collection with
|
|
6
|
+
* rating classification based on web.dev thresholds.
|
|
7
|
+
*
|
|
8
|
+
* All browser APIs are guarded for SSR safety.
|
|
9
|
+
*
|
|
10
|
+
* @module @pwfabric/runtime/perf
|
|
11
|
+
*/
|
|
12
|
+
/** Web Vital metric names */
|
|
13
|
+
export type WebVitalName = 'LCP' | 'FID' | 'CLS' | 'FCP' | 'TTFB' | 'INP';
|
|
14
|
+
/** Rating classification for a metric */
|
|
15
|
+
export type WebVitalRating = 'good' | 'needs-improvement' | 'poor';
|
|
16
|
+
/** A single Web Vital measurement */
|
|
17
|
+
export interface WebVitalMetric {
|
|
18
|
+
/** Metric name */
|
|
19
|
+
readonly name: WebVitalName;
|
|
20
|
+
/** Metric value (ms for timing metrics, unitless for CLS) */
|
|
21
|
+
readonly value: number;
|
|
22
|
+
/** Rating based on web.dev thresholds */
|
|
23
|
+
readonly rating: WebVitalRating;
|
|
24
|
+
/** Timestamp when the metric was collected */
|
|
25
|
+
readonly timestamp: number;
|
|
26
|
+
}
|
|
27
|
+
/** Callback invoked when a metric is collected */
|
|
28
|
+
export type WebVitalCallback = (metric: WebVitalMetric) => void;
|
|
29
|
+
/** Aggregated report of all collected Web Vitals */
|
|
30
|
+
export interface WebVitalsReport {
|
|
31
|
+
/** Largest Contentful Paint */
|
|
32
|
+
readonly lcp?: WebVitalMetric | undefined;
|
|
33
|
+
/** First Input Delay */
|
|
34
|
+
readonly fid?: WebVitalMetric | undefined;
|
|
35
|
+
/** Cumulative Layout Shift */
|
|
36
|
+
readonly cls?: WebVitalMetric | undefined;
|
|
37
|
+
/** First Contentful Paint */
|
|
38
|
+
readonly fcp?: WebVitalMetric | undefined;
|
|
39
|
+
/** Time to First Byte */
|
|
40
|
+
readonly ttfb?: WebVitalMetric | undefined;
|
|
41
|
+
/** Interaction to Next Paint */
|
|
42
|
+
readonly inp?: WebVitalMetric | undefined;
|
|
43
|
+
/** Timestamp when the report was generated */
|
|
44
|
+
readonly collectedAt: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Rate a metric value against web.dev thresholds.
|
|
48
|
+
*
|
|
49
|
+
* @param name - The metric name
|
|
50
|
+
* @param value - The measured value
|
|
51
|
+
* @returns The rating classification
|
|
52
|
+
*/
|
|
53
|
+
export declare function rateMetric(name: WebVitalName, value: number): WebVitalRating;
|
|
54
|
+
/**
|
|
55
|
+
* Collects Core Web Vitals using PerformanceObserver.
|
|
56
|
+
*
|
|
57
|
+
* Supports LCP, FID, CLS, FCP, TTFB, and INP metrics.
|
|
58
|
+
* All observers are cleaned up on dispose.
|
|
59
|
+
*/
|
|
60
|
+
export declare class WebVitalsCollector {
|
|
61
|
+
private metrics;
|
|
62
|
+
private observers;
|
|
63
|
+
private callbacks;
|
|
64
|
+
private clsValue;
|
|
65
|
+
private clsSessionEntries;
|
|
66
|
+
private clsSessionValue;
|
|
67
|
+
/**
|
|
68
|
+
* Start collecting all Web Vitals.
|
|
69
|
+
*
|
|
70
|
+
* Creates PerformanceObservers for each metric type.
|
|
71
|
+
* Safe to call in SSR environments (no-ops silently).
|
|
72
|
+
*/
|
|
73
|
+
startCollecting(): void;
|
|
74
|
+
/**
|
|
75
|
+
* Register a callback for a specific metric.
|
|
76
|
+
*
|
|
77
|
+
* The callback fires each time the metric is updated.
|
|
78
|
+
*
|
|
79
|
+
* @param name - The metric name to observe
|
|
80
|
+
* @param callback - Function to call with the metric
|
|
81
|
+
*/
|
|
82
|
+
onMetric(name: WebVitalName, callback: WebVitalCallback): void;
|
|
83
|
+
/**
|
|
84
|
+
* Get the current Web Vitals report.
|
|
85
|
+
*
|
|
86
|
+
* @returns A snapshot of all collected metrics
|
|
87
|
+
*/
|
|
88
|
+
getReport(): WebVitalsReport;
|
|
89
|
+
/**
|
|
90
|
+
* Stop collecting metrics and disconnect all observers.
|
|
91
|
+
*/
|
|
92
|
+
stopCollecting(): void;
|
|
93
|
+
/**
|
|
94
|
+
* Stop collecting and clear all state.
|
|
95
|
+
*/
|
|
96
|
+
dispose(): void;
|
|
97
|
+
private recordMetric;
|
|
98
|
+
private tryObserve;
|
|
99
|
+
private observeLCP;
|
|
100
|
+
private observeFID;
|
|
101
|
+
private observeCLS;
|
|
102
|
+
private observeFCP;
|
|
103
|
+
private observeTTFB;
|
|
104
|
+
private observeINP;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create a new WebVitalsCollector instance.
|
|
108
|
+
*
|
|
109
|
+
* @returns A new collector (not yet started)
|
|
110
|
+
*/
|
|
111
|
+
export declare function createWebVitalsCollector(): WebVitalsCollector;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Web Vitals Collection
|
|
3
|
+
*
|
|
4
|
+
* Collects Core Web Vitals (LCP, FID, CLS, FCP, TTFB, INP) using
|
|
5
|
+
* PerformanceObserver. Provides real-time metric collection with
|
|
6
|
+
* rating classification based on web.dev thresholds.
|
|
7
|
+
*
|
|
8
|
+
* All browser APIs are guarded for SSR safety.
|
|
9
|
+
*
|
|
10
|
+
* @module @pwfabric/runtime/perf
|
|
11
|
+
*/
|
|
12
|
+
// ============================================================
|
|
13
|
+
// THRESHOLDS (from web.dev)
|
|
14
|
+
// ============================================================
|
|
15
|
+
/** Rating thresholds: [goodThreshold, poorThreshold] */
|
|
16
|
+
const THRESHOLDS = {
|
|
17
|
+
LCP: [2500, 4000],
|
|
18
|
+
FID: [100, 300],
|
|
19
|
+
CLS: [0.1, 0.25],
|
|
20
|
+
FCP: [1800, 3000],
|
|
21
|
+
TTFB: [800, 1800],
|
|
22
|
+
INP: [200, 500],
|
|
23
|
+
};
|
|
24
|
+
// ============================================================
|
|
25
|
+
// UTILITY
|
|
26
|
+
// ============================================================
|
|
27
|
+
/**
|
|
28
|
+
* Rate a metric value against web.dev thresholds.
|
|
29
|
+
*
|
|
30
|
+
* @param name - The metric name
|
|
31
|
+
* @param value - The measured value
|
|
32
|
+
* @returns The rating classification
|
|
33
|
+
*/
|
|
34
|
+
export function rateMetric(name, value) {
|
|
35
|
+
const threshold = THRESHOLDS[name];
|
|
36
|
+
if (!threshold) {
|
|
37
|
+
return 'good';
|
|
38
|
+
}
|
|
39
|
+
if (value <= threshold[0]) {
|
|
40
|
+
return 'good';
|
|
41
|
+
}
|
|
42
|
+
if (value > threshold[1]) {
|
|
43
|
+
return 'poor';
|
|
44
|
+
}
|
|
45
|
+
return 'needs-improvement';
|
|
46
|
+
}
|
|
47
|
+
// ============================================================
|
|
48
|
+
// COLLECTOR
|
|
49
|
+
// ============================================================
|
|
50
|
+
/**
|
|
51
|
+
* Collects Core Web Vitals using PerformanceObserver.
|
|
52
|
+
*
|
|
53
|
+
* Supports LCP, FID, CLS, FCP, TTFB, and INP metrics.
|
|
54
|
+
* All observers are cleaned up on dispose.
|
|
55
|
+
*/
|
|
56
|
+
export class WebVitalsCollector {
|
|
57
|
+
metrics = new Map();
|
|
58
|
+
observers = [];
|
|
59
|
+
callbacks = new Map();
|
|
60
|
+
clsValue = 0;
|
|
61
|
+
clsSessionEntries = [];
|
|
62
|
+
clsSessionValue = 0;
|
|
63
|
+
/**
|
|
64
|
+
* Start collecting all Web Vitals.
|
|
65
|
+
*
|
|
66
|
+
* Creates PerformanceObservers for each metric type.
|
|
67
|
+
* Safe to call in SSR environments (no-ops silently).
|
|
68
|
+
*/
|
|
69
|
+
startCollecting() {
|
|
70
|
+
if (typeof PerformanceObserver === 'undefined') {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this.observeLCP();
|
|
74
|
+
this.observeFID();
|
|
75
|
+
this.observeCLS();
|
|
76
|
+
this.observeFCP();
|
|
77
|
+
this.observeTTFB();
|
|
78
|
+
this.observeINP();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Register a callback for a specific metric.
|
|
82
|
+
*
|
|
83
|
+
* The callback fires each time the metric is updated.
|
|
84
|
+
*
|
|
85
|
+
* @param name - The metric name to observe
|
|
86
|
+
* @param callback - Function to call with the metric
|
|
87
|
+
*/
|
|
88
|
+
onMetric(name, callback) {
|
|
89
|
+
const existing = this.callbacks.get(name) ?? [];
|
|
90
|
+
existing.push(callback);
|
|
91
|
+
this.callbacks.set(name, existing);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get the current Web Vitals report.
|
|
95
|
+
*
|
|
96
|
+
* @returns A snapshot of all collected metrics
|
|
97
|
+
*/
|
|
98
|
+
getReport() {
|
|
99
|
+
return {
|
|
100
|
+
lcp: this.metrics.get('LCP'),
|
|
101
|
+
fid: this.metrics.get('FID'),
|
|
102
|
+
cls: this.metrics.get('CLS'),
|
|
103
|
+
fcp: this.metrics.get('FCP'),
|
|
104
|
+
ttfb: this.metrics.get('TTFB'),
|
|
105
|
+
inp: this.metrics.get('INP'),
|
|
106
|
+
collectedAt: Date.now(),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Stop collecting metrics and disconnect all observers.
|
|
111
|
+
*/
|
|
112
|
+
stopCollecting() {
|
|
113
|
+
for (const observer of this.observers) {
|
|
114
|
+
observer.disconnect();
|
|
115
|
+
}
|
|
116
|
+
this.observers = [];
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Stop collecting and clear all state.
|
|
120
|
+
*/
|
|
121
|
+
dispose() {
|
|
122
|
+
this.stopCollecting();
|
|
123
|
+
this.metrics.clear();
|
|
124
|
+
this.callbacks.clear();
|
|
125
|
+
this.clsValue = 0;
|
|
126
|
+
this.clsSessionEntries = [];
|
|
127
|
+
this.clsSessionValue = 0;
|
|
128
|
+
}
|
|
129
|
+
// ----------------------------------------------------------
|
|
130
|
+
// Private: metric recording
|
|
131
|
+
// ----------------------------------------------------------
|
|
132
|
+
recordMetric(name, value) {
|
|
133
|
+
const metric = {
|
|
134
|
+
name,
|
|
135
|
+
value,
|
|
136
|
+
rating: rateMetric(name, value),
|
|
137
|
+
timestamp: Date.now(),
|
|
138
|
+
};
|
|
139
|
+
this.metrics.set(name, metric);
|
|
140
|
+
const cbs = this.callbacks.get(name);
|
|
141
|
+
if (cbs) {
|
|
142
|
+
for (const cb of cbs) {
|
|
143
|
+
cb(metric);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// ----------------------------------------------------------
|
|
148
|
+
// Private: observer helpers
|
|
149
|
+
// ----------------------------------------------------------
|
|
150
|
+
tryObserve(entryType, handler, options) {
|
|
151
|
+
try {
|
|
152
|
+
const observer = new PerformanceObserver((list) => {
|
|
153
|
+
handler(list.getEntries());
|
|
154
|
+
});
|
|
155
|
+
const observeOptions = {
|
|
156
|
+
type: options?.type ?? entryType,
|
|
157
|
+
buffered: options?.buffered ?? true,
|
|
158
|
+
};
|
|
159
|
+
observer.observe(observeOptions);
|
|
160
|
+
this.observers.push(observer);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Entry type not supported in this browser — silently skip
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
observeLCP() {
|
|
167
|
+
this.tryObserve('largest-contentful-paint', (entries) => {
|
|
168
|
+
const last = entries[entries.length - 1];
|
|
169
|
+
if (last) {
|
|
170
|
+
this.recordMetric('LCP', last.startTime);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
observeFID() {
|
|
175
|
+
this.tryObserve('first-input', (entries) => {
|
|
176
|
+
const first = entries[0];
|
|
177
|
+
if (first) {
|
|
178
|
+
const fidEntry = first;
|
|
179
|
+
this.recordMetric('FID', fidEntry.processingStart - fidEntry.startTime);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
observeCLS() {
|
|
184
|
+
this.tryObserve('layout-shift', (entries) => {
|
|
185
|
+
for (const entry of entries) {
|
|
186
|
+
const lsEntry = entry;
|
|
187
|
+
// Only count shifts without recent user input
|
|
188
|
+
if (!lsEntry.hadRecentInput) {
|
|
189
|
+
// Session window algorithm
|
|
190
|
+
if (this.clsSessionEntries.length > 0 &&
|
|
191
|
+
entry.startTime -
|
|
192
|
+
(this.clsSessionEntries[this.clsSessionEntries.length - 1]?.startTime ?? 0) >
|
|
193
|
+
1000) {
|
|
194
|
+
// Gap > 1s → start new session window
|
|
195
|
+
if (this.clsSessionValue > this.clsValue) {
|
|
196
|
+
this.clsValue = this.clsSessionValue;
|
|
197
|
+
}
|
|
198
|
+
this.clsSessionEntries = [];
|
|
199
|
+
this.clsSessionValue = 0;
|
|
200
|
+
}
|
|
201
|
+
// Add to current session
|
|
202
|
+
if (this.clsSessionEntries.length === 0 ||
|
|
203
|
+
entry.startTime - (this.clsSessionEntries[0]?.startTime ?? 0) <= 5000) {
|
|
204
|
+
this.clsSessionEntries.push(entry);
|
|
205
|
+
this.clsSessionValue += lsEntry.value;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// Session exceeded 5s → start new session
|
|
209
|
+
if (this.clsSessionValue > this.clsValue) {
|
|
210
|
+
this.clsValue = this.clsSessionValue;
|
|
211
|
+
}
|
|
212
|
+
this.clsSessionEntries = [entry];
|
|
213
|
+
this.clsSessionValue = lsEntry.value;
|
|
214
|
+
}
|
|
215
|
+
// Report the max of current session and previous max
|
|
216
|
+
const currentCls = Math.max(this.clsValue, this.clsSessionValue);
|
|
217
|
+
this.recordMetric('CLS', currentCls);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
observeFCP() {
|
|
223
|
+
this.tryObserve('paint', (entries) => {
|
|
224
|
+
const fcp = entries.find((e) => e.name === 'first-contentful-paint');
|
|
225
|
+
if (fcp) {
|
|
226
|
+
this.recordMetric('FCP', fcp.startTime);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
observeTTFB() {
|
|
231
|
+
this.tryObserve('navigation', (entries) => {
|
|
232
|
+
const nav = entries[0];
|
|
233
|
+
if (nav) {
|
|
234
|
+
this.recordMetric('TTFB', nav.responseStart);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
observeINP() {
|
|
239
|
+
this.tryObserve('event', (entries) => {
|
|
240
|
+
let maxDuration = 0;
|
|
241
|
+
for (const entry of entries) {
|
|
242
|
+
const eventEntry = entry;
|
|
243
|
+
if (eventEntry.duration > maxDuration) {
|
|
244
|
+
maxDuration = eventEntry.duration;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (maxDuration > 0) {
|
|
248
|
+
const existing = this.metrics.get('INP');
|
|
249
|
+
// INP tracks the worst interaction
|
|
250
|
+
if (!existing || maxDuration > existing.value) {
|
|
251
|
+
this.recordMetric('INP', maxDuration);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}, { type: 'event', buffered: true });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// ============================================================
|
|
258
|
+
// FACTORY
|
|
259
|
+
// ============================================================
|
|
260
|
+
/**
|
|
261
|
+
* Create a new WebVitalsCollector instance.
|
|
262
|
+
*
|
|
263
|
+
* @returns A new collector (not yet started)
|
|
264
|
+
*/
|
|
265
|
+
export function createWebVitalsCollector() {
|
|
266
|
+
return new WebVitalsCollector();
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=web-vitals.js.map
|