@pwfabric/runtime 0.0.0-stage → 1.0.0-rc.0
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 +13 -2
- package/dist/__tests__/dom-test-setup.d.ts +21 -0
- package/dist/__tests__/dom-test-setup.d.ts.map +1 -0
- package/dist/__tests__/dom-test-setup.js +36 -0
- package/dist/__tests__/dom-test-setup.js.map +1 -0
- package/dist/a11y/announcer.d.ts +66 -0
- package/dist/a11y/announcer.d.ts.map +1 -0
- package/dist/a11y/announcer.js +140 -0
- package/dist/a11y/announcer.js.map +1 -0
- package/dist/a11y/contrast.d.ts +55 -0
- package/dist/a11y/contrast.d.ts.map +1 -0
- package/dist/a11y/contrast.js +93 -0
- package/dist/a11y/contrast.js.map +1 -0
- package/dist/a11y/focus.d.ts +124 -0
- package/dist/a11y/focus.d.ts.map +1 -0
- package/dist/a11y/focus.js +282 -0
- package/dist/a11y/focus.js.map +1 -0
- package/dist/a11y/index.d.ts +22 -0
- package/dist/a11y/index.d.ts.map +1 -0
- package/dist/a11y/index.js +18 -0
- package/dist/a11y/index.js.map +1 -0
- package/dist/a11y/keyboard.d.ts +145 -0
- package/dist/a11y/keyboard.d.ts.map +1 -0
- package/dist/a11y/keyboard.js +317 -0
- package/dist/a11y/keyboard.js.map +1 -0
- package/dist/adapters/default-image.d.ts +26 -0
- package/dist/adapters/default-image.d.ts.map +1 -0
- package/dist/adapters/default-image.js +37 -0
- package/dist/adapters/default-image.js.map +1 -0
- package/dist/adapters/index.d.ts +28 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/next-image.d.ts +39 -0
- package/dist/adapters/next-image.d.ts.map +1 -0
- package/dist/adapters/next-image.js +124 -0
- package/dist/adapters/next-image.js.map +1 -0
- package/dist/adapters/types.d.ts +62 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +11 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/analytics/index.d.ts +15 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +15 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/tracker.d.ts +66 -0
- package/dist/analytics/tracker.d.ts.map +1 -0
- package/dist/analytics/tracker.js +251 -0
- package/dist/analytics/tracker.js.map +1 -0
- package/dist/analytics/types.d.ts +131 -0
- package/dist/analytics/types.d.ts.map +1 -0
- package/dist/analytics/types.js +11 -0
- package/dist/analytics/types.js.map +1 -0
- package/dist/auth/index.d.ts +16 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/provider.d.ts +133 -0
- package/dist/auth/provider.d.ts.map +1 -0
- package/dist/auth/provider.js +473 -0
- package/dist/auth/provider.js.map +1 -0
- package/dist/auth/types.d.ts +118 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +9 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/binding/collection.d.ts +191 -0
- package/dist/binding/collection.d.ts.map +1 -0
- package/dist/binding/collection.js +358 -0
- package/dist/binding/collection.js.map +1 -0
- package/dist/binding/datasource.d.ts +320 -0
- package/dist/binding/datasource.d.ts.map +1 -0
- package/dist/binding/datasource.js +556 -0
- package/dist/binding/datasource.js.map +1 -0
- package/dist/binding/engine.d.ts +66 -0
- package/dist/binding/engine.d.ts.map +1 -0
- package/dist/binding/engine.js +268 -0
- package/dist/binding/engine.js.map +1 -0
- package/dist/binding/expression.d.ts +151 -0
- package/dist/binding/expression.d.ts.map +1 -0
- package/dist/binding/expression.js +787 -0
- package/dist/binding/expression.js.map +1 -0
- package/dist/binding/index.d.ts +20 -0
- package/dist/binding/index.d.ts.map +1 -0
- package/dist/binding/index.js +14 -0
- package/dist/binding/index.js.map +1 -0
- package/dist/binding/two-way.d.ts +150 -0
- package/dist/binding/two-way.d.ts.map +1 -0
- package/dist/binding/two-way.js +462 -0
- package/dist/binding/two-way.js.map +1 -0
- package/dist/binding/types.d.ts +120 -0
- package/dist/binding/types.d.ts.map +1 -0
- package/dist/binding/types.js +9 -0
- package/dist/binding/types.js.map +1 -0
- package/dist/blocks/__test-fixtures__/block-fixtures.d.ts +40 -0
- package/dist/blocks/__test-fixtures__/block-fixtures.d.ts.map +1 -0
- package/dist/blocks/__test-fixtures__/block-fixtures.js +506 -0
- package/dist/blocks/__test-fixtures__/block-fixtures.js.map +1 -0
- package/dist/blocks/component-types.d.ts +82 -0
- package/dist/blocks/component-types.d.ts.map +1 -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 +169 -0
- package/dist/blocks/composition-rules.d.ts.map +1 -0
- package/dist/blocks/composition-rules.js +391 -0
- package/dist/blocks/composition-rules.js.map +1 -0
- package/dist/blocks/hooks.d.ts +32 -0
- package/dist/blocks/hooks.d.ts.map +1 -0
- package/dist/blocks/hooks.js +42 -0
- package/dist/blocks/hooks.js.map +1 -0
- package/dist/blocks/index.d.ts +16 -0
- package/dist/blocks/index.d.ts.map +1 -0
- package/dist/blocks/index.js +24 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/registry.d.ts +85 -0
- package/dist/blocks/registry.d.ts.map +1 -0
- package/dist/blocks/registry.js +136 -0
- package/dist/blocks/registry.js.map +1 -0
- package/dist/blocks/rule-engine.d.ts +132 -0
- package/dist/blocks/rule-engine.d.ts.map +1 -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 +14 -0
- package/dist/blocks/surface-event-context.d.ts.map +1 -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 +14 -0
- package/dist/blocks/surface-id-context.d.ts.map +1 -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 +42 -0
- package/dist/bus.d.ts.map +1 -0
- package/dist/bus.js +158 -0
- package/dist/bus.js.map +1 -0
- package/dist/cache/index.d.ts +10 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +10 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/memory.d.ts +33 -0
- package/dist/cache/memory.d.ts.map +1 -0
- package/dist/cache/memory.js +138 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/types.d.ts +63 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +9 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/capability/index.d.ts +73 -0
- package/dist/capability/index.d.ts.map +1 -0
- package/dist/capability/index.js +87 -0
- package/dist/capability/index.js.map +1 -0
- package/dist/capability/kernel-capabilities.d.ts +32 -0
- package/dist/capability/kernel-capabilities.d.ts.map +1 -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 +72 -0
- package/dist/capability/platform-capabilities.d.ts.map +1 -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 +71 -0
- package/dist/capability/type-mapping.d.ts.map +1 -0
- package/dist/capability/type-mapping.js +107 -0
- package/dist/capability/type-mapping.js.map +1 -0
- package/dist/capability/validation.d.ts +47 -0
- package/dist/capability/validation.d.ts.map +1 -0
- package/dist/capability/validation.js +54 -0
- package/dist/capability/validation.js.map +1 -0
- package/dist/connectors/declared-connector.d.ts +49 -0
- package/dist/connectors/declared-connector.d.ts.map +1 -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 +51 -0
- package/dist/connectors/engine-registry.d.ts.map +1 -0
- package/dist/connectors/engine-registry.js +135 -0
- package/dist/connectors/engine-registry.js.map +1 -0
- package/dist/connectors/index.d.ts +15 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +14 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/module-loader.d.ts +71 -0
- package/dist/connectors/module-loader.d.ts.map +1 -0
- package/dist/connectors/module-loader.js +143 -0
- package/dist/connectors/module-loader.js.map +1 -0
- package/dist/connectors/types.d.ts +175 -0
- package/dist/connectors/types.d.ts.map +1 -0
- package/dist/connectors/types.js +23 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/context.d.ts +28 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/data/DataCapabilityProvider.d.ts +40 -0
- package/dist/data/DataCapabilityProvider.d.ts.map +1 -0
- package/dist/data/DataCapabilityProvider.js +58 -0
- package/dist/data/DataCapabilityProvider.js.map +1 -0
- package/dist/data/index.d.ts +18 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +18 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/manager.d.ts +41 -0
- package/dist/data/manager.d.ts.map +1 -0
- package/dist/data/manager.js +442 -0
- package/dist/data/manager.js.map +1 -0
- package/dist/data/types.d.ts +219 -0
- package/dist/data/types.d.ts.map +1 -0
- package/dist/data/types.js +2 -0
- package/dist/data/types.js.map +1 -0
- package/dist/entity/aggregate.d.ts +9 -0
- package/dist/entity/aggregate.d.ts.map +1 -0
- package/dist/entity/aggregate.js +24 -0
- package/dist/entity/aggregate.js.map +1 -0
- package/dist/entity/apply-query.d.ts +59 -0
- package/dist/entity/apply-query.d.ts.map +1 -0
- package/dist/entity/apply-query.js +264 -0
- package/dist/entity/apply-query.js.map +1 -0
- package/dist/entity/behavior.d.ts +95 -0
- package/dist/entity/behavior.d.ts.map +1 -0
- package/dist/entity/behavior.js +78 -0
- package/dist/entity/behavior.js.map +1 -0
- package/dist/entity/connector.d.ts +142 -0
- package/dist/entity/connector.d.ts.map +1 -0
- package/dist/entity/connector.js +89 -0
- package/dist/entity/connector.js.map +1 -0
- package/dist/entity/connectors/index.d.ts +13 -0
- package/dist/entity/connectors/index.d.ts.map +1 -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 +52 -0
- package/dist/entity/connectors/static.d.ts.map +1 -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 +51 -0
- package/dist/entity/entity-datasource.d.ts.map +1 -0
- package/dist/entity/entity-datasource.js +33 -0
- package/dist/entity/entity-datasource.js.map +1 -0
- package/dist/entity/facade.d.ts +71 -0
- package/dist/entity/facade.d.ts.map +1 -0
- package/dist/entity/facade.js +70 -0
- package/dist/entity/facade.js.map +1 -0
- package/dist/entity/http-connector.d.ts +124 -0
- package/dist/entity/http-connector.d.ts.map +1 -0
- package/dist/entity/http-connector.js +295 -0
- package/dist/entity/http-connector.js.map +1 -0
- package/dist/entity/index.d.ts +39 -0
- package/dist/entity/index.d.ts.map +1 -0
- package/dist/entity/index.js +31 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/entity/postgres-connector.d.ts +65 -0
- package/dist/entity/postgres-connector.d.ts.map +1 -0
- package/dist/entity/postgres-connector.js +239 -0
- package/dist/entity/postgres-connector.js.map +1 -0
- package/dist/entity/registry.d.ts +60 -0
- package/dist/entity/registry.d.ts.map +1 -0
- package/dist/entity/registry.js +90 -0
- package/dist/entity/registry.js.map +1 -0
- package/dist/entity/store.d.ts +67 -0
- package/dist/entity/store.d.ts.map +1 -0
- package/dist/entity/store.js +349 -0
- package/dist/entity/store.js.map +1 -0
- package/dist/entity/types.d.ts +181 -0
- package/dist/entity/types.d.ts.map +1 -0
- package/dist/entity/types.js +10 -0
- package/dist/entity/types.js.map +1 -0
- package/dist/execution-context.d.ts +89 -0
- package/dist/execution-context.d.ts.map +1 -0
- package/dist/execution-context.js +146 -0
- package/dist/execution-context.js.map +1 -0
- package/dist/executor.d.ts +28 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +33 -0
- package/dist/executor.js.map +1 -0
- package/dist/forms/form-entity-bridge.d.ts +95 -0
- package/dist/forms/form-entity-bridge.d.ts.map +1 -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 +35 -0
- package/dist/forms/form-write-target.d.ts.map +1 -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 +23 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/index.js +25 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/named-control-values.d.ts +28 -0
- package/dist/forms/named-control-values.d.ts.map +1 -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 +56 -0
- package/dist/forms/processor.d.ts.map +1 -0
- package/dist/forms/processor.js +154 -0
- package/dist/forms/processor.js.map +1 -0
- package/dist/forms/types.d.ts +175 -0
- package/dist/forms/types.d.ts.map +1 -0
- package/dist/forms/types.js +33 -0
- package/dist/forms/types.js.map +1 -0
- package/dist/forms/validator.d.ts +62 -0
- package/dist/forms/validator.d.ts.map +1 -0
- package/dist/forms/validator.js +218 -0
- package/dist/forms/validator.js.map +1 -0
- package/dist/graph/index.d.ts +11 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +9 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/resolver.d.ts +67 -0
- package/dist/graph/resolver.d.ts.map +1 -0
- package/dist/graph/resolver.js +243 -0
- package/dist/graph/resolver.js.map +1 -0
- package/dist/graph/types.d.ts +126 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +9 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/hooks/useInstalledAtoms.d.ts +97 -0
- package/dist/hooks/useInstalledAtoms.d.ts.map +1 -0
- package/dist/hooks/useInstalledAtoms.js +92 -0
- package/dist/hooks/useInstalledAtoms.js.map +1 -0
- package/dist/i18n/index.d.ts +16 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +14 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/translator.d.ts +77 -0
- package/dist/i18n/translator.d.ts.map +1 -0
- package/dist/i18n/translator.js +357 -0
- package/dist/i18n/translator.js.map +1 -0
- package/dist/i18n/types.d.ts +94 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +9 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +48 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +258 -0
- package/dist/kernel.js.map +1 -0
- package/dist/perf/budget.d.ts +93 -0
- package/dist/perf/budget.d.ts.map +1 -0
- package/dist/perf/budget.js +122 -0
- package/dist/perf/budget.js.map +1 -0
- package/dist/perf/index.d.ts +17 -0
- package/dist/perf/index.d.ts.map +1 -0
- package/dist/perf/index.js +13 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/render-timer.d.ts +91 -0
- package/dist/perf/render-timer.d.ts.map +1 -0
- package/dist/perf/render-timer.js +136 -0
- package/dist/perf/render-timer.js.map +1 -0
- package/dist/perf/reporter.d.ts +127 -0
- package/dist/perf/reporter.d.ts.map +1 -0
- package/dist/perf/reporter.js +241 -0
- package/dist/perf/reporter.js.map +1 -0
- package/dist/perf/web-vitals.d.ts +112 -0
- package/dist/perf/web-vitals.d.ts.map +1 -0
- package/dist/perf/web-vitals.js +268 -0
- package/dist/perf/web-vitals.js.map +1 -0
- package/dist/registry.d.ts +64 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +126 -0
- package/dist/registry.js.map +1 -0
- package/dist/render/BehaviorSurfaceProvider.d.ts +66 -0
- package/dist/render/BehaviorSurfaceProvider.d.ts.map +1 -0
- package/dist/render/BehaviorSurfaceProvider.js +185 -0
- package/dist/render/BehaviorSurfaceProvider.js.map +1 -0
- package/dist/render/BlockErrorBoundary.d.ts +27 -0
- package/dist/render/BlockErrorBoundary.d.ts.map +1 -0
- package/dist/render/BlockErrorBoundary.js +41 -0
- package/dist/render/BlockErrorBoundary.js.map +1 -0
- package/dist/render/DataBoundSurfaceRenderer.d.ts +79 -0
- package/dist/render/DataBoundSurfaceRenderer.d.ts.map +1 -0
- package/dist/render/DataBoundSurfaceRenderer.js +998 -0
- package/dist/render/DataBoundSurfaceRenderer.js.map +1 -0
- package/dist/render/DynamicBlockWrapper.d.ts +24 -0
- package/dist/render/DynamicBlockWrapper.d.ts.map +1 -0
- package/dist/render/DynamicBlockWrapper.js +117 -0
- package/dist/render/DynamicBlockWrapper.js.map +1 -0
- package/dist/render/appearance-resolver.d.ts +51 -0
- package/dist/render/appearance-resolver.d.ts.map +1 -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 +70 -0
- package/dist/render/capability-gate.d.ts.map +1 -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 +42 -0
- package/dist/render/conditional-states-css.d.ts.map +1 -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 +32 -0
- package/dist/render/connections-context.d.ts.map +1 -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 +37 -0
- package/dist/render/dark-theme-css.d.ts.map +1 -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 +192 -0
- package/dist/render/data-binding-types.d.ts.map +1 -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 +31 -0
- package/dist/render/data-cache.d.ts.map +1 -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 +18 -0
- package/dist/render/derive-dark-theme.d.ts.map +1 -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 +78 -0
- package/dist/render/dynamic-artifact-context.d.ts.map +1 -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 +81 -0
- package/dist/render/dynamic-block-loader.d.ts.map +1 -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 +19 -0
- package/dist/render/endpoint.d.ts.map +1 -0
- package/dist/render/endpoint.js +109 -0
- package/dist/render/endpoint.js.map +1 -0
- package/dist/render/entity-facade-context.d.ts +35 -0
- package/dist/render/entity-facade-context.d.ts.map +1 -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 +42 -0
- package/dist/render/group-states-css.d.ts.map +1 -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 +9 -0
- package/dist/render/hooks/useSurfaceEvents.d.ts.map +1 -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 +9 -0
- package/dist/render/hooks/useSurfaceId.d.ts.map +1 -0
- package/dist/render/hooks/useSurfaceId.js +9 -0
- package/dist/render/hooks/useSurfaceId.js.map +1 -0
- package/dist/render/index.d.ts +49 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +61 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/interaction-rule-engine.d.ts +13 -0
- package/dist/render/interaction-rule-engine.d.ts.map +1 -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 +32 -0
- package/dist/render/keyframes-css.d.ts.map +1 -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 +4 -0
- package/dist/render/query-state.d.ts.map +1 -0
- package/dist/render/query-state.js +70 -0
- package/dist/render/query-state.js.map +1 -0
- package/dist/render/renderer.d.ts +45 -0
- package/dist/render/renderer.d.ts.map +1 -0
- package/dist/render/renderer.js +937 -0
- package/dist/render/renderer.js.map +1 -0
- package/dist/render/resolve-expressions.d.ts +38 -0
- package/dist/render/resolve-expressions.d.ts.map +1 -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 +43 -0
- package/dist/render/responsive-style-css.d.ts.map +1 -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 +50 -0
- package/dist/render/responsive.d.ts.map +1 -0
- package/dist/render/responsive.js +99 -0
- package/dist/render/responsive.js.map +1 -0
- package/dist/render/scoped-css.d.ts +72 -0
- package/dist/render/scoped-css.d.ts.map +1 -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 +70 -0
- package/dist/render/shell-selection.d.ts.map +1 -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 +33 -0
- package/dist/render/states-css.d.ts.map +1 -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 +19 -0
- package/dist/render/surface-data-context.d.ts.map +1 -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 +20 -0
- package/dist/render/surface-event-bus.d.ts.map +1 -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 +12 -0
- package/dist/render/surface-event-context.d.ts.map +1 -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 +9 -0
- package/dist/render/surface-id-context.d.ts.map +1 -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 +15 -0
- package/dist/render/theme-utils.d.ts.map +1 -0
- package/dist/render/theme-utils.js +15 -0
- package/dist/render/theme-utils.js.map +1 -0
- package/dist/render/types.d.ts +283 -0
- package/dist/render/types.d.ts.map +1 -0
- package/dist/render/types.js +9 -0
- package/dist/render/types.js.map +1 -0
- package/dist/render/visibility-css.d.ts +43 -0
- package/dist/render/visibility-css.d.ts.map +1 -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 +35 -0
- package/dist/render/width-scope.d.ts.map +1 -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 +34 -0
- package/dist/render/write-only-sources.d.ts.map +1 -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 +15 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +15 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/router.d.ts +43 -0
- package/dist/routing/router.d.ts.map +1 -0
- package/dist/routing/router.js +218 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/routing/types.d.ts +125 -0
- package/dist/routing/types.d.ts.map +1 -0
- package/dist/routing/types.js +9 -0
- package/dist/routing/types.js.map +1 -0
- package/dist/seo/generator.d.ts +40 -0
- package/dist/seo/generator.d.ts.map +1 -0
- package/dist/seo/generator.js +236 -0
- package/dist/seo/generator.js.map +1 -0
- package/dist/seo/index.d.ts +15 -0
- package/dist/seo/index.d.ts.map +1 -0
- package/dist/seo/index.js +15 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/types.d.ts +182 -0
- package/dist/seo/types.d.ts.map +1 -0
- package/dist/seo/types.js +9 -0
- package/dist/seo/types.js.map +1 -0
- package/dist/state/index.d.ts +16 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +16 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/manager.d.ts +34 -0
- package/dist/state/manager.d.ts.map +1 -0
- package/dist/state/manager.js +81 -0
- package/dist/state/manager.js.map +1 -0
- package/dist/state/store.d.ts +41 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +145 -0
- package/dist/state/store.js.map +1 -0
- package/dist/state/types.d.ts +108 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +9 -0
- package/dist/state/types.js.map +1 -0
- package/dist/surface/expression.d.ts +66 -0
- package/dist/surface/expression.d.ts.map +1 -0
- package/dist/surface/expression.js +179 -0
- package/dist/surface/expression.js.map +1 -0
- package/dist/surface/index.d.ts +33 -0
- package/dist/surface/index.d.ts.map +1 -0
- package/dist/surface/index.js +25 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/surface/interpreter.d.ts +97 -0
- package/dist/surface/interpreter.d.ts.map +1 -0
- package/dist/surface/interpreter.js +231 -0
- package/dist/surface/interpreter.js.map +1 -0
- package/dist/surface/layout.d.ts +55 -0
- package/dist/surface/layout.d.ts.map +1 -0
- package/dist/surface/layout.js +288 -0
- package/dist/surface/layout.js.map +1 -0
- package/dist/surface/output.d.ts +91 -0
- package/dist/surface/output.d.ts.map +1 -0
- package/dist/surface/output.js +104 -0
- package/dist/surface/output.js.map +1 -0
- package/dist/surface/primitives.d.ts +181 -0
- package/dist/surface/primitives.d.ts.map +1 -0
- package/dist/surface/primitives.js +339 -0
- package/dist/surface/primitives.js.map +1 -0
- package/dist/surface/renderer.d.ts +97 -0
- package/dist/surface/renderer.d.ts.map +1 -0
- package/dist/surface/renderer.js +190 -0
- package/dist/surface/renderer.js.map +1 -0
- package/dist/surface/schemas.d.ts +182 -0
- package/dist/surface/schemas.d.ts.map +1 -0
- package/dist/surface/schemas.js +142 -0
- package/dist/surface/schemas.js.map +1 -0
- package/dist/surface/transforms.d.ts +145 -0
- package/dist/surface/transforms.d.ts.map +1 -0
- package/dist/surface/transforms.js +120 -0
- package/dist/surface/transforms.js.map +1 -0
- package/dist/surface/types.d.ts +161 -0
- package/dist/surface/types.d.ts.map +1 -0
- package/dist/surface/types.js +51 -0
- package/dist/surface/types.js.map +1 -0
- package/dist/surface/validation.d.ts +124 -0
- package/dist/surface/validation.d.ts.map +1 -0
- package/dist/surface/validation.js +147 -0
- package/dist/surface/validation.js.map +1 -0
- package/dist/validator.d.ts +56 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +100 -0
- package/dist/validator.js.map +1 -0
- package/package.json +105 -3
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression Evaluator for Data Binding
|
|
3
|
+
*
|
|
4
|
+
* Enhanced expression evaluation for data binding in surfaces
|
|
5
|
+
*
|
|
6
|
+
* This module extends the base expression evaluator with:
|
|
7
|
+
* - Arithmetic operations (+, -, *, /, %)
|
|
8
|
+
* - Data/state/params context separation
|
|
9
|
+
* - Dependency extraction for reactive updates
|
|
10
|
+
* - Safe null-coalescing operators
|
|
11
|
+
*
|
|
12
|
+
* @module @pwfabric/runtime/binding/expression
|
|
13
|
+
*/
|
|
14
|
+
// ============================================================
|
|
15
|
+
// PATH RESOLUTION
|
|
16
|
+
// ============================================================
|
|
17
|
+
/**
|
|
18
|
+
* Read one named property off a value, stepping into a list of one record.
|
|
19
|
+
*
|
|
20
|
+
* A LIST SOURCE ALWAYS BINDS AS A LIST (DataBoundSurfaceRenderer): a `rest`,
|
|
21
|
+
* `entity` or `graphql` source answering with ONE object is bound as `[obj]`,
|
|
22
|
+
* so `{{src[0].field}}` and `{{src.length}}` behave the same whatever the
|
|
23
|
+
* endpoint returned. But a detail page is authored as `{{product.title}}`, and
|
|
24
|
+
* a property read on an array finds nothing — such a page would render blank
|
|
25
|
+
* (`{{rows.name}}` → empty while `{{rows[0].name}}` → "banana").
|
|
26
|
+
*
|
|
27
|
+
* So: when the value is an array of EXACTLY ONE element and the array itself
|
|
28
|
+
* does not carry the key (not `length`, not an index, not an array method),
|
|
29
|
+
* the read continues into that one element. A list of two or more is a list —
|
|
30
|
+
* its `.title` stays undefined, as it always was. Both evaluators (this one
|
|
31
|
+
* and surface/expression) read properties through here, so a path means the
|
|
32
|
+
* same thing in a binding and in a block prop.
|
|
33
|
+
*/
|
|
34
|
+
export function readProperty(target, key) {
|
|
35
|
+
if (target === null || target === undefined || typeof target !== 'object')
|
|
36
|
+
return undefined;
|
|
37
|
+
if (Array.isArray(target) && target.length === 1 && !(key in target)) {
|
|
38
|
+
const only = target[0];
|
|
39
|
+
return only !== null && typeof only === 'object'
|
|
40
|
+
? only[key]
|
|
41
|
+
: undefined;
|
|
42
|
+
}
|
|
43
|
+
return target[key];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a dotted path in an object, handling nested properties and array indices
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* resolvePath({ user: { name: 'John' } }, 'user.name') // 'John'
|
|
50
|
+
* resolvePath({ items: ['a', 'b'] }, 'items[0]') // 'a'
|
|
51
|
+
* resolvePath({ users: [{ name: 'John' }] }, 'users[0].name') // 'John'
|
|
52
|
+
*/
|
|
53
|
+
export function resolvePath(obj, path) {
|
|
54
|
+
if (!path || typeof obj !== 'object' || obj === null) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const parts = parsePath(path);
|
|
58
|
+
let current = obj;
|
|
59
|
+
for (const part of parts) {
|
|
60
|
+
if (current === null || current === undefined) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
if (part.type === 'property') {
|
|
64
|
+
if (typeof current === 'object') {
|
|
65
|
+
current = readProperty(current, part.key);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (part.type === 'index') {
|
|
72
|
+
if (Array.isArray(current)) {
|
|
73
|
+
current = current[part.index];
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return current;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Parse a path string into segments
|
|
84
|
+
*/
|
|
85
|
+
function parsePath(path) {
|
|
86
|
+
const segments = [];
|
|
87
|
+
// Match property names and array indices: foo, [0], foo[0], foo.bar[1].baz
|
|
88
|
+
//
|
|
89
|
+
// A HYPHEN is a name CHARACTER. With `\w+` it would be a boundary, and since
|
|
90
|
+
// source ids are hyphenated, `hero-stats.0.text` would split into FOUR
|
|
91
|
+
// segments (`hero`,`stats`,`0`,`text`) instead of three and never resolve.
|
|
92
|
+
// The sibling resolver (surface/expression.ts) accepts hyphenated ids too:
|
|
93
|
+
// its path regex is `/^[\w\-.[\]]+$/`.
|
|
94
|
+
const regex = /([\w-]+)|\[(\d+)\]/g;
|
|
95
|
+
let match;
|
|
96
|
+
while ((match = regex.exec(path)) !== null) {
|
|
97
|
+
if (match[1] !== undefined) {
|
|
98
|
+
// Property name
|
|
99
|
+
segments.push({ type: 'property', key: match[1] });
|
|
100
|
+
}
|
|
101
|
+
else if (match[2] !== undefined) {
|
|
102
|
+
// Array index
|
|
103
|
+
segments.push({ type: 'index', index: parseInt(match[2], 10) });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return segments;
|
|
107
|
+
}
|
|
108
|
+
const MULTI_OPS = ['===', '!==', '>=', '<=', '==', '!=', '&&', '||', '??'];
|
|
109
|
+
const SINGLE_OPS = new Set(['+', '-', '*', '/', '%', '>', '<', '!']);
|
|
110
|
+
/** A token after which a `-` is binary minus, not the sign of a number. */
|
|
111
|
+
function endsValue(t) {
|
|
112
|
+
if (!t)
|
|
113
|
+
return false;
|
|
114
|
+
if (t.type === 'number' || t.type === 'string' || t.type === 'literal' || t.type === 'identifier')
|
|
115
|
+
return true;
|
|
116
|
+
return (t.type === 'paren' && t.value === ')') || (t.type === 'bracket' && t.value === ']');
|
|
117
|
+
}
|
|
118
|
+
function tokenize(expr, opts) {
|
|
119
|
+
const tokens = [];
|
|
120
|
+
let i = 0;
|
|
121
|
+
const nameChar = /[\w$]/;
|
|
122
|
+
const readName = (allowLeadingDigit) => {
|
|
123
|
+
let name = '';
|
|
124
|
+
while (i < expr.length) {
|
|
125
|
+
const c = expr.charAt(i);
|
|
126
|
+
if (nameChar.test(c)) {
|
|
127
|
+
name += c;
|
|
128
|
+
i++;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
// a hyphen joins two name characters (`hero-stats`) in hyphenNames mode
|
|
132
|
+
if (opts.hyphenNames &&
|
|
133
|
+
c === '-' &&
|
|
134
|
+
name.length > 0 &&
|
|
135
|
+
/[\w$]/.test(expr.charAt(i + 1))) {
|
|
136
|
+
name += c;
|
|
137
|
+
i++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
if (!allowLeadingDigit && /^\d/.test(name))
|
|
143
|
+
throw new Error(`Unexpected number in name '${name}'`);
|
|
144
|
+
return name;
|
|
145
|
+
};
|
|
146
|
+
while (i < expr.length) {
|
|
147
|
+
const ch = expr.charAt(i);
|
|
148
|
+
if (/\s/.test(ch)) {
|
|
149
|
+
i++;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const prev = tokens[tokens.length - 1];
|
|
153
|
+
// A property name after a dot: digits (`items.0`) and keywords
|
|
154
|
+
// (`item.default`, `row.null`) are names here, never literals.
|
|
155
|
+
if (prev?.type === 'dot' && /[\w$]/.test(ch)) {
|
|
156
|
+
const name = readName(true);
|
|
157
|
+
tokens.push({ type: 'identifier', value: name, raw: name });
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (ch === "'" || ch === '"') {
|
|
161
|
+
const quote = ch;
|
|
162
|
+
let value = '';
|
|
163
|
+
i++;
|
|
164
|
+
let closed = false;
|
|
165
|
+
while (i < expr.length) {
|
|
166
|
+
const c = expr.charAt(i);
|
|
167
|
+
if (c === quote) {
|
|
168
|
+
closed = true;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
if (c === '\\' && i + 1 < expr.length) {
|
|
172
|
+
i++;
|
|
173
|
+
const e = expr.charAt(i);
|
|
174
|
+
value += e === 'n' ? '\n' : e === 't' ? '\t' : e;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
value += c;
|
|
178
|
+
}
|
|
179
|
+
i++;
|
|
180
|
+
}
|
|
181
|
+
if (!closed)
|
|
182
|
+
throw new Error('Unterminated string literal');
|
|
183
|
+
i++;
|
|
184
|
+
tokens.push({ type: 'string', value, raw: `${quote}${value}${quote}` });
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
// Numbers. A leading `-` is a sign only where a value cannot end —
|
|
188
|
+
// `5-3` is subtraction, `-3` and `a * -3` are negative literals.
|
|
189
|
+
if (/\d/.test(ch) || (ch === '-' && /\d/.test(expr.charAt(i + 1)) && !endsValue(prev))) {
|
|
190
|
+
let num = '';
|
|
191
|
+
if (ch === '-') {
|
|
192
|
+
num = '-';
|
|
193
|
+
i++;
|
|
194
|
+
}
|
|
195
|
+
while (i < expr.length && /\d/.test(expr.charAt(i)))
|
|
196
|
+
num += expr.charAt(i++);
|
|
197
|
+
if (expr.charAt(i) === '.' && /\d/.test(expr.charAt(i + 1))) {
|
|
198
|
+
num += expr.charAt(i++);
|
|
199
|
+
while (i < expr.length && /\d/.test(expr.charAt(i)))
|
|
200
|
+
num += expr.charAt(i++);
|
|
201
|
+
}
|
|
202
|
+
tokens.push({ type: 'number', value: parseFloat(num), raw: num });
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (/[a-zA-Z_$]/.test(ch)) {
|
|
206
|
+
const name = readName(false);
|
|
207
|
+
if (name === 'true')
|
|
208
|
+
tokens.push({ type: 'literal', value: true, raw: name });
|
|
209
|
+
else if (name === 'false')
|
|
210
|
+
tokens.push({ type: 'literal', value: false, raw: name });
|
|
211
|
+
else if (name === 'null')
|
|
212
|
+
tokens.push({ type: 'literal', value: null, raw: name });
|
|
213
|
+
else if (name === 'undefined')
|
|
214
|
+
tokens.push({ type: 'literal', value: opts.undefinedAsNull ? null : undefined, raw: name });
|
|
215
|
+
else
|
|
216
|
+
tokens.push({ type: 'identifier', value: name, raw: name });
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const multi = MULTI_OPS.find((op) => expr.startsWith(op, i));
|
|
220
|
+
if (multi) {
|
|
221
|
+
tokens.push({ type: 'operator', value: multi, raw: multi });
|
|
222
|
+
i += multi.length;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (SINGLE_OPS.has(ch))
|
|
226
|
+
tokens.push({ type: 'operator', value: ch, raw: ch });
|
|
227
|
+
else if (ch === '(' || ch === ')')
|
|
228
|
+
tokens.push({ type: 'paren', value: ch, raw: ch });
|
|
229
|
+
else if (ch === '[' || ch === ']')
|
|
230
|
+
tokens.push({ type: 'bracket', value: ch, raw: ch });
|
|
231
|
+
else if (ch === '.')
|
|
232
|
+
tokens.push({ type: 'dot', value: ch, raw: ch });
|
|
233
|
+
else if (ch === ',')
|
|
234
|
+
tokens.push({ type: 'comma', value: ch, raw: ch });
|
|
235
|
+
else if (ch === '?')
|
|
236
|
+
tokens.push({ type: 'question', value: ch, raw: ch });
|
|
237
|
+
else if (ch === ':')
|
|
238
|
+
tokens.push({ type: 'colon', value: ch, raw: ch });
|
|
239
|
+
else
|
|
240
|
+
throw new Error(`Unexpected character '${ch}' at ${i}`);
|
|
241
|
+
i++;
|
|
242
|
+
}
|
|
243
|
+
return tokens;
|
|
244
|
+
}
|
|
245
|
+
const BINARY_LEVELS = [
|
|
246
|
+
['||'],
|
|
247
|
+
['&&'],
|
|
248
|
+
['??'],
|
|
249
|
+
['===', '!==', '==', '!='],
|
|
250
|
+
['>', '<', '>=', '<='],
|
|
251
|
+
['+', '-'],
|
|
252
|
+
['*', '/', '%'],
|
|
253
|
+
];
|
|
254
|
+
function parse(tokens) {
|
|
255
|
+
let pos = 0;
|
|
256
|
+
const peek = () => tokens[pos];
|
|
257
|
+
const is = (type, value) => {
|
|
258
|
+
const t = tokens[pos];
|
|
259
|
+
return t !== undefined && t.type === type && (value === undefined || t.value === value);
|
|
260
|
+
};
|
|
261
|
+
const expect = (type, value, what) => {
|
|
262
|
+
if (!is(type, value))
|
|
263
|
+
throw new Error(`Expected ${what}`);
|
|
264
|
+
pos++;
|
|
265
|
+
};
|
|
266
|
+
const parseArgs = (close, closeType) => {
|
|
267
|
+
const args = [];
|
|
268
|
+
if (is(closeType, close)) {
|
|
269
|
+
pos++;
|
|
270
|
+
return args;
|
|
271
|
+
}
|
|
272
|
+
for (;;) {
|
|
273
|
+
args.push(parseTernary());
|
|
274
|
+
if (is('comma')) {
|
|
275
|
+
pos++;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
expect(closeType, close, `'${close}'`);
|
|
279
|
+
return args;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
function parseTernary() {
|
|
283
|
+
const test = parseBinary(0);
|
|
284
|
+
if (!is('question'))
|
|
285
|
+
return test;
|
|
286
|
+
pos++;
|
|
287
|
+
const then = parseTernary();
|
|
288
|
+
expect('colon', ':', "':' in ternary expression");
|
|
289
|
+
return { k: 'cond', test, then, else: parseTernary() };
|
|
290
|
+
}
|
|
291
|
+
function parseBinary(level) {
|
|
292
|
+
const ops = BINARY_LEVELS[level];
|
|
293
|
+
if (!ops)
|
|
294
|
+
return parseUnary();
|
|
295
|
+
let left = parseBinary(level + 1);
|
|
296
|
+
for (;;) {
|
|
297
|
+
const t = peek();
|
|
298
|
+
if (!t || t.type !== 'operator' || !ops.includes(t.value))
|
|
299
|
+
return left;
|
|
300
|
+
pos++;
|
|
301
|
+
left = { k: 'binary', op: t.value, l: left, r: parseBinary(level + 1) };
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function parseUnary() {
|
|
305
|
+
if (is('operator', '!') || is('operator', '-') || is('operator', '+')) {
|
|
306
|
+
const op = tokens[pos++].value;
|
|
307
|
+
return { k: 'unary', op, arg: parseUnary() };
|
|
308
|
+
}
|
|
309
|
+
return parsePostfix();
|
|
310
|
+
}
|
|
311
|
+
function parsePostfix() {
|
|
312
|
+
let node = parsePrimary();
|
|
313
|
+
for (;;) {
|
|
314
|
+
if (is('dot')) {
|
|
315
|
+
pos++;
|
|
316
|
+
const t = peek();
|
|
317
|
+
if (!t || t.type !== 'identifier')
|
|
318
|
+
throw new Error('Expected property name after .');
|
|
319
|
+
pos++;
|
|
320
|
+
const name = t.value;
|
|
321
|
+
if (is('paren', '(')) {
|
|
322
|
+
pos++;
|
|
323
|
+
node = { k: 'method', obj: node, name, args: parseArgs(')', 'paren') };
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
node = { k: 'member', obj: node, prop: name };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else if (is('bracket', '[')) {
|
|
330
|
+
pos++;
|
|
331
|
+
const index = parseTernary();
|
|
332
|
+
expect('bracket', ']', "']'");
|
|
333
|
+
node = { k: 'index', obj: node, index };
|
|
334
|
+
}
|
|
335
|
+
else if (is('paren', '(')) {
|
|
336
|
+
throw new Error('Only helpers and safe methods can be called');
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
return node;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function parsePrimary() {
|
|
344
|
+
const t = peek();
|
|
345
|
+
if (!t)
|
|
346
|
+
throw new Error('Unexpected end of expression');
|
|
347
|
+
if (t.type === 'number' || t.type === 'string' || t.type === 'literal') {
|
|
348
|
+
pos++;
|
|
349
|
+
return { k: 'lit', v: t.value };
|
|
350
|
+
}
|
|
351
|
+
if (t.type === 'identifier') {
|
|
352
|
+
pos++;
|
|
353
|
+
const name = t.value;
|
|
354
|
+
if (is('paren', '(')) {
|
|
355
|
+
pos++;
|
|
356
|
+
return { k: 'call', name, args: parseArgs(')', 'paren') };
|
|
357
|
+
}
|
|
358
|
+
return { k: 'name', name };
|
|
359
|
+
}
|
|
360
|
+
if (is('paren', '(')) {
|
|
361
|
+
pos++;
|
|
362
|
+
const inner = parseTernary();
|
|
363
|
+
expect('paren', ')', "')'");
|
|
364
|
+
return inner;
|
|
365
|
+
}
|
|
366
|
+
if (is('bracket', '[')) {
|
|
367
|
+
pos++;
|
|
368
|
+
return { k: 'array', items: parseArgs(']', 'bracket') };
|
|
369
|
+
}
|
|
370
|
+
throw new Error(`Unexpected token '${t.raw}'`);
|
|
371
|
+
}
|
|
372
|
+
const root = parseTernary();
|
|
373
|
+
if (pos < tokens.length)
|
|
374
|
+
throw new Error(`Unexpected token '${tokens[pos].raw}'`);
|
|
375
|
+
return root;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Parse once, evaluate many times. Block props are re-resolved on every state
|
|
379
|
+
* change, and the same few hundred template strings come through each time.
|
|
380
|
+
*/
|
|
381
|
+
const AST_CACHE_LIMIT = 2000;
|
|
382
|
+
const astCache = new Map();
|
|
383
|
+
function parseCached(expr, opts) {
|
|
384
|
+
const key = `${opts.hyphenNames ? 'h' : '-'}${opts.undefinedAsNull ? 'n' : '-'}${expr}`;
|
|
385
|
+
let hit = astCache.get(key);
|
|
386
|
+
if (hit === undefined) {
|
|
387
|
+
try {
|
|
388
|
+
hit = parse(tokenize(expr, opts));
|
|
389
|
+
}
|
|
390
|
+
catch (e) {
|
|
391
|
+
hit = e instanceof Error ? e : new Error(String(e));
|
|
392
|
+
}
|
|
393
|
+
if (astCache.size >= AST_CACHE_LIMIT)
|
|
394
|
+
astCache.clear();
|
|
395
|
+
astCache.set(key, hit);
|
|
396
|
+
}
|
|
397
|
+
if (hit instanceof Error)
|
|
398
|
+
throw hit;
|
|
399
|
+
return hit;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Methods that may be called on a value. Pure, allocation-bounded, and
|
|
403
|
+
* nothing that reaches a constructor, a prototype or a function: a template
|
|
404
|
+
* is authored by a model and rendered in a visitor's browser.
|
|
405
|
+
*/
|
|
406
|
+
const STRING_METHODS = new Set([
|
|
407
|
+
'toLowerCase', 'toUpperCase', 'trim', 'trimStart', 'trimEnd', 'includes', 'startsWith', 'endsWith',
|
|
408
|
+
'indexOf', 'lastIndexOf', 'slice', 'substring', 'padStart', 'padEnd', 'split', 'replace',
|
|
409
|
+
'replaceAll', 'charAt', 'at', 'concat', 'repeat', 'toString', 'localeCompare',
|
|
410
|
+
]);
|
|
411
|
+
const ARRAY_METHODS = new Set(['includes', 'indexOf', 'lastIndexOf', 'join', 'slice', 'at', 'concat']);
|
|
412
|
+
const NUMBER_METHODS = new Set(['toFixed', 'toPrecision', 'toString', 'toLocaleString']);
|
|
413
|
+
function callMethod(target, name, args) {
|
|
414
|
+
if (target === null || target === undefined)
|
|
415
|
+
return undefined;
|
|
416
|
+
let allowed = false;
|
|
417
|
+
if (typeof target === 'string')
|
|
418
|
+
allowed = STRING_METHODS.has(name);
|
|
419
|
+
else if (Array.isArray(target))
|
|
420
|
+
allowed = ARRAY_METHODS.has(name);
|
|
421
|
+
else if (typeof target === 'number')
|
|
422
|
+
allowed = NUMBER_METHODS.has(name);
|
|
423
|
+
if (!allowed)
|
|
424
|
+
throw new Error(`Method '${name}' is not supported`);
|
|
425
|
+
// A `repeat`/`padStart` count from data must not allocate without bound.
|
|
426
|
+
if ((name === 'repeat' || name === 'padStart' || name === 'padEnd') && Number(args[0]) > 10_000) {
|
|
427
|
+
throw new Error(`Method '${name}' argument too large`);
|
|
428
|
+
}
|
|
429
|
+
const fn = target[name];
|
|
430
|
+
return fn.apply(target, args);
|
|
431
|
+
}
|
|
432
|
+
function readMember(target, prop) {
|
|
433
|
+
if (typeof target === 'string')
|
|
434
|
+
return prop === 'length' ? target.length : undefined;
|
|
435
|
+
if (target !== null && target !== undefined && typeof target === 'object')
|
|
436
|
+
return readProperty(target, prop);
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
function evalNode(node, scope) {
|
|
440
|
+
switch (node.k) {
|
|
441
|
+
case 'lit':
|
|
442
|
+
return node.v;
|
|
443
|
+
case 'name':
|
|
444
|
+
return scope.lookup(node.name);
|
|
445
|
+
case 'member':
|
|
446
|
+
return readMember(evalNode(node.obj, scope), node.prop);
|
|
447
|
+
case 'index': {
|
|
448
|
+
const target = evalNode(node.obj, scope);
|
|
449
|
+
const index = evalNode(node.index, scope);
|
|
450
|
+
if (target === null || target === undefined)
|
|
451
|
+
return undefined;
|
|
452
|
+
if (Array.isArray(target))
|
|
453
|
+
return target[index];
|
|
454
|
+
if (typeof target === 'object')
|
|
455
|
+
return target[String(index)];
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
case 'call': {
|
|
459
|
+
const fn = scope.helper(node.name);
|
|
460
|
+
if (typeof fn !== 'function')
|
|
461
|
+
throw new Error(`Unknown helper: ${node.name}`);
|
|
462
|
+
return fn(...node.args.map((a) => evalNode(a, scope)));
|
|
463
|
+
}
|
|
464
|
+
case 'method':
|
|
465
|
+
return callMethod(evalNode(node.obj, scope), node.name, node.args.map((a) => evalNode(a, scope)));
|
|
466
|
+
case 'unary': {
|
|
467
|
+
const v = evalNode(node.arg, scope);
|
|
468
|
+
if (node.op === '!')
|
|
469
|
+
return !v;
|
|
470
|
+
if (node.op === '-')
|
|
471
|
+
return -v;
|
|
472
|
+
return +v;
|
|
473
|
+
}
|
|
474
|
+
case 'cond':
|
|
475
|
+
return evalNode(node.test, scope) ? evalNode(node.then, scope) : evalNode(node.else, scope);
|
|
476
|
+
case 'array':
|
|
477
|
+
return node.items.map((n) => evalNode(n, scope));
|
|
478
|
+
case 'binary': {
|
|
479
|
+
// short-circuiting operators first — the right side may never run
|
|
480
|
+
if (node.op === '&&') {
|
|
481
|
+
const l = evalNode(node.l, scope);
|
|
482
|
+
return l ? evalNode(node.r, scope) : l;
|
|
483
|
+
}
|
|
484
|
+
if (node.op === '||') {
|
|
485
|
+
const l = evalNode(node.l, scope);
|
|
486
|
+
return l ? l : evalNode(node.r, scope);
|
|
487
|
+
}
|
|
488
|
+
if (node.op === '??') {
|
|
489
|
+
const l = evalNode(node.l, scope);
|
|
490
|
+
return l ?? evalNode(node.r, scope);
|
|
491
|
+
}
|
|
492
|
+
const l = evalNode(node.l, scope);
|
|
493
|
+
const r = evalNode(node.r, scope);
|
|
494
|
+
switch (node.op) {
|
|
495
|
+
case '===': return l === r;
|
|
496
|
+
case '!==': return l !== r;
|
|
497
|
+
case '==': return l == r;
|
|
498
|
+
case '!=': return l != r;
|
|
499
|
+
case '>': return l > r;
|
|
500
|
+
case '<': return l < r;
|
|
501
|
+
case '>=': return l >= r;
|
|
502
|
+
case '<=': return l <= r;
|
|
503
|
+
case '+':
|
|
504
|
+
return typeof l === 'string' || typeof r === 'string'
|
|
505
|
+
? String(l) + String(r)
|
|
506
|
+
: l + r;
|
|
507
|
+
case '-': return l - r;
|
|
508
|
+
case '*': return l * r;
|
|
509
|
+
case '/': return l / r;
|
|
510
|
+
case '%': return l % r;
|
|
511
|
+
}
|
|
512
|
+
throw new Error(`Unknown operator ${node.op}`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Evaluate one expression (the text between `{{` and `}}`) against a scope.
|
|
518
|
+
* Throws on a syntax error, an unknown helper or an unsupported method — the
|
|
519
|
+
* two public evaluators turn that into `{ success: false }`.
|
|
520
|
+
*/
|
|
521
|
+
export function evaluateWithScope(expression, scope, opts = {}) {
|
|
522
|
+
const trimmed = expression.trim();
|
|
523
|
+
if (!trimmed)
|
|
524
|
+
return undefined;
|
|
525
|
+
return evalNode(parseCached(trimmed, opts), scope);
|
|
526
|
+
}
|
|
527
|
+
// ============================================================
|
|
528
|
+
// EXPRESSION EVALUATOR IMPLEMENTATION
|
|
529
|
+
// ============================================================
|
|
530
|
+
/**
|
|
531
|
+
* Evaluate an expression with full context support
|
|
532
|
+
*/
|
|
533
|
+
export function evaluateExpression(expression, context) {
|
|
534
|
+
try {
|
|
535
|
+
const helpers = context.helpers;
|
|
536
|
+
const scope = {
|
|
537
|
+
lookup(name) {
|
|
538
|
+
if (name === 'data')
|
|
539
|
+
return context.data;
|
|
540
|
+
if (name === 'state')
|
|
541
|
+
return context.state;
|
|
542
|
+
if (name === 'params')
|
|
543
|
+
return context.params;
|
|
544
|
+
// A helper named bare (without a call) has always resolved to the
|
|
545
|
+
// function itself here; kept.
|
|
546
|
+
if (helpers && Object.prototype.hasOwnProperty.call(helpers, name))
|
|
547
|
+
return helpers[name];
|
|
548
|
+
// Shorthand access. Priority: params > state > data (most specific first)
|
|
549
|
+
if (name in context.params)
|
|
550
|
+
return context.params[name];
|
|
551
|
+
if (name in context.state)
|
|
552
|
+
return context.state[name];
|
|
553
|
+
if (name in context.data)
|
|
554
|
+
return context.data[name];
|
|
555
|
+
return undefined;
|
|
556
|
+
},
|
|
557
|
+
helper: (name) => (helpers && Object.prototype.hasOwnProperty.call(helpers, name) ? helpers[name] : undefined),
|
|
558
|
+
};
|
|
559
|
+
return { success: true, value: evaluateWithScope(expression, scope, { undefinedAsNull: true }) };
|
|
560
|
+
}
|
|
561
|
+
catch (e) {
|
|
562
|
+
return { success: false, error: `Expression error: ${e}` };
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// ============================================================
|
|
566
|
+
// DEPENDENCY EXTRACTION
|
|
567
|
+
// ============================================================
|
|
568
|
+
/**
|
|
569
|
+
* Extract dependencies from an expression for reactive updates
|
|
570
|
+
*/
|
|
571
|
+
export function extractDependencies(expression) {
|
|
572
|
+
const dependencies = [];
|
|
573
|
+
const seen = new Set();
|
|
574
|
+
// Match patterns like: data.foo.bar, state.count, params.id
|
|
575
|
+
// Also match shorthand: foo.bar (when referring to data/state/params root)
|
|
576
|
+
const patterns = [
|
|
577
|
+
/\bdata\.(\w+(?:\.\w+|\[\d+\])*)/g,
|
|
578
|
+
/\bstate\.(\w+(?:\.\w+|\[\d+\])*)/g,
|
|
579
|
+
/\bparams\.(\w+(?:\.\w+|\[\d+\])*)/g,
|
|
580
|
+
];
|
|
581
|
+
const sources = ['data', 'state', 'params'];
|
|
582
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
583
|
+
const pattern = patterns[i];
|
|
584
|
+
const source = sources[i];
|
|
585
|
+
if (!pattern || !source)
|
|
586
|
+
continue;
|
|
587
|
+
let match;
|
|
588
|
+
while ((match = pattern.exec(expression)) !== null) {
|
|
589
|
+
const path = match[1];
|
|
590
|
+
if (path === undefined)
|
|
591
|
+
continue;
|
|
592
|
+
const key = `${source}.${path}`;
|
|
593
|
+
if (!seen.has(key)) {
|
|
594
|
+
seen.add(key);
|
|
595
|
+
dependencies.push({ source, path });
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return dependencies;
|
|
600
|
+
}
|
|
601
|
+
// ============================================================
|
|
602
|
+
// INTERPOLATION
|
|
603
|
+
// ============================================================
|
|
604
|
+
/**
|
|
605
|
+
* Check if a value contains expression syntax
|
|
606
|
+
*/
|
|
607
|
+
export function isExpression(value) {
|
|
608
|
+
if (typeof value !== 'string')
|
|
609
|
+
return false;
|
|
610
|
+
return /\{\{.+?\}\}/.test(value);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Interpolate expressions within a template string
|
|
614
|
+
*
|
|
615
|
+
* @example
|
|
616
|
+
* interpolate('Hello, {{user.name}}!', context) // 'Hello, John!'
|
|
617
|
+
* interpolate('Total: {{price * quantity}}', context) // 'Total: 100'
|
|
618
|
+
*/
|
|
619
|
+
export function interpolate(template, context) {
|
|
620
|
+
const pattern = /\{\{(.+?)\}\}/g;
|
|
621
|
+
return template.replace(pattern, (_, expr) => {
|
|
622
|
+
const result = evaluateExpression(expr.trim(), context);
|
|
623
|
+
if (result.success) {
|
|
624
|
+
return String(result.value ?? '');
|
|
625
|
+
}
|
|
626
|
+
return '';
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
// ============================================================
|
|
630
|
+
// DEFAULT HELPERS
|
|
631
|
+
// ============================================================
|
|
632
|
+
/**
|
|
633
|
+
* Default helper functions for expressions
|
|
634
|
+
*/
|
|
635
|
+
export const DEFAULT_BINDING_HELPERS = {
|
|
636
|
+
// String helpers
|
|
637
|
+
upper: (s) => String(s).toUpperCase(),
|
|
638
|
+
lower: (s) => String(s).toLowerCase(),
|
|
639
|
+
trim: (s) => String(s).trim(),
|
|
640
|
+
capitalize: (s) => {
|
|
641
|
+
const str = String(s);
|
|
642
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
643
|
+
},
|
|
644
|
+
concat: (...args) => args.map(String).join(''),
|
|
645
|
+
// Number helpers
|
|
646
|
+
round: (n, decimals) => {
|
|
647
|
+
const num = Number(n);
|
|
648
|
+
const d = Number(decimals ?? 0);
|
|
649
|
+
return Math.round(num * Math.pow(10, d)) / Math.pow(10, d);
|
|
650
|
+
},
|
|
651
|
+
floor: (n) => Math.floor(Number(n)),
|
|
652
|
+
ceil: (n) => Math.ceil(Number(n)),
|
|
653
|
+
abs: (n) => Math.abs(Number(n)),
|
|
654
|
+
min: (...args) => Math.min(...args.map(Number)),
|
|
655
|
+
max: (...args) => Math.max(...args.map(Number)),
|
|
656
|
+
/**
|
|
657
|
+
* How many pages `total` rows fill at `size` per page — `ceil(total / size)`,
|
|
658
|
+
* but SAFE: a missing, non-numeric, zero or negative total or size gives 0,
|
|
659
|
+
* never `NaN` or `Infinity` (the envelope is absent until the first fetch
|
|
660
|
+
* lands). Pair with `__meta.<source>.total` on a `returns` source:
|
|
661
|
+
* `{{pages(__meta.products.total, 20)}}`. Wrap in `max(1, …)` for a pager
|
|
662
|
+
* that should read "1 / 1" on an empty list.
|
|
663
|
+
*/
|
|
664
|
+
pages: (total, size) => {
|
|
665
|
+
const t = Number(total);
|
|
666
|
+
const s = Number(size);
|
|
667
|
+
if (!Number.isFinite(t) || !Number.isFinite(s) || t <= 0 || s <= 0)
|
|
668
|
+
return 0;
|
|
669
|
+
return Math.ceil(t / s);
|
|
670
|
+
},
|
|
671
|
+
// Aggregate helpers — one data source, several KPI figures. Without these a
|
|
672
|
+
// KPI strip needed one source (one fetch) per number. Filtering stays the
|
|
673
|
+
// data layer's job (EntityQuery.filter); these only fold what is already
|
|
674
|
+
// fetched.
|
|
675
|
+
sumBy: (items, field) => {
|
|
676
|
+
if (!Array.isArray(items))
|
|
677
|
+
return 0;
|
|
678
|
+
const f = field === undefined ? undefined : String(field);
|
|
679
|
+
return items.reduce((acc, it) => {
|
|
680
|
+
const v = f === undefined ? it : it?.[f];
|
|
681
|
+
const n = Number(v);
|
|
682
|
+
return Number.isFinite(n) ? acc + n : acc;
|
|
683
|
+
}, 0);
|
|
684
|
+
},
|
|
685
|
+
groupCount: (items, field) => {
|
|
686
|
+
const out = {};
|
|
687
|
+
if (!Array.isArray(items))
|
|
688
|
+
return out;
|
|
689
|
+
const f = String(field);
|
|
690
|
+
for (const it of items) {
|
|
691
|
+
const v = it?.[f];
|
|
692
|
+
const key = v === null || v === undefined || v === '' ? 'unknown' : String(v);
|
|
693
|
+
out[key] = (out[key] ?? 0) + 1;
|
|
694
|
+
}
|
|
695
|
+
return out;
|
|
696
|
+
},
|
|
697
|
+
// Array helpers
|
|
698
|
+
length: (arr) => (Array.isArray(arr) ? arr.length : String(arr).length),
|
|
699
|
+
first: (arr) => (Array.isArray(arr) ? arr[0] : undefined),
|
|
700
|
+
last: (arr) => (Array.isArray(arr) ? arr[arr.length - 1] : undefined),
|
|
701
|
+
join: (arr, sep) => Array.isArray(arr) ? arr.join(String(sep ?? ',')) : String(arr),
|
|
702
|
+
includes: (arr, item) => Array.isArray(arr) ? arr.includes(item) : String(arr).includes(String(item)),
|
|
703
|
+
slice: (arr, start, end) => {
|
|
704
|
+
if (Array.isArray(arr)) {
|
|
705
|
+
return arr.slice(start !== undefined ? Number(start) : undefined, end !== undefined ? Number(end) : undefined);
|
|
706
|
+
}
|
|
707
|
+
return String(arr).slice(start !== undefined ? Number(start) : undefined, end !== undefined ? Number(end) : undefined);
|
|
708
|
+
},
|
|
709
|
+
// Type checks
|
|
710
|
+
isEmpty: (v) => v === null ||
|
|
711
|
+
v === undefined ||
|
|
712
|
+
v === '' ||
|
|
713
|
+
(Array.isArray(v) && v.length === 0) ||
|
|
714
|
+
(typeof v === 'object' && Object.keys(v).length === 0),
|
|
715
|
+
isNotEmpty: (v) => v !== null && v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0),
|
|
716
|
+
isArray: (v) => Array.isArray(v),
|
|
717
|
+
isString: (v) => typeof v === 'string',
|
|
718
|
+
isNumber: (v) => typeof v === 'number' && !isNaN(v),
|
|
719
|
+
isBoolean: (v) => typeof v === 'boolean',
|
|
720
|
+
isNull: (v) => v === null || v === undefined,
|
|
721
|
+
// Object helpers
|
|
722
|
+
keys: (obj) => (typeof obj === 'object' && obj !== null ? Object.keys(obj) : []),
|
|
723
|
+
values: (obj) => (typeof obj === 'object' && obj !== null ? Object.values(obj) : []),
|
|
724
|
+
// Formatting helpers
|
|
725
|
+
json: (v) => JSON.stringify(v),
|
|
726
|
+
/**
|
|
727
|
+
* Today's date in the visitor's time zone, as `YYYY-MM-DD`.
|
|
728
|
+
* The shape date fields are stored in, so it compares as a string:
|
|
729
|
+
* `{{event.date >= today()}}` for "upcoming", `today(-7)` for a week ago.
|
|
730
|
+
* The optional argument shifts by whole days. Before this an "upcoming"
|
|
731
|
+
* filter had nothing to compare against and a page hard-coded a date.
|
|
732
|
+
*/
|
|
733
|
+
today: (offsetDays) => {
|
|
734
|
+
const d = new Date();
|
|
735
|
+
const shift = Number(offsetDays ?? 0);
|
|
736
|
+
if (Number.isFinite(shift) && shift !== 0)
|
|
737
|
+
d.setDate(d.getDate() + Math.trunc(shift));
|
|
738
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
739
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
740
|
+
return `${d.getFullYear()}-${mm}-${dd}`;
|
|
741
|
+
},
|
|
742
|
+
date: (v) => new Date(v).toLocaleDateString(),
|
|
743
|
+
time: (v) => new Date(v).toLocaleTimeString(),
|
|
744
|
+
datetime: (v) => new Date(v).toLocaleString(),
|
|
745
|
+
currency: (v, currency, locale) => {
|
|
746
|
+
const num = Number(v);
|
|
747
|
+
return new Intl.NumberFormat(String(locale ?? 'en-US'), {
|
|
748
|
+
style: 'currency',
|
|
749
|
+
currency: String(currency ?? 'USD'),
|
|
750
|
+
}).format(num);
|
|
751
|
+
},
|
|
752
|
+
percent: (v, decimals) => {
|
|
753
|
+
const num = Number(v);
|
|
754
|
+
const d = Number(decimals ?? 0);
|
|
755
|
+
return `${(num * 100).toFixed(d)}%`;
|
|
756
|
+
},
|
|
757
|
+
};
|
|
758
|
+
// ============================================================
|
|
759
|
+
// EXPRESSION EVALUATOR FACTORY
|
|
760
|
+
// ============================================================
|
|
761
|
+
/**
|
|
762
|
+
* Create an expression evaluator with context and helpers
|
|
763
|
+
*/
|
|
764
|
+
export function createExpressionEvaluator(additionalHelpers) {
|
|
765
|
+
const helpers = { ...DEFAULT_BINDING_HELPERS, ...additionalHelpers };
|
|
766
|
+
return {
|
|
767
|
+
evaluate(expression, context) {
|
|
768
|
+
const result = evaluateExpression(expression, {
|
|
769
|
+
...context,
|
|
770
|
+
helpers: { ...helpers, ...context.helpers },
|
|
771
|
+
});
|
|
772
|
+
if (result.success) {
|
|
773
|
+
return result.value;
|
|
774
|
+
}
|
|
775
|
+
throw new Error(result.error);
|
|
776
|
+
},
|
|
777
|
+
isExpression,
|
|
778
|
+
extractDependencies,
|
|
779
|
+
interpolate(template, context) {
|
|
780
|
+
return interpolate(template, {
|
|
781
|
+
...context,
|
|
782
|
+
helpers: { ...helpers, ...context.helpers },
|
|
783
|
+
});
|
|
784
|
+
},
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
//# sourceMappingURL=expression.js.map
|