@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,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WRITE-ONLY SOURCES — a form's target that nothing on the page reads.
|
|
3
|
+
*
|
|
4
|
+
* A data source a form writes to (`platform/entities` `config.formBindings[].sourceId`)
|
|
5
|
+
* was also fetched as a list on mount and again after every submit (the
|
|
6
|
+
* bridge's `refreshAll`). For an intake-only model — anonymous visitors may
|
|
7
|
+
* create, never list — that is a refused request on every visit and every
|
|
8
|
+
* submit (`GET /_host/records/<model>` → 403
|
|
9
|
+
* `RECORDS_NOT_PUBLIC`, `__errors.records` set, no block bound to it).
|
|
10
|
+
*
|
|
11
|
+
* A source is write-only when:
|
|
12
|
+
* - at least one form binding targets it, and every binding that does writes
|
|
13
|
+
* with POST (a PATCH/PUT needs the loaded record to know what to update), and
|
|
14
|
+
* - its id is referenced NOWHERE else: no block (props, expressions, actions,
|
|
15
|
+
* repeat), no behavior, no other source, no other capability config.
|
|
16
|
+
*
|
|
17
|
+
* The reference check is a whole-word search of every string, which errs on
|
|
18
|
+
* the side of fetching: a stray word equal to the id keeps today's behaviour.
|
|
19
|
+
* A write-only source stays declared and registered — it is still the write
|
|
20
|
+
* target — it is just never asked for its rows.
|
|
21
|
+
*
|
|
22
|
+
* @module @pwfabric/runtime/render/write-only-sources
|
|
23
|
+
*/
|
|
24
|
+
import { isCapability } from '../capability/type-mapping.js';
|
|
25
|
+
function collectStrings(value, out, seen) {
|
|
26
|
+
if (typeof value === 'string') {
|
|
27
|
+
out.push(value);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (value === null || typeof value !== 'object')
|
|
31
|
+
return;
|
|
32
|
+
if (seen.has(value))
|
|
33
|
+
return;
|
|
34
|
+
seen.add(value);
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
for (const v of value)
|
|
37
|
+
collectStrings(v, out, seen);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
for (const [k, v] of Object.entries(value)) {
|
|
41
|
+
// A key can be a reference too (`{ records: … }` maps).
|
|
42
|
+
out.push(k);
|
|
43
|
+
collectStrings(v, out, seen);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function escapeRegExp(s) {
|
|
47
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
48
|
+
}
|
|
49
|
+
/** The ids of the declared sources that are only ever written to. */
|
|
50
|
+
export function writeOnlySourceIds(input) {
|
|
51
|
+
const capabilities = (input.capabilities ?? []);
|
|
52
|
+
const entities = capabilities.find((c) => isCapability(c, 'platform/entities'));
|
|
53
|
+
const bindings = entities?.config?.formBindings;
|
|
54
|
+
if (!Array.isArray(bindings) || bindings.length === 0)
|
|
55
|
+
return new Set();
|
|
56
|
+
// Candidates: targeted by a form, and only ever with POST.
|
|
57
|
+
const postOnly = new Map();
|
|
58
|
+
for (const raw of bindings) {
|
|
59
|
+
if (typeof raw?.sourceId !== 'string')
|
|
60
|
+
continue;
|
|
61
|
+
const isPost = raw.method === undefined || raw.method === 'POST';
|
|
62
|
+
postOnly.set(raw.sourceId, (postOnly.get(raw.sourceId) ?? true) && isPost);
|
|
63
|
+
}
|
|
64
|
+
const declared = new Set(input.dataSources.map((s) => s.id));
|
|
65
|
+
const candidates = [...postOnly].filter(([id, post]) => post && declared.has(id)).map(([id]) => id);
|
|
66
|
+
if (candidates.length === 0)
|
|
67
|
+
return new Set();
|
|
68
|
+
// Everything that could read a source, minus the declarations themselves:
|
|
69
|
+
// the form bindings (they ARE the write) and the source list (each source is
|
|
70
|
+
// checked against the OTHER sources below).
|
|
71
|
+
const strings = [];
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
collectStrings(input.blocks, strings, seen);
|
|
74
|
+
collectStrings(input.behaviors ?? [], strings, seen);
|
|
75
|
+
for (const c of capabilities) {
|
|
76
|
+
const config = c.config;
|
|
77
|
+
if (config === null || typeof config !== 'object')
|
|
78
|
+
continue;
|
|
79
|
+
const rest = { ...config };
|
|
80
|
+
delete rest['dataSources'];
|
|
81
|
+
delete rest['behaviors']; // collected above (the same list, seeded from persistence)
|
|
82
|
+
if (c === entities)
|
|
83
|
+
delete rest['formBindings'];
|
|
84
|
+
collectStrings(rest, strings, seen);
|
|
85
|
+
}
|
|
86
|
+
const result = new Set();
|
|
87
|
+
for (const id of candidates) {
|
|
88
|
+
const word = new RegExp(`(^|[^A-Za-z0-9_$])${escapeRegExp(id)}([^A-Za-z0-9_$]|$)`);
|
|
89
|
+
const others = [];
|
|
90
|
+
for (const s of input.dataSources)
|
|
91
|
+
if (s.id !== id)
|
|
92
|
+
collectStrings(s, others, new Set());
|
|
93
|
+
const read = strings.some((s) => word.test(s)) || others.some((s) => word.test(s));
|
|
94
|
+
if (!read)
|
|
95
|
+
result.add(id);
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=write-only-sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-only-sources.js","sourceRoot":"","sources":["../../src/render/write-only-sources.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WRITE-ONLY SOURCES — a form's target that nothing on the page reads.\n *\n * A data source a form writes to (`platform/entities` `config.formBindings[].sourceId`)\n * was also fetched as a list on mount and again after every submit (the\n * bridge's `refreshAll`). For an intake-only model — anonymous visitors may\n * create, never list — that is a refused request on every visit and every\n * submit (`GET /_host/records/<model>` → 403\n * `RECORDS_NOT_PUBLIC`, `__errors.records` set, no block bound to it).\n *\n * A source is write-only when:\n * - at least one form binding targets it, and every binding that does writes\n * with POST (a PATCH/PUT needs the loaded record to know what to update), and\n * - its id is referenced NOWHERE else: no block (props, expressions, actions,\n * repeat), no behavior, no other source, no other capability config.\n *\n * The reference check is a whole-word search of every string, which errs on\n * the side of fetching: a stray word equal to the id keeps today's behaviour.\n * A write-only source stays declared and registered — it is still the write\n * target — it is just never asked for its rows.\n *\n * @module @pwfabric/runtime/render/write-only-sources\n */\n\nimport { isCapability } from '../capability/type-mapping.js'\n\ninterface FormBindingLike {\n readonly sourceId?: unknown\n readonly method?: unknown\n}\n\ninterface CapabilityLike {\n readonly capability?: string\n readonly config?: unknown\n}\n\nexport interface WriteOnlySourceInput {\n readonly blocks: readonly unknown[]\n readonly dataSources: readonly { readonly id: string }[]\n readonly capabilities?: readonly unknown[] | undefined\n readonly behaviors?: readonly unknown[] | undefined\n}\n\nfunction collectStrings(value: unknown, out: string[], seen: Set<object>): void {\n if (typeof value === 'string') {\n out.push(value)\n return\n }\n if (value === null || typeof value !== 'object') return\n if (seen.has(value)) return\n seen.add(value)\n if (Array.isArray(value)) {\n for (const v of value) collectStrings(v, out, seen)\n return\n }\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n // A key can be a reference too (`{ records: … }` maps).\n out.push(k)\n collectStrings(v, out, seen)\n }\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/** The ids of the declared sources that are only ever written to. */\nexport function writeOnlySourceIds(input: WriteOnlySourceInput): ReadonlySet<string> {\n const capabilities = (input.capabilities ?? []) as readonly CapabilityLike[]\n const entities = capabilities.find((c) => isCapability(c, 'platform/entities'))\n const bindings = (entities?.config as { formBindings?: unknown } | undefined)?.formBindings\n if (!Array.isArray(bindings) || bindings.length === 0) return new Set()\n\n // Candidates: targeted by a form, and only ever with POST.\n const postOnly = new Map<string, boolean>()\n for (const raw of bindings as readonly FormBindingLike[]) {\n if (typeof raw?.sourceId !== 'string') continue\n const isPost = raw.method === undefined || raw.method === 'POST'\n postOnly.set(raw.sourceId, (postOnly.get(raw.sourceId) ?? true) && isPost)\n }\n const declared = new Set(input.dataSources.map((s) => s.id))\n const candidates = [...postOnly].filter(([id, post]) => post && declared.has(id)).map(([id]) => id)\n if (candidates.length === 0) return new Set()\n\n // Everything that could read a source, minus the declarations themselves:\n // the form bindings (they ARE the write) and the source list (each source is\n // checked against the OTHER sources below).\n const strings: string[] = []\n const seen = new Set<object>()\n collectStrings(input.blocks, strings, seen)\n collectStrings(input.behaviors ?? [], strings, seen)\n for (const c of capabilities) {\n const config = c.config\n if (config === null || typeof config !== 'object') continue\n const rest: Record<string, unknown> = { ...(config as Record<string, unknown>) }\n delete rest['dataSources']\n delete rest['behaviors'] // collected above (the same list, seeded from persistence)\n if (c === entities) delete rest['formBindings']\n collectStrings(rest, strings, seen)\n }\n\n const result = new Set<string>()\n for (const id of candidates) {\n const word = new RegExp(`(^|[^A-Za-z0-9_$])${escapeRegExp(id)}([^A-Za-z0-9_$]|$)`)\n const others: string[] = []\n for (const s of input.dataSources) if (s.id !== id) collectStrings(s, others, new Set())\n const read = strings.some((s) => word.test(s)) || others.some((s) => word.test(s))\n if (!read) result.add(id)\n }\n return result\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing Capability Module
|
|
3
|
+
*
|
|
4
|
+
* Route matching, navigation, and URL parameter resolution
|
|
5
|
+
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* - Types: RouteDefinition, RouteMatch, NavigationTarget, RoutingCapability, etc.
|
|
8
|
+
* - Implementations: DefaultRoutingCapability
|
|
9
|
+
* - Factory functions: createRoutingCapability
|
|
10
|
+
*
|
|
11
|
+
* @module @pwfabric/runtime/routing
|
|
12
|
+
*/
|
|
13
|
+
export type { RouteDefinition, RouteGuard, RouteGuardContext, RouteMatchResult, RouteMatch, NavigationTarget, NavigationResult, NavigationFailureReason, NavigationListener, RoutingCapability, } from './types.js';
|
|
14
|
+
export { DefaultRoutingCapability, createRoutingCapability } from './router.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing Capability Module
|
|
3
|
+
*
|
|
4
|
+
* Route matching, navigation, and URL parameter resolution
|
|
5
|
+
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* - Types: RouteDefinition, RouteMatch, NavigationTarget, RoutingCapability, etc.
|
|
8
|
+
* - Implementations: DefaultRoutingCapability
|
|
9
|
+
* - Factory functions: createRoutingCapability
|
|
10
|
+
*
|
|
11
|
+
* @module @pwfabric/runtime/routing
|
|
12
|
+
*/
|
|
13
|
+
// Implementations
|
|
14
|
+
export { DefaultRoutingCapability, createRoutingCapability } from './router.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAgBH,kBAAkB;AAClB,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA","sourcesContent":["/**\n * Routing Capability Module\n *\n * Route matching, navigation, and URL parameter resolution\n *\n * Exports:\n * - Types: RouteDefinition, RouteMatch, NavigationTarget, RoutingCapability, etc.\n * - Implementations: DefaultRoutingCapability\n * - Factory functions: createRoutingCapability\n *\n * @module @pwfabric/runtime/routing\n */\n\n// Types\nexport type {\n RouteDefinition,\n RouteGuard,\n RouteGuardContext,\n RouteMatchResult,\n RouteMatch,\n NavigationTarget,\n NavigationResult,\n NavigationFailureReason,\n NavigationListener,\n RoutingCapability,\n} from './types.js'\n\n// Implementations\nexport { DefaultRoutingCapability, createRoutingCapability } from './router.js'\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing Capability Implementation
|
|
3
|
+
*
|
|
4
|
+
* Route matching, navigation, and parameter resolution
|
|
5
|
+
*
|
|
6
|
+
* @module @pwfabric/runtime/routing/router
|
|
7
|
+
*/
|
|
8
|
+
import type { RoutingCapability, RouteDefinition, RouteMatch, RouteMatchResult, NavigationTarget, NavigationResult, NavigationListener } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Default routing capability implementation
|
|
11
|
+
*/
|
|
12
|
+
export declare class DefaultRoutingCapability implements RoutingCapability {
|
|
13
|
+
private readonly routes;
|
|
14
|
+
private readonly listeners;
|
|
15
|
+
private readonly navigationHistory;
|
|
16
|
+
private currentRoute;
|
|
17
|
+
private readonly maxHistorySize;
|
|
18
|
+
constructor(options?: {
|
|
19
|
+
maxHistorySize?: number;
|
|
20
|
+
});
|
|
21
|
+
addRoute(route: RouteDefinition): void;
|
|
22
|
+
removeRoute(routeId: string): boolean;
|
|
23
|
+
matchRoute(path: string): RouteMatchResult;
|
|
24
|
+
navigate(target: NavigationTarget): Promise<NavigationResult>;
|
|
25
|
+
resolveParams(pattern: string, params: Record<string, string>): string;
|
|
26
|
+
getCurrentRoute(): RouteMatch | undefined;
|
|
27
|
+
getHistory(): readonly RouteMatch[];
|
|
28
|
+
onNavigate(listener: NavigationListener): () => void;
|
|
29
|
+
getRouteIds(): readonly string[];
|
|
30
|
+
clear(): void;
|
|
31
|
+
private compileRoute;
|
|
32
|
+
private tryMatch;
|
|
33
|
+
private parsePath;
|
|
34
|
+
private buildPath;
|
|
35
|
+
private notifyListeners;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a routing capability instance
|
|
39
|
+
*/
|
|
40
|
+
export declare function createRoutingCapability(options?: {
|
|
41
|
+
maxHistorySize?: number;
|
|
42
|
+
}): RoutingCapability;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing Capability Implementation
|
|
3
|
+
*
|
|
4
|
+
* Route matching, navigation, and parameter resolution
|
|
5
|
+
*
|
|
6
|
+
* @module @pwfabric/runtime/routing/router
|
|
7
|
+
*/
|
|
8
|
+
// ============================================================
|
|
9
|
+
// DEFAULT ROUTING CAPABILITY
|
|
10
|
+
// ============================================================
|
|
11
|
+
/**
|
|
12
|
+
* Default routing capability implementation
|
|
13
|
+
*/
|
|
14
|
+
export class DefaultRoutingCapability {
|
|
15
|
+
routes = new Map();
|
|
16
|
+
listeners = new Set();
|
|
17
|
+
navigationHistory = [];
|
|
18
|
+
currentRoute = undefined;
|
|
19
|
+
maxHistorySize;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.maxHistorySize = options?.maxHistorySize ?? 50;
|
|
22
|
+
}
|
|
23
|
+
addRoute(route) {
|
|
24
|
+
if (this.routes.has(route.id)) {
|
|
25
|
+
throw new Error(`Route '${route.id}' is already registered`);
|
|
26
|
+
}
|
|
27
|
+
const compiled = this.compileRoute(route);
|
|
28
|
+
this.routes.set(route.id, compiled);
|
|
29
|
+
}
|
|
30
|
+
removeRoute(routeId) {
|
|
31
|
+
return this.routes.delete(routeId);
|
|
32
|
+
}
|
|
33
|
+
matchRoute(path) {
|
|
34
|
+
const { pathname, query, hash } = this.parsePath(path);
|
|
35
|
+
for (const compiled of this.routes.values()) {
|
|
36
|
+
const match = this.tryMatch(compiled, pathname, query, hash);
|
|
37
|
+
if (match) {
|
|
38
|
+
return { matched: true, match };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { matched: false, reason: `No route matches path: ${path}` };
|
|
42
|
+
}
|
|
43
|
+
async navigate(target) {
|
|
44
|
+
const fullPath = this.buildPath(target);
|
|
45
|
+
const matchResult = this.matchRoute(fullPath);
|
|
46
|
+
if (!matchResult.matched) {
|
|
47
|
+
return { success: false, reason: 'no_match' };
|
|
48
|
+
}
|
|
49
|
+
const match = matchResult.match;
|
|
50
|
+
// Check if navigating to the same route
|
|
51
|
+
if (this.currentRoute &&
|
|
52
|
+
this.currentRoute.route.id === match.route.id &&
|
|
53
|
+
this.currentRoute.path === match.path) {
|
|
54
|
+
return { success: false, reason: 'same_route' };
|
|
55
|
+
}
|
|
56
|
+
// Execute guards
|
|
57
|
+
if (match.route.guards && match.route.guards.length > 0) {
|
|
58
|
+
for (const guard of match.route.guards) {
|
|
59
|
+
const canActivate = await guard.canActivate({
|
|
60
|
+
from: this.currentRoute,
|
|
61
|
+
to: match,
|
|
62
|
+
params: match.params,
|
|
63
|
+
});
|
|
64
|
+
if (!canActivate) {
|
|
65
|
+
return { success: false, reason: 'guard_rejected' };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Update current route
|
|
70
|
+
const previousRoute = this.currentRoute;
|
|
71
|
+
this.currentRoute = match;
|
|
72
|
+
// Add to history
|
|
73
|
+
if (!target.replace) {
|
|
74
|
+
this.navigationHistory.push(match);
|
|
75
|
+
while (this.navigationHistory.length > this.maxHistorySize) {
|
|
76
|
+
this.navigationHistory.shift();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (this.navigationHistory.length > 0) {
|
|
80
|
+
this.navigationHistory[this.navigationHistory.length - 1] = match;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.navigationHistory.push(match);
|
|
84
|
+
}
|
|
85
|
+
// Notify listeners
|
|
86
|
+
this.notifyListeners(match, previousRoute);
|
|
87
|
+
return { success: true, match };
|
|
88
|
+
}
|
|
89
|
+
resolveParams(pattern, params) {
|
|
90
|
+
let resolved = pattern;
|
|
91
|
+
for (const [key, value] of Object.entries(params)) {
|
|
92
|
+
resolved = resolved.replace(`:${key}`, encodeURIComponent(value));
|
|
93
|
+
}
|
|
94
|
+
return resolved;
|
|
95
|
+
}
|
|
96
|
+
getCurrentRoute() {
|
|
97
|
+
return this.currentRoute;
|
|
98
|
+
}
|
|
99
|
+
getHistory() {
|
|
100
|
+
return [...this.navigationHistory];
|
|
101
|
+
}
|
|
102
|
+
onNavigate(listener) {
|
|
103
|
+
this.listeners.add(listener);
|
|
104
|
+
return () => {
|
|
105
|
+
this.listeners.delete(listener);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
getRouteIds() {
|
|
109
|
+
return Array.from(this.routes.keys());
|
|
110
|
+
}
|
|
111
|
+
clear() {
|
|
112
|
+
this.routes.clear();
|
|
113
|
+
this.listeners.clear();
|
|
114
|
+
this.navigationHistory.length = 0;
|
|
115
|
+
this.currentRoute = undefined;
|
|
116
|
+
}
|
|
117
|
+
// ============================================================
|
|
118
|
+
// PRIVATE METHODS
|
|
119
|
+
// ============================================================
|
|
120
|
+
compileRoute(route) {
|
|
121
|
+
const paramNames = [];
|
|
122
|
+
// Convert pattern to regex
|
|
123
|
+
// :param -> named capture group
|
|
124
|
+
// * -> wildcard capture
|
|
125
|
+
let regexStr = route.pattern
|
|
126
|
+
.replace(/\/:(\w+)/g, (_match, paramName) => {
|
|
127
|
+
paramNames.push(paramName);
|
|
128
|
+
return '/([^/]+)';
|
|
129
|
+
})
|
|
130
|
+
.replace(/\*$/, '(.*)');
|
|
131
|
+
// Ensure exact match (anchor start and end)
|
|
132
|
+
regexStr = `^${regexStr}$`;
|
|
133
|
+
return {
|
|
134
|
+
definition: route,
|
|
135
|
+
regex: new RegExp(regexStr),
|
|
136
|
+
paramNames,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
tryMatch(compiled, pathname, query, hash) {
|
|
140
|
+
const match = pathname.match(compiled.regex);
|
|
141
|
+
if (!match)
|
|
142
|
+
return null;
|
|
143
|
+
const params = {};
|
|
144
|
+
for (let i = 0; i < compiled.paramNames.length; i++) {
|
|
145
|
+
const name = compiled.paramNames[i];
|
|
146
|
+
const value = match[i + 1];
|
|
147
|
+
if (name && value !== undefined) {
|
|
148
|
+
params[name] = decodeURIComponent(value);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
route: compiled.definition,
|
|
153
|
+
params,
|
|
154
|
+
query,
|
|
155
|
+
path: pathname,
|
|
156
|
+
hash,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
parsePath(path) {
|
|
160
|
+
let pathname = path;
|
|
161
|
+
let hash = '';
|
|
162
|
+
const query = {};
|
|
163
|
+
// Extract hash
|
|
164
|
+
const hashIndex = pathname.indexOf('#');
|
|
165
|
+
if (hashIndex >= 0) {
|
|
166
|
+
hash = pathname.slice(hashIndex + 1);
|
|
167
|
+
pathname = pathname.slice(0, hashIndex);
|
|
168
|
+
}
|
|
169
|
+
// Extract query
|
|
170
|
+
const queryIndex = pathname.indexOf('?');
|
|
171
|
+
if (queryIndex >= 0) {
|
|
172
|
+
const queryString = pathname.slice(queryIndex + 1);
|
|
173
|
+
pathname = pathname.slice(0, queryIndex);
|
|
174
|
+
for (const pair of queryString.split('&')) {
|
|
175
|
+
const [key, value] = pair.split('=');
|
|
176
|
+
if (key) {
|
|
177
|
+
query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : '';
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Normalize pathname
|
|
182
|
+
if (!pathname.startsWith('/')) {
|
|
183
|
+
pathname = '/' + pathname;
|
|
184
|
+
}
|
|
185
|
+
return { pathname, query, hash };
|
|
186
|
+
}
|
|
187
|
+
buildPath(target) {
|
|
188
|
+
let path = target.path;
|
|
189
|
+
if (target.query && Object.keys(target.query).length > 0) {
|
|
190
|
+
const queryParts = Object.entries(target.query).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
191
|
+
path += '?' + queryParts.join('&');
|
|
192
|
+
}
|
|
193
|
+
if (target.hash) {
|
|
194
|
+
path += '#' + target.hash;
|
|
195
|
+
}
|
|
196
|
+
return path;
|
|
197
|
+
}
|
|
198
|
+
notifyListeners(to, from) {
|
|
199
|
+
for (const listener of this.listeners) {
|
|
200
|
+
try {
|
|
201
|
+
listener(to, from);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Swallow listener errors
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// ============================================================
|
|
210
|
+
// FACTORY FUNCTION
|
|
211
|
+
// ============================================================
|
|
212
|
+
/**
|
|
213
|
+
* Create a routing capability instance
|
|
214
|
+
*/
|
|
215
|
+
export function createRoutingCapability(options) {
|
|
216
|
+
return new DefaultRoutingCapability(options);
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/routing/router.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Routing Capability Implementation\n *\n * Route matching, navigation, and parameter resolution\n *\n * @module @pwfabric/runtime/routing/router\n */\n\nimport type {\n RoutingCapability,\n RouteDefinition,\n RouteMatch,\n RouteMatchResult,\n NavigationTarget,\n NavigationResult,\n NavigationListener,\n} from './types.js'\n\n// ============================================================\n// COMPILED ROUTE\n// ============================================================\n\ninterface CompiledRoute {\n definition: RouteDefinition\n regex: RegExp\n paramNames: string[]\n}\n\n// ============================================================\n// DEFAULT ROUTING CAPABILITY\n// ============================================================\n\n/**\n * Default routing capability implementation\n */\nexport class DefaultRoutingCapability implements RoutingCapability {\n private readonly routes = new Map<string, CompiledRoute>()\n private readonly listeners = new Set<NavigationListener>()\n private readonly navigationHistory: RouteMatch[] = []\n private currentRoute: RouteMatch | undefined = undefined\n private readonly maxHistorySize: number\n\n constructor(options?: { maxHistorySize?: number }) {\n this.maxHistorySize = options?.maxHistorySize ?? 50\n }\n\n addRoute(route: RouteDefinition): void {\n if (this.routes.has(route.id)) {\n throw new Error(`Route '${route.id}' is already registered`)\n }\n const compiled = this.compileRoute(route)\n this.routes.set(route.id, compiled)\n }\n\n removeRoute(routeId: string): boolean {\n return this.routes.delete(routeId)\n }\n\n matchRoute(path: string): RouteMatchResult {\n const { pathname, query, hash } = this.parsePath(path)\n\n for (const compiled of this.routes.values()) {\n const match = this.tryMatch(compiled, pathname, query, hash)\n if (match) {\n return { matched: true, match }\n }\n }\n\n return { matched: false, reason: `No route matches path: ${path}` }\n }\n\n async navigate(target: NavigationTarget): Promise<NavigationResult> {\n const fullPath = this.buildPath(target)\n const matchResult = this.matchRoute(fullPath)\n\n if (!matchResult.matched) {\n return { success: false, reason: 'no_match' }\n }\n\n const match = matchResult.match\n\n // Check if navigating to the same route\n if (\n this.currentRoute &&\n this.currentRoute.route.id === match.route.id &&\n this.currentRoute.path === match.path\n ) {\n return { success: false, reason: 'same_route' }\n }\n\n // Execute guards\n if (match.route.guards && match.route.guards.length > 0) {\n for (const guard of match.route.guards) {\n const canActivate = await guard.canActivate({\n from: this.currentRoute,\n to: match,\n params: match.params,\n })\n if (!canActivate) {\n return { success: false, reason: 'guard_rejected' }\n }\n }\n }\n\n // Update current route\n const previousRoute = this.currentRoute\n this.currentRoute = match\n\n // Add to history\n if (!target.replace) {\n this.navigationHistory.push(match)\n while (this.navigationHistory.length > this.maxHistorySize) {\n this.navigationHistory.shift()\n }\n } else if (this.navigationHistory.length > 0) {\n this.navigationHistory[this.navigationHistory.length - 1] = match\n } else {\n this.navigationHistory.push(match)\n }\n\n // Notify listeners\n this.notifyListeners(match, previousRoute)\n\n return { success: true, match }\n }\n\n resolveParams(pattern: string, params: Record<string, string>): string {\n let resolved = pattern\n for (const [key, value] of Object.entries(params)) {\n resolved = resolved.replace(`:${key}`, encodeURIComponent(value))\n }\n return resolved\n }\n\n getCurrentRoute(): RouteMatch | undefined {\n return this.currentRoute\n }\n\n getHistory(): readonly RouteMatch[] {\n return [...this.navigationHistory]\n }\n\n onNavigate(listener: NavigationListener): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n getRouteIds(): readonly string[] {\n return Array.from(this.routes.keys())\n }\n\n clear(): void {\n this.routes.clear()\n this.listeners.clear()\n this.navigationHistory.length = 0\n this.currentRoute = undefined\n }\n\n // ============================================================\n // PRIVATE METHODS\n // ============================================================\n\n private compileRoute(route: RouteDefinition): CompiledRoute {\n const paramNames: string[] = []\n\n // Convert pattern to regex\n // :param -> named capture group\n // * -> wildcard capture\n let regexStr = route.pattern\n .replace(/\\/:(\\w+)/g, (_match, paramName: string) => {\n paramNames.push(paramName)\n return '/([^/]+)'\n })\n .replace(/\\*$/, '(.*)')\n\n // Ensure exact match (anchor start and end)\n regexStr = `^${regexStr}$`\n\n return {\n definition: route,\n regex: new RegExp(regexStr),\n paramNames,\n }\n }\n\n private tryMatch(\n compiled: CompiledRoute,\n pathname: string,\n query: Record<string, string>,\n hash: string\n ): RouteMatch | null {\n const match = pathname.match(compiled.regex)\n if (!match) return null\n\n const params: Record<string, string> = {}\n for (let i = 0; i < compiled.paramNames.length; i++) {\n const name = compiled.paramNames[i]\n const value = match[i + 1]\n if (name && value !== undefined) {\n params[name] = decodeURIComponent(value)\n }\n }\n\n return {\n route: compiled.definition,\n params,\n query,\n path: pathname,\n hash,\n }\n }\n\n private parsePath(path: string): {\n pathname: string\n query: Record<string, string>\n hash: string\n } {\n let pathname = path\n let hash = ''\n const query: Record<string, string> = {}\n\n // Extract hash\n const hashIndex = pathname.indexOf('#')\n if (hashIndex >= 0) {\n hash = pathname.slice(hashIndex + 1)\n pathname = pathname.slice(0, hashIndex)\n }\n\n // Extract query\n const queryIndex = pathname.indexOf('?')\n if (queryIndex >= 0) {\n const queryString = pathname.slice(queryIndex + 1)\n pathname = pathname.slice(0, queryIndex)\n\n for (const pair of queryString.split('&')) {\n const [key, value] = pair.split('=')\n if (key) {\n query[decodeURIComponent(key)] = value ? decodeURIComponent(value) : ''\n }\n }\n }\n\n // Normalize pathname\n if (!pathname.startsWith('/')) {\n pathname = '/' + pathname\n }\n\n return { pathname, query, hash }\n }\n\n private buildPath(target: NavigationTarget): string {\n let path = target.path\n\n if (target.query && Object.keys(target.query).length > 0) {\n const queryParts = Object.entries(target.query).map(\n ([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`\n )\n path += '?' + queryParts.join('&')\n }\n\n if (target.hash) {\n path += '#' + target.hash\n }\n\n return path\n }\n\n private notifyListeners(to: RouteMatch, from: RouteMatch | undefined): void {\n for (const listener of this.listeners) {\n try {\n listener(to, from)\n } catch {\n // Swallow listener errors\n }\n }\n }\n}\n\n// ============================================================\n// FACTORY FUNCTION\n// ============================================================\n\n/**\n * Create a routing capability instance\n */\nexport function createRoutingCapability(options?: { maxHistorySize?: number }): RoutingCapability {\n return new DefaultRoutingCapability(options)\n}\n"]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing Capability Types
|
|
3
|
+
*
|
|
4
|
+
* Route matching, navigation, and URL parameter resolution
|
|
5
|
+
*
|
|
6
|
+
* @module @pwfabric/runtime/routing/types
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Route definition for mapping URLs to surfaces
|
|
10
|
+
*/
|
|
11
|
+
export interface RouteDefinition {
|
|
12
|
+
/** Unique route identifier */
|
|
13
|
+
readonly id: string;
|
|
14
|
+
/** URL pattern (supports :param and * wildcard) */
|
|
15
|
+
readonly pattern: string;
|
|
16
|
+
/** Surface ID this route maps to */
|
|
17
|
+
readonly surfaceId: string;
|
|
18
|
+
/** Human-readable name */
|
|
19
|
+
readonly name?: string;
|
|
20
|
+
/** Route metadata */
|
|
21
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
22
|
+
/** Navigation guards for this route */
|
|
23
|
+
readonly guards?: readonly RouteGuard[];
|
|
24
|
+
/** Child routes (for nested routing) */
|
|
25
|
+
readonly children?: readonly RouteDefinition[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Route guard function
|
|
29
|
+
*/
|
|
30
|
+
export interface RouteGuard {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
canActivate(context: RouteGuardContext): boolean | Promise<boolean>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Context provided to route guards
|
|
36
|
+
*/
|
|
37
|
+
export interface RouteGuardContext {
|
|
38
|
+
readonly from: RouteMatch | undefined;
|
|
39
|
+
readonly to: RouteMatch;
|
|
40
|
+
readonly params: Readonly<Record<string, string>>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Result of matching a URL against routes
|
|
44
|
+
*/
|
|
45
|
+
export type RouteMatchResult = {
|
|
46
|
+
readonly matched: true;
|
|
47
|
+
readonly match: RouteMatch;
|
|
48
|
+
} | {
|
|
49
|
+
readonly matched: false;
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* A successfully matched route
|
|
54
|
+
*/
|
|
55
|
+
export interface RouteMatch {
|
|
56
|
+
/** The matched route definition */
|
|
57
|
+
readonly route: RouteDefinition;
|
|
58
|
+
/** Extracted URL parameters */
|
|
59
|
+
readonly params: Readonly<Record<string, string>>;
|
|
60
|
+
/** Query parameters */
|
|
61
|
+
readonly query: Readonly<Record<string, string>>;
|
|
62
|
+
/** The full matched path */
|
|
63
|
+
readonly path: string;
|
|
64
|
+
/** Hash fragment (without #) */
|
|
65
|
+
readonly hash: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Navigation target
|
|
69
|
+
*/
|
|
70
|
+
export interface NavigationTarget {
|
|
71
|
+
/** Path to navigate to */
|
|
72
|
+
readonly path: string;
|
|
73
|
+
/** Query parameters */
|
|
74
|
+
readonly query?: Readonly<Record<string, string>>;
|
|
75
|
+
/** Hash fragment */
|
|
76
|
+
readonly hash?: string;
|
|
77
|
+
/** Whether to replace current history entry */
|
|
78
|
+
readonly replace?: boolean;
|
|
79
|
+
/** Navigation state data */
|
|
80
|
+
readonly state?: unknown;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Navigation result
|
|
84
|
+
*/
|
|
85
|
+
export type NavigationResult = {
|
|
86
|
+
readonly success: true;
|
|
87
|
+
readonly match: RouteMatch;
|
|
88
|
+
} | {
|
|
89
|
+
readonly success: false;
|
|
90
|
+
readonly reason: NavigationFailureReason;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Reasons navigation can fail
|
|
94
|
+
*/
|
|
95
|
+
export type NavigationFailureReason = 'no_match' | 'guard_rejected' | 'same_route' | 'aborted';
|
|
96
|
+
/**
|
|
97
|
+
* Listener for navigation events
|
|
98
|
+
*/
|
|
99
|
+
export type NavigationListener = (to: RouteMatch, from: RouteMatch | undefined) => void;
|
|
100
|
+
/**
|
|
101
|
+
* Routing capability interface for managing routes and navigation
|
|
102
|
+
*/
|
|
103
|
+
export interface RoutingCapability {
|
|
104
|
+
/** Register a route definition */
|
|
105
|
+
addRoute(route: RouteDefinition): void;
|
|
106
|
+
/** Remove a route by ID */
|
|
107
|
+
removeRoute(routeId: string): boolean;
|
|
108
|
+
/** Match a URL path against registered routes */
|
|
109
|
+
matchRoute(path: string): RouteMatchResult;
|
|
110
|
+
/** Navigate to a target */
|
|
111
|
+
navigate(target: NavigationTarget): Promise<NavigationResult>;
|
|
112
|
+
/** Resolve parameters from a route pattern and values */
|
|
113
|
+
resolveParams(pattern: string, params: Record<string, string>): string;
|
|
114
|
+
/** Get the current route match */
|
|
115
|
+
getCurrentRoute(): RouteMatch | undefined;
|
|
116
|
+
/** Get navigation history */
|
|
117
|
+
getHistory(): readonly RouteMatch[];
|
|
118
|
+
/** Subscribe to navigation events */
|
|
119
|
+
onNavigate(listener: NavigationListener): () => void;
|
|
120
|
+
/** Get all registered route IDs */
|
|
121
|
+
getRouteIds(): readonly string[];
|
|
122
|
+
/** Clear all routes */
|
|
123
|
+
clear(): void;
|
|
124
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/routing/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * Routing Capability Types\n *\n * Route matching, navigation, and URL parameter resolution\n *\n * @module @pwfabric/runtime/routing/types\n */\n\n// ============================================================\n// ROUTE DEFINITION\n// ============================================================\n\n/**\n * Route definition for mapping URLs to surfaces\n */\nexport interface RouteDefinition {\n /** Unique route identifier */\n readonly id: string\n\n /** URL pattern (supports :param and * wildcard) */\n readonly pattern: string\n\n /** Surface ID this route maps to */\n readonly surfaceId: string\n\n /** Human-readable name */\n readonly name?: string\n\n /** Route metadata */\n readonly metadata?: Readonly<Record<string, unknown>>\n\n /** Navigation guards for this route */\n readonly guards?: readonly RouteGuard[]\n\n /** Child routes (for nested routing) */\n readonly children?: readonly RouteDefinition[]\n}\n\n/**\n * Route guard function\n */\nexport interface RouteGuard {\n readonly name: string\n canActivate(context: RouteGuardContext): boolean | Promise<boolean>\n}\n\n/**\n * Context provided to route guards\n */\nexport interface RouteGuardContext {\n readonly from: RouteMatch | undefined\n readonly to: RouteMatch\n readonly params: Readonly<Record<string, string>>\n}\n\n// ============================================================\n// ROUTE MATCH\n// ============================================================\n\n/**\n * Result of matching a URL against routes\n */\nexport type RouteMatchResult =\n | { readonly matched: true; readonly match: RouteMatch }\n | { readonly matched: false; readonly reason: string }\n\n/**\n * A successfully matched route\n */\nexport interface RouteMatch {\n /** The matched route definition */\n readonly route: RouteDefinition\n\n /** Extracted URL parameters */\n readonly params: Readonly<Record<string, string>>\n\n /** Query parameters */\n readonly query: Readonly<Record<string, string>>\n\n /** The full matched path */\n readonly path: string\n\n /** Hash fragment (without #) */\n readonly hash: string\n}\n\n// ============================================================\n// NAVIGATION\n// ============================================================\n\n/**\n * Navigation target\n */\nexport interface NavigationTarget {\n /** Path to navigate to */\n readonly path: string\n\n /** Query parameters */\n readonly query?: Readonly<Record<string, string>>\n\n /** Hash fragment */\n readonly hash?: string\n\n /** Whether to replace current history entry */\n readonly replace?: boolean\n\n /** Navigation state data */\n readonly state?: unknown\n}\n\n/**\n * Navigation result\n */\nexport type NavigationResult =\n | { readonly success: true; readonly match: RouteMatch }\n | { readonly success: false; readonly reason: NavigationFailureReason }\n\n/**\n * Reasons navigation can fail\n */\nexport type NavigationFailureReason = 'no_match' | 'guard_rejected' | 'same_route' | 'aborted'\n\n/**\n * Listener for navigation events\n */\nexport type NavigationListener = (to: RouteMatch, from: RouteMatch | undefined) => void\n\n// ============================================================\n// ROUTING CAPABILITY INTERFACE\n// ============================================================\n\n/**\n * Routing capability interface for managing routes and navigation\n */\nexport interface RoutingCapability {\n /** Register a route definition */\n addRoute(route: RouteDefinition): void\n\n /** Remove a route by ID */\n removeRoute(routeId: string): boolean\n\n /** Match a URL path against registered routes */\n matchRoute(path: string): RouteMatchResult\n\n /** Navigate to a target */\n navigate(target: NavigationTarget): Promise<NavigationResult>\n\n /** Resolve parameters from a route pattern and values */\n resolveParams(pattern: string, params: Record<string, string>): string\n\n /** Get the current route match */\n getCurrentRoute(): RouteMatch | undefined\n\n /** Get navigation history */\n getHistory(): readonly RouteMatch[]\n\n /** Subscribe to navigation events */\n onNavigate(listener: NavigationListener): () => void\n\n /** Get all registered route IDs */\n getRouteIds(): readonly string[]\n\n /** Clear all routes */\n clear(): void\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO Capability Implementation
|
|
3
|
+
*
|
|
4
|
+
* Meta tag generation, JSON-LD structured data, and Open Graph tags
|
|
5
|
+
*
|
|
6
|
+
* All outputs are plain serializable objects suitable for SSR.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/seo/generator
|
|
9
|
+
*/
|
|
10
|
+
import type { SeoCapability, MetaTagInput, MetaTagOutput, StructuredDataInput, StructuredDataOutput, OpenGraphInput, OpenGraphOutput, SeoConfig, SeoOutput } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Default SEO capability implementation
|
|
13
|
+
*/
|
|
14
|
+
export declare class DefaultSeoCapability implements SeoCapability {
|
|
15
|
+
private readonly defaults;
|
|
16
|
+
constructor(defaults?: Partial<SeoDefaults>);
|
|
17
|
+
generateMetaTags(input: MetaTagInput): MetaTagOutput;
|
|
18
|
+
generateStructuredData(input: StructuredDataInput): StructuredDataOutput;
|
|
19
|
+
generateOpenGraph(input: OpenGraphInput): OpenGraphOutput;
|
|
20
|
+
generateAll(config: SeoConfig): SeoOutput;
|
|
21
|
+
}
|
|
22
|
+
interface SeoDefaults {
|
|
23
|
+
siteName: string;
|
|
24
|
+
titleSeparator: string;
|
|
25
|
+
defaultViewport: string;
|
|
26
|
+
defaultCharset: string;
|
|
27
|
+
defaultRobots: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create an SEO capability instance
|
|
31
|
+
*/
|
|
32
|
+
export declare function createSeoCapability(defaults?: {
|
|
33
|
+
siteName?: string;
|
|
34
|
+
titleSeparator?: string;
|
|
35
|
+
defaultViewport?: string;
|
|
36
|
+
defaultCharset?: string;
|
|
37
|
+
defaultRobots?: string;
|
|
38
|
+
}): SeoCapability;
|
|
39
|
+
export {};
|