@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,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* build an engine from its DECLARATION.
|
|
3
|
+
*
|
|
4
|
+
* A REST engine is not a class. A class has a call signature, and a
|
|
5
|
+
* hand-written engine that takes one options object where the runtime passes
|
|
6
|
+
* two positional arguments ends up asking SharePoint for `lists/undefined`.
|
|
7
|
+
* A declaration has no signature to get wrong.
|
|
8
|
+
*
|
|
9
|
+
* WHAT THIS MODULE IS ALLOWED TO KNOW: how to turn seven declared facts into
|
|
10
|
+
* an `HttpEntityConnector`. It must not know a vendor's name, and the
|
|
11
|
+
* placement test measures exactly that — no provider name anywhere in
|
|
12
|
+
* `packages/runtime`. Everything SharePoint-shaped lives in the declaration,
|
|
13
|
+
* which lives in the pack that ships the connector.
|
|
14
|
+
*
|
|
15
|
+
* WHAT THE BASE DOES NOT LEARN. `entityPathMapper` and `responseMapper` stay
|
|
16
|
+
* plain functions on `HttpConnectorConfig`; this builds them. The pre-flight —
|
|
17
|
+
* Graph cannot address a list before its site id is resolved — is handled by
|
|
18
|
+
* constructing ONE base per resolved binding, so the mapper stays synchronous.
|
|
19
|
+
* Making it asynchronous would teach a shared contract one vendor's
|
|
20
|
+
* addressing dance.
|
|
21
|
+
*
|
|
22
|
+
* @module @pwfabric/runtime/connectors/declared-connector
|
|
23
|
+
*/
|
|
24
|
+
import { HttpEntityConnector } from '../entity/http-connector.js';
|
|
25
|
+
import { applyEntityQuery } from '../entity/apply-query.js';
|
|
26
|
+
// ============================================================
|
|
27
|
+
// VALUE TRANSFORMS — a CLOSED registry
|
|
28
|
+
// ============================================================
|
|
29
|
+
/**
|
|
30
|
+
* The only value transforms a declaration may name.
|
|
31
|
+
*
|
|
32
|
+
* CLOSED ON PURPOSE. A declaration carries a NAME, never a function — the
|
|
33
|
+
* moment it could carry a function it would be a class again with extra steps,
|
|
34
|
+
* and an AI-written declaration would regain the power to get something wrong.
|
|
35
|
+
* An unknown name throws at construction (see `resolveTransform`) rather than
|
|
36
|
+
* passing values through unchanged, because a silently skipped transform looks
|
|
37
|
+
* exactly like a field the source never sent.
|
|
38
|
+
*
|
|
39
|
+
* These two are named for what they DO, not for who needs them: the runtime
|
|
40
|
+
* must not learn a vendor's name.
|
|
41
|
+
*/
|
|
42
|
+
export const VALUE_TRANSFORMS = {
|
|
43
|
+
/**
|
|
44
|
+
* A field whose value is a JSON STRING describing a hosted file becomes the
|
|
45
|
+
* absolute URL. Image/Thumbnail-typed columns serialise this way and a
|
|
46
|
+
* consumer binding it into an `image` block needs a real URL.
|
|
47
|
+
*/
|
|
48
|
+
'hosted-file-url': (value) => {
|
|
49
|
+
if (typeof value !== 'string' || !value.startsWith('{'))
|
|
50
|
+
return value;
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(value);
|
|
53
|
+
return parsed.serverUrl && parsed.serverRelativeUrl
|
|
54
|
+
? `${parsed.serverUrl}${parsed.serverRelativeUrl}`
|
|
55
|
+
: value;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* A numeric string becomes a number. Form data yields strings only, while
|
|
63
|
+
* lookup-style columns are typed numeric by the API.
|
|
64
|
+
*/
|
|
65
|
+
'numeric-string': (value) => typeof value === 'string' && value !== '' && !Number.isNaN(Number(value)) ? Number(value) : value,
|
|
66
|
+
};
|
|
67
|
+
function resolveTransform(use) {
|
|
68
|
+
const fn = VALUE_TRANSFORMS[use];
|
|
69
|
+
if (!fn) {
|
|
70
|
+
throw new Error(`engine declaration names an unknown transform "${use}" — known: ${Object.keys(VALUE_TRANSFORMS).join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
return fn;
|
|
73
|
+
}
|
|
74
|
+
// ============================================================
|
|
75
|
+
// TEMPLATE
|
|
76
|
+
// ============================================================
|
|
77
|
+
/** `{name}` substitution. An unbound placeholder is left visible, not blanked. */
|
|
78
|
+
function fill(template, bindings) {
|
|
79
|
+
return template.replace(/\{(\w+)\}/g, (whole, key) => bindings[key] ?? whole);
|
|
80
|
+
}
|
|
81
|
+
function readPath(source, path) {
|
|
82
|
+
return path.split('.').reduce((acc, key) => {
|
|
83
|
+
if (acc && typeof acc === 'object')
|
|
84
|
+
return acc[key];
|
|
85
|
+
return undefined;
|
|
86
|
+
}, source);
|
|
87
|
+
}
|
|
88
|
+
// ============================================================
|
|
89
|
+
// THE ORIGIN — https, except an explicit http:// on loopback
|
|
90
|
+
// ============================================================
|
|
91
|
+
/** Hosts a declaration may reach over plain `http://`: the machine itself. */
|
|
92
|
+
function isLoopbackHost(hostname) {
|
|
93
|
+
const h = hostname.toLowerCase();
|
|
94
|
+
return h === 'localhost' || h === '127.0.0.1' || h === '[::1]' || h.endsWith('.localhost');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The origin a filled declared host resolves to.
|
|
98
|
+
*
|
|
99
|
+
* - no scheme or `https://` → `https://<host>` (unchanged since the first release);
|
|
100
|
+
* - `http://` on a loopback host (`localhost`, `127.0.0.1`, `[::1]`,
|
|
101
|
+
* `*.localhost`) → kept as `http://<host>`, so a development server is reachable;
|
|
102
|
+
* - `http://` anywhere else → `https://<host>`, as every release before
|
|
103
|
+
* this one did. It is never downgraded (the visitor's token must not cross
|
|
104
|
+
* the network in clear text), and published apps whose connection holds an
|
|
105
|
+
* `http://` address keep working the way they always have.
|
|
106
|
+
*/
|
|
107
|
+
function declaredOrigin(filledHost) {
|
|
108
|
+
const plain = /^http:\/\//i.exec(filledHost);
|
|
109
|
+
if (!plain)
|
|
110
|
+
return { origin: `https://${filledHost.replace(/^https?:\/\//i, '')}` };
|
|
111
|
+
const rest = filledHost.slice(plain[0].length);
|
|
112
|
+
let hostname = '';
|
|
113
|
+
try {
|
|
114
|
+
hostname = new URL(`http://${rest}`).hostname;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
hostname = '';
|
|
118
|
+
}
|
|
119
|
+
if (hostname && isLoopbackHost(hostname))
|
|
120
|
+
return { origin: `http://${rest}` };
|
|
121
|
+
return { origin: `https://${rest}` };
|
|
122
|
+
}
|
|
123
|
+
export function createDeclaredConnector(options) {
|
|
124
|
+
const { name, declaration: d, config, getAccessToken } = options;
|
|
125
|
+
// Fail at construction, not at the first read: a declaration naming a
|
|
126
|
+
// transform nobody implements is broken whether or not anyone calls it.
|
|
127
|
+
const transforms = (d.transforms ?? []).map((t) => ({ field: t.field, fn: resolveTransform(t.use) }));
|
|
128
|
+
const stringConfig = {};
|
|
129
|
+
for (const [k, v] of Object.entries(config)) {
|
|
130
|
+
if (typeof v === 'string')
|
|
131
|
+
stringConfig[k] = v;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* THE HOST IS A TEMPLATE — the one substitution a user-owned API cannot do
|
|
135
|
+
* without, and the one this function used to compute before the config it
|
|
136
|
+
* would have drawn on even existed.
|
|
137
|
+
*
|
|
138
|
+
* `graph.microsoft.com/v1.0` is a constant because Graph is one address for
|
|
139
|
+
* everyone. A connection to the user's OWN API has no constant to state: the
|
|
140
|
+
* address IS the thing the user supplies. `host: "{baseUrl}"` is how they
|
|
141
|
+
* supply it, and the scheme is stripped after filling so a pasted
|
|
142
|
+
* `https://api.example.com` and a bare `api.example.com` mean the same thing.
|
|
143
|
+
*
|
|
144
|
+
* An explicit `http://` survives ONLY for a loopback host — a development
|
|
145
|
+
* server on `http://127.0.0.1:8787` is otherwise unreachable. Anywhere else
|
|
146
|
+
* `http://` is upgraded to https, as before: see {@link declaredOrigin}.
|
|
147
|
+
*/
|
|
148
|
+
const declared = declaredOrigin(fill(d.host, stringConfig));
|
|
149
|
+
const origin = declared.origin;
|
|
150
|
+
/**
|
|
151
|
+
* THE READ PATHS ARE TEMPLATES TOO — and that is what makes a generic REST
|
|
152
|
+
* connection declarable rather than compiled.
|
|
153
|
+
*
|
|
154
|
+
* If `fill` reached only `resolve.path` and `path`, the RESPONSE SHAPE would
|
|
155
|
+
* have to be hardcoded into the declaration: an API answering `{data:[…]}`
|
|
156
|
+
* and one answering `{results:[…]}` would need two different atoms. Two
|
|
157
|
+
* atoms that differ only in a string is code wearing a manifest, and the
|
|
158
|
+
* declared-engine design's whole claim is that "a new API is a connection
|
|
159
|
+
* record rather than code".
|
|
160
|
+
*
|
|
161
|
+
* `fill` on a string carrying no `{name}` returns that string, so a
|
|
162
|
+
* declaration without templates behaves as a literal one. `graph-lists`
|
|
163
|
+
* is the proof and has a test that says so.
|
|
164
|
+
*/
|
|
165
|
+
const readItems = fill(d.read.items, stringConfig);
|
|
166
|
+
const readId = fill(d.read.id, stringConfig);
|
|
167
|
+
const readFlatten = d.read.flatten === undefined ? undefined : fill(d.read.flatten, stringConfig);
|
|
168
|
+
const authHeaders = async () => {
|
|
169
|
+
if (d.auth.type !== 'bearer')
|
|
170
|
+
return {};
|
|
171
|
+
const token = await getAccessToken();
|
|
172
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
173
|
+
};
|
|
174
|
+
/** Pre-flight, memoised per template result — the site id and its kin. */
|
|
175
|
+
const bindings = new Map();
|
|
176
|
+
function resolveBindings(extra) {
|
|
177
|
+
const base = { ...stringConfig, ...extra };
|
|
178
|
+
if (!d.resolve)
|
|
179
|
+
return Promise.resolve(base);
|
|
180
|
+
const url = origin + fill(d.resolve.path, base);
|
|
181
|
+
let pending = bindings.get(url);
|
|
182
|
+
if (!pending) {
|
|
183
|
+
pending = authHeaders()
|
|
184
|
+
.then((headers) => fetch(url, { headers }))
|
|
185
|
+
.then((res) => res.json())
|
|
186
|
+
.then((body) => {
|
|
187
|
+
const value = readPath(body, d.resolve.field);
|
|
188
|
+
if (typeof value !== 'string') {
|
|
189
|
+
throw new Error(`engine pre-flight ${url} carried no "${d.resolve.field}"`);
|
|
190
|
+
}
|
|
191
|
+
return { ...base, [d.resolve.as]: value };
|
|
192
|
+
})
|
|
193
|
+
.catch((err) => {
|
|
194
|
+
bindings.delete(url);
|
|
195
|
+
throw err;
|
|
196
|
+
});
|
|
197
|
+
bindings.set(url, pending);
|
|
198
|
+
}
|
|
199
|
+
return pending;
|
|
200
|
+
}
|
|
201
|
+
function applyTransforms(row) {
|
|
202
|
+
if (transforms.length === 0)
|
|
203
|
+
return row;
|
|
204
|
+
const out = { ...row };
|
|
205
|
+
for (const { field, fn } of transforms) {
|
|
206
|
+
if (field in out)
|
|
207
|
+
out[field] = fn(out[field]);
|
|
208
|
+
}
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
function mapRow(raw) {
|
|
212
|
+
const item = (raw ?? {});
|
|
213
|
+
const row = { id: readPath(item, readId) };
|
|
214
|
+
for (const key of d.read.lift ?? []) {
|
|
215
|
+
const v = readPath(item, key);
|
|
216
|
+
if (v !== undefined)
|
|
217
|
+
row[key] = v;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* `flatten: "*"` means THE ITEM ITSELF IS THE BAG.
|
|
221
|
+
*
|
|
222
|
+
* `mapRow` starts from `{ id }` and only grows when `flatten` names a
|
|
223
|
+
* nested bag. Graph's bag is `fields`; flat REST has no bag because the
|
|
224
|
+
* item is already the row — so without this a plain REST read produced
|
|
225
|
+
* rows carrying nothing but `id`, and the panels rendered empty for a
|
|
226
|
+
* reason no warning named.
|
|
227
|
+
*
|
|
228
|
+
* `"*"` is spelled out rather than inferred from an absent `flatten`:
|
|
229
|
+
* inferring it would silently change what every existing declaration
|
|
230
|
+
* means, and a fallback nobody declared is how this family of bugs got
|
|
231
|
+
* here. It is also not a valid `readPath`, so it can never collide with a
|
|
232
|
+
* real path. The schema is untouched — it already accepts any non-empty
|
|
233
|
+
* string, so contracts stay frozen.
|
|
234
|
+
*/
|
|
235
|
+
const bag = readFlatten === '*' ? item : readFlatten ? readPath(item, readFlatten) : undefined;
|
|
236
|
+
if (bag && typeof bag === 'object')
|
|
237
|
+
Object.assign(row, bag);
|
|
238
|
+
return applyTransforms(row);
|
|
239
|
+
}
|
|
240
|
+
/** One base per resolved binding set — the site is already inside baseUrl. */
|
|
241
|
+
const bases = new Map();
|
|
242
|
+
function baseFor(extra) {
|
|
243
|
+
const key = JSON.stringify(extra);
|
|
244
|
+
let pending = bases.get(key);
|
|
245
|
+
if (!pending) {
|
|
246
|
+
pending = resolveBindings(extra)
|
|
247
|
+
.then((bound) => new HttpEntityConnector(name, {
|
|
248
|
+
baseUrl: origin,
|
|
249
|
+
// Synchronous: every binding is already resolved into the string.
|
|
250
|
+
entityPathMapper: (entityType) => fill(d.path, { ...bound, entityType: String(entityType) }),
|
|
251
|
+
...(d.params ? { defaultQueryParams: d.params } : {}),
|
|
252
|
+
// `none` means no credential at all: the visitor's token must never reach
|
|
253
|
+
// a host that did not ask for it, so no auth config is passed.
|
|
254
|
+
...(d.auth.type === 'none' ? {} : { auth: { type: d.auth.type, token: getAccessToken } }),
|
|
255
|
+
responseMapper: (raw) => {
|
|
256
|
+
const items = readPath(raw, readItems);
|
|
257
|
+
return { items: Array.isArray(items) ? items.map(mapRow) : [] };
|
|
258
|
+
},
|
|
259
|
+
}))
|
|
260
|
+
.catch((err) => {
|
|
261
|
+
bases.delete(key);
|
|
262
|
+
throw err;
|
|
263
|
+
});
|
|
264
|
+
bases.set(key, pending);
|
|
265
|
+
}
|
|
266
|
+
return pending;
|
|
267
|
+
}
|
|
268
|
+
/** A query may name its own binding overrides (e.g. a different site). */
|
|
269
|
+
function overridesFrom(query) {
|
|
270
|
+
const out = {};
|
|
271
|
+
for (const [k, v] of Object.entries((query ?? {}))) {
|
|
272
|
+
if (typeof v === 'string' && k in stringConfig)
|
|
273
|
+
out[k] = v;
|
|
274
|
+
}
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
name,
|
|
279
|
+
type: 'custom',
|
|
280
|
+
async query(entityType, query) {
|
|
281
|
+
try {
|
|
282
|
+
const base = await baseFor(overridesFrom(query));
|
|
283
|
+
// A declared engine talks to a FOREIGN API. The base connector would
|
|
284
|
+
// serialise the surface's query as the platform's own wire params —
|
|
285
|
+
// `filter=<json>&sort=&limit=&offset=` — which mean nothing there:
|
|
286
|
+
// Microsoft Graph answers 400 to any list read carrying `filter=`
|
|
287
|
+
// (sort-only reads happen to pass, so half the pages would be live and
|
|
288
|
+
// half fallback). Fetch with the declaration's own params only and apply the query
|
|
289
|
+
// here, where every declared engine speaks the same query language.
|
|
290
|
+
const res = await base.query(entityType, {});
|
|
291
|
+
if (!res.success)
|
|
292
|
+
return res;
|
|
293
|
+
return { success: true, result: applyEntityQuery(res.result.items, query) };
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
return { success: false, error: err instanceof Error ? err.message : 'engine query failed' };
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
async read(entityType, instanceId) {
|
|
300
|
+
const result = await this.query(entityType, {});
|
|
301
|
+
if (!result.success)
|
|
302
|
+
return { success: false, error: result.error };
|
|
303
|
+
const match = result.result.items.find((i) => i.id === instanceId);
|
|
304
|
+
return match ? { success: true, data: match } : { success: false, error: 'Not found' };
|
|
305
|
+
},
|
|
306
|
+
async create(entityType, data) {
|
|
307
|
+
try {
|
|
308
|
+
const base = await baseFor({});
|
|
309
|
+
// The wrap belongs to the LAYER: the base posts whatever it is handed,
|
|
310
|
+
// and the declaration says what shape to hand it.
|
|
311
|
+
const body = d.write?.bodyWrap ? { [d.write.bodyWrap]: applyTransforms(data) } : applyTransforms(data);
|
|
312
|
+
const result = await base.create(entityType, body);
|
|
313
|
+
return result.success ? { success: true, data: mapRow(result.data) } : result;
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
return { success: false, error: err instanceof Error ? err.message : 'engine create failed' };
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
async update() {
|
|
320
|
+
return { success: false, error: 'declared engines do not support update yet' };
|
|
321
|
+
},
|
|
322
|
+
async delete(entityType, instanceId) {
|
|
323
|
+
try {
|
|
324
|
+
const base = await baseFor({});
|
|
325
|
+
return await base.delete(entityType, instanceId);
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
return { success: false, error: err instanceof Error ? err.message : 'engine delete failed' };
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=declared-connector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declared-connector.js","sourceRoot":"","sources":["../../src/connectors/declared-connector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * build an engine from its DECLARATION.\n *\n * A REST engine is not a class. A class has a call signature, and a\n * hand-written engine that takes one options object where the runtime passes\n * two positional arguments ends up asking SharePoint for `lists/undefined`.\n * A declaration has no signature to get wrong.\n *\n * WHAT THIS MODULE IS ALLOWED TO KNOW: how to turn seven declared facts into\n * an `HttpEntityConnector`. It must not know a vendor's name, and the\n * placement test measures exactly that — no provider name anywhere in\n * `packages/runtime`. Everything SharePoint-shaped lives in the declaration,\n * which lives in the pack that ships the connector.\n *\n * WHAT THE BASE DOES NOT LEARN. `entityPathMapper` and `responseMapper` stay\n * plain functions on `HttpConnectorConfig`; this builds them. The pre-flight —\n * Graph cannot address a list before its site id is resolved — is handled by\n * constructing ONE base per resolved binding, so the mapper stays synchronous.\n * Making it asynchronous would teach a shared contract one vendor's\n * addressing dance.\n *\n * @module @pwfabric/runtime/connectors/declared-connector\n */\n\nimport type { EngineDeclaration } from '@pwfabric/contracts'\n\nimport { HttpEntityConnector } from '../entity/http-connector.js'\nimport type {\n ConnectorQueryResult,\n ConnectorResult,\n EntityConnector,\n} from '../entity/connector.js'\nimport type { EntityInstanceId, EntityQuery, EntityTypeId } from '../entity/types.js'\nimport { applyEntityQuery } from '../entity/apply-query.js'\n\n// ============================================================\n// VALUE TRANSFORMS — a CLOSED registry\n// ============================================================\n\n/**\n * The only value transforms a declaration may name.\n *\n * CLOSED ON PURPOSE. A declaration carries a NAME, never a function — the\n * moment it could carry a function it would be a class again with extra steps,\n * and an AI-written declaration would regain the power to get something wrong.\n * An unknown name throws at construction (see `resolveTransform`) rather than\n * passing values through unchanged, because a silently skipped transform looks\n * exactly like a field the source never sent.\n *\n * These two are named for what they DO, not for who needs them: the runtime\n * must not learn a vendor's name.\n */\nexport const VALUE_TRANSFORMS: Readonly<Record<string, (value: unknown) => unknown>> = {\n /**\n * A field whose value is a JSON STRING describing a hosted file becomes the\n * absolute URL. Image/Thumbnail-typed columns serialise this way and a\n * consumer binding it into an `image` block needs a real URL.\n */\n 'hosted-file-url': (value) => {\n if (typeof value !== 'string' || !value.startsWith('{')) return value\n try {\n const parsed = JSON.parse(value) as { serverUrl?: string; serverRelativeUrl?: string }\n return parsed.serverUrl && parsed.serverRelativeUrl\n ? `${parsed.serverUrl}${parsed.serverRelativeUrl}`\n : value\n } catch {\n return value\n }\n },\n /**\n * A numeric string becomes a number. Form data yields strings only, while\n * lookup-style columns are typed numeric by the API.\n */\n 'numeric-string': (value) =>\n typeof value === 'string' && value !== '' && !Number.isNaN(Number(value)) ? Number(value) : value,\n}\n\nfunction resolveTransform(use: string): (value: unknown) => unknown {\n const fn = VALUE_TRANSFORMS[use]\n if (!fn) {\n throw new Error(\n `engine declaration names an unknown transform \"${use}\" — known: ${Object.keys(VALUE_TRANSFORMS).join(', ')}`\n )\n }\n return fn\n}\n\n// ============================================================\n// TEMPLATE\n// ============================================================\n\n/** `{name}` substitution. An unbound placeholder is left visible, not blanked. */\nfunction fill(template: string, bindings: Readonly<Record<string, string>>): string {\n return template.replace(/\\{(\\w+)\\}/g, (whole, key: string) => bindings[key] ?? whole)\n}\n\nfunction readPath(source: unknown, path: string): unknown {\n return path.split('.').reduce<unknown>((acc, key) => {\n if (acc && typeof acc === 'object') return (acc as Record<string, unknown>)[key]\n return undefined\n }, source)\n}\n\n// ============================================================\n// THE ORIGIN — https, except an explicit http:// on loopback\n// ============================================================\n\n/** Hosts a declaration may reach over plain `http://`: the machine itself. */\nfunction isLoopbackHost(hostname: string): boolean {\n const h = hostname.toLowerCase()\n return h === 'localhost' || h === '127.0.0.1' || h === '[::1]' || h.endsWith('.localhost')\n}\n\n/**\n * The origin a filled declared host resolves to.\n *\n * - no scheme or `https://` → `https://<host>` (unchanged since the first release);\n * - `http://` on a loopback host (`localhost`, `127.0.0.1`, `[::1]`,\n * `*.localhost`) → kept as `http://<host>`, so a development server is reachable;\n * - `http://` anywhere else → `https://<host>`, as every release before\n * this one did. It is never downgraded (the visitor's token must not cross\n * the network in clear text), and published apps whose connection holds an\n * `http://` address keep working the way they always have.\n */\nfunction declaredOrigin(filledHost: string): { readonly origin: string } {\n const plain = /^http:\\/\\//i.exec(filledHost)\n if (!plain) return { origin: `https://${filledHost.replace(/^https?:\\/\\//i, '')}` }\n const rest = filledHost.slice(plain[0].length)\n let hostname = ''\n try {\n hostname = new URL(`http://${rest}`).hostname\n } catch {\n hostname = ''\n }\n if (hostname && isLoopbackHost(hostname)) return { origin: `http://${rest}` }\n return { origin: `https://${rest}` }\n}\n\n// ============================================================\n// THE ENGINE\n// ============================================================\n\nexport interface DeclaredConnectorOptions {\n /** Connection name — the engine reports it, and the registry keys on it. */\n readonly name: string\n readonly declaration: EngineDeclaration\n /** Per-connection config the declaration's templates draw on. */\n readonly config: Readonly<Record<string, unknown>>\n readonly getAccessToken: () => Promise<string | null>\n}\n\nexport function createDeclaredConnector(options: DeclaredConnectorOptions): EntityConnector {\n const { name, declaration: d, config, getAccessToken } = options\n\n // Fail at construction, not at the first read: a declaration naming a\n // transform nobody implements is broken whether or not anyone calls it.\n const transforms = (d.transforms ?? []).map((t) => ({ field: t.field, fn: resolveTransform(t.use) }))\n\n const stringConfig: Record<string, string> = {}\n for (const [k, v] of Object.entries(config)) {\n if (typeof v === 'string') stringConfig[k] = v\n }\n\n /**\n * THE HOST IS A TEMPLATE — the one substitution a user-owned API cannot do\n * without, and the one this function used to compute before the config it\n * would have drawn on even existed.\n *\n * `graph.microsoft.com/v1.0` is a constant because Graph is one address for\n * everyone. A connection to the user's OWN API has no constant to state: the\n * address IS the thing the user supplies. `host: \"{baseUrl}\"` is how they\n * supply it, and the scheme is stripped after filling so a pasted\n * `https://api.example.com` and a bare `api.example.com` mean the same thing.\n *\n * An explicit `http://` survives ONLY for a loopback host — a development\n * server on `http://127.0.0.1:8787` is otherwise unreachable. Anywhere else\n * `http://` is upgraded to https, as before: see {@link declaredOrigin}.\n */\n const declared = declaredOrigin(fill(d.host, stringConfig))\n const origin = declared.origin\n\n /**\n * THE READ PATHS ARE TEMPLATES TOO — and that is what makes a generic REST\n * connection declarable rather than compiled.\n *\n * If `fill` reached only `resolve.path` and `path`, the RESPONSE SHAPE would\n * have to be hardcoded into the declaration: an API answering `{data:[…]}`\n * and one answering `{results:[…]}` would need two different atoms. Two\n * atoms that differ only in a string is code wearing a manifest, and the\n * declared-engine design's whole claim is that \"a new API is a connection\n * record rather than code\".\n *\n * `fill` on a string carrying no `{name}` returns that string, so a\n * declaration without templates behaves as a literal one. `graph-lists`\n * is the proof and has a test that says so.\n */\n const readItems = fill(d.read.items, stringConfig)\n const readId = fill(d.read.id, stringConfig)\n const readFlatten = d.read.flatten === undefined ? undefined : fill(d.read.flatten, stringConfig)\n\n const authHeaders = async (): Promise<Record<string, string>> => {\n if (d.auth.type !== 'bearer') return {}\n const token = await getAccessToken()\n return token ? { Authorization: `Bearer ${token}` } : {}\n }\n\n /** Pre-flight, memoised per template result — the site id and its kin. */\n const bindings = new Map<string, Promise<Record<string, string>>>()\n function resolveBindings(extra: Readonly<Record<string, string>>): Promise<Record<string, string>> {\n const base = { ...stringConfig, ...extra }\n if (!d.resolve) return Promise.resolve(base)\n const url = origin + fill(d.resolve.path, base)\n let pending = bindings.get(url)\n if (!pending) {\n pending = authHeaders()\n .then((headers) => fetch(url, { headers }))\n .then((res) => res.json())\n .then((body: unknown) => {\n const value = readPath(body, d.resolve!.field)\n if (typeof value !== 'string') {\n throw new Error(`engine pre-flight ${url} carried no \"${d.resolve!.field}\"`)\n }\n return { ...base, [d.resolve!.as]: value }\n })\n .catch((err: unknown) => {\n bindings.delete(url)\n throw err\n })\n bindings.set(url, pending)\n }\n return pending\n }\n\n function applyTransforms(row: Record<string, unknown>): Record<string, unknown> {\n if (transforms.length === 0) return row\n const out = { ...row }\n for (const { field, fn } of transforms) {\n if (field in out) out[field] = fn(out[field])\n }\n return out\n }\n\n function mapRow(raw: unknown): Record<string, unknown> {\n const item = (raw ?? {}) as Record<string, unknown>\n const row: Record<string, unknown> = { id: readPath(item, readId) }\n for (const key of d.read.lift ?? []) {\n const v = readPath(item, key)\n if (v !== undefined) row[key] = v\n }\n /**\n * `flatten: \"*\"` means THE ITEM ITSELF IS THE BAG.\n *\n * `mapRow` starts from `{ id }` and only grows when `flatten` names a\n * nested bag. Graph's bag is `fields`; flat REST has no bag because the\n * item is already the row — so without this a plain REST read produced\n * rows carrying nothing but `id`, and the panels rendered empty for a\n * reason no warning named.\n *\n * `\"*\"` is spelled out rather than inferred from an absent `flatten`:\n * inferring it would silently change what every existing declaration\n * means, and a fallback nobody declared is how this family of bugs got\n * here. It is also not a valid `readPath`, so it can never collide with a\n * real path. The schema is untouched — it already accepts any non-empty\n * string, so contracts stay frozen.\n */\n const bag = readFlatten === '*' ? item : readFlatten ? readPath(item, readFlatten) : undefined\n if (bag && typeof bag === 'object') Object.assign(row, bag as Record<string, unknown>)\n return applyTransforms(row)\n }\n\n /** One base per resolved binding set — the site is already inside baseUrl. */\n const bases = new Map<string, Promise<HttpEntityConnector>>()\n function baseFor(extra: Readonly<Record<string, string>>): Promise<HttpEntityConnector> {\n const key = JSON.stringify(extra)\n let pending = bases.get(key)\n if (!pending) {\n pending = resolveBindings(extra)\n .then(\n (bound) =>\n new HttpEntityConnector(name, {\n baseUrl: origin,\n // Synchronous: every binding is already resolved into the string.\n entityPathMapper: (entityType) => fill(d.path, { ...bound, entityType: String(entityType) }),\n ...(d.params ? { defaultQueryParams: d.params } : {}),\n // `none` means no credential at all: the visitor's token must never reach\n // a host that did not ask for it, so no auth config is passed.\n ...(d.auth.type === 'none' ? {} : { auth: { type: d.auth.type, token: getAccessToken } }),\n responseMapper: (raw) => {\n const items = readPath(raw, readItems)\n return { items: Array.isArray(items) ? items.map(mapRow) : [] }\n },\n })\n )\n .catch((err: unknown) => {\n bases.delete(key)\n throw err\n })\n bases.set(key, pending)\n }\n return pending\n }\n\n /** A query may name its own binding overrides (e.g. a different site). */\n function overridesFrom(query: EntityQuery | undefined): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries((query ?? {}) as Record<string, unknown>)) {\n if (typeof v === 'string' && k in stringConfig) out[k] = v\n }\n return out\n }\n\n return {\n name,\n type: 'custom' as const,\n\n async query(entityType: EntityTypeId, query: EntityQuery): Promise<ConnectorQueryResult> {\n try {\n const base = await baseFor(overridesFrom(query))\n // A declared engine talks to a FOREIGN API. The base connector would\n // serialise the surface's query as the platform's own wire params —\n // `filter=<json>&sort=&limit=&offset=` — which mean nothing there:\n // Microsoft Graph answers 400 to any list read carrying `filter=`\n // (sort-only reads happen to pass, so half the pages would be live and\n // half fallback). Fetch with the declaration's own params only and apply the query\n // here, where every declared engine speaks the same query language.\n const res = await base.query(entityType, {})\n if (!res.success) return res\n return { success: true, result: applyEntityQuery(res.result.items, query) }\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'engine query failed' }\n }\n },\n\n async read(entityType: EntityTypeId, instanceId: EntityInstanceId): Promise<ConnectorResult> {\n const result = await this.query(entityType, {})\n if (!result.success) return { success: false, error: result.error }\n const match = result.result.items.find((i) => (i as { id?: unknown }).id === instanceId)\n return match ? { success: true, data: match } : { success: false, error: 'Not found' }\n },\n\n async create(entityType: EntityTypeId, data: Record<string, unknown>): Promise<ConnectorResult> {\n try {\n const base = await baseFor({})\n // The wrap belongs to the LAYER: the base posts whatever it is handed,\n // and the declaration says what shape to hand it.\n const body = d.write?.bodyWrap ? { [d.write.bodyWrap]: applyTransforms(data) } : applyTransforms(data)\n const result = await base.create(entityType, body)\n return result.success ? { success: true, data: mapRow(result.data) } : result\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'engine create failed' }\n }\n },\n\n async update(): Promise<ConnectorResult> {\n return { success: false, error: 'declared engines do not support update yet' }\n },\n\n async delete(entityType: EntityTypeId, instanceId: EntityInstanceId): Promise<ConnectorResult> {\n try {\n const base = await baseFor({})\n return await base.delete(entityType, instanceId)\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'engine delete failed' }\n }\n },\n } as EntityConnector\n}\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector Engine Registry
|
|
3
|
+
*
|
|
4
|
+
* Connection name → constructed engine instance. Owns the load-and-
|
|
5
|
+
* construct lifecycle: `configure()` declares the world's connections
|
|
6
|
+
* (which engine bundle serves each name + per-connection config), and
|
|
7
|
+
* `resolve()` lazily loads the bundle via `ConnectorModuleLoader` and
|
|
8
|
+
* constructs the engine ONCE with `(getAccessToken, config)` — the exact
|
|
9
|
+
* signature the existing engines (GraphListConnector, the Graph motor)
|
|
10
|
+
* already take.
|
|
11
|
+
*
|
|
12
|
+
* Auth seam: the access-token hook handed to each engine closes over an
|
|
13
|
+
* injectable resolver. By default the resolver answers
|
|
14
|
+
* `null` — engines behave as unauthenticated and degrade honestly. A host
|
|
15
|
+
* plugs the AuthRegistry in via `setAccessTokenResolver` (or the factory
|
|
16
|
+
* option) without another core change.
|
|
17
|
+
*
|
|
18
|
+
* Error isolation: `resolve()` NEVER throws. A failed load or construction
|
|
19
|
+
* marks the entry `failed` with the reason (`getStatus`) and resolves
|
|
20
|
+
* `null`; consumers surface the reason (e.g. into the `__errors` context)
|
|
21
|
+
* instead of crashing the surface.
|
|
22
|
+
*
|
|
23
|
+
* @module @pwfabric/runtime/connectors/engine-registry
|
|
24
|
+
*/
|
|
25
|
+
import { type ConnectorModuleLoaderImpl } from './module-loader.js';
|
|
26
|
+
import type { ConnectorAccessTokenResolver, ConnectorConnectionBinding, ConnectorEngine, ConnectorEngineRegistry, ConnectorEngineStatus } from './types.js';
|
|
27
|
+
export declare class DefaultConnectorEngineRegistry implements ConnectorEngineRegistry {
|
|
28
|
+
private readonly entries;
|
|
29
|
+
private readonly loader;
|
|
30
|
+
private tokenResolver;
|
|
31
|
+
constructor(options?: {
|
|
32
|
+
readonly loader?: ConnectorModuleLoaderImpl;
|
|
33
|
+
readonly getAccessToken?: ConnectorAccessTokenResolver;
|
|
34
|
+
});
|
|
35
|
+
configure(bindings: readonly ConnectorConnectionBinding[]): void;
|
|
36
|
+
register(connection: string, engine: ConnectorEngine): void;
|
|
37
|
+
resolve(connection: string): Promise<ConnectorEngine | null>;
|
|
38
|
+
getStatus(connection: string): ConnectorEngineStatus;
|
|
39
|
+
setAccessTokenResolver(resolver: ConnectorAccessTokenResolver): void;
|
|
40
|
+
list(): readonly string[];
|
|
41
|
+
clear(): void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a connector engine registry. One per world/host render context —
|
|
45
|
+
* deliberately NOT a singleton (per-connection config is world state).
|
|
46
|
+
*/
|
|
47
|
+
export declare function createConnectorEngineRegistry(options?: {
|
|
48
|
+
readonly loader?: ConnectorModuleLoaderImpl;
|
|
49
|
+
readonly getAccessToken?: ConnectorAccessTokenResolver;
|
|
50
|
+
}): ConnectorEngineRegistry;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector Engine Registry
|
|
3
|
+
*
|
|
4
|
+
* Connection name → constructed engine instance. Owns the load-and-
|
|
5
|
+
* construct lifecycle: `configure()` declares the world's connections
|
|
6
|
+
* (which engine bundle serves each name + per-connection config), and
|
|
7
|
+
* `resolve()` lazily loads the bundle via `ConnectorModuleLoader` and
|
|
8
|
+
* constructs the engine ONCE with `(getAccessToken, config)` — the exact
|
|
9
|
+
* signature the existing engines (GraphListConnector, the Graph motor)
|
|
10
|
+
* already take.
|
|
11
|
+
*
|
|
12
|
+
* Auth seam: the access-token hook handed to each engine closes over an
|
|
13
|
+
* injectable resolver. By default the resolver answers
|
|
14
|
+
* `null` — engines behave as unauthenticated and degrade honestly. A host
|
|
15
|
+
* plugs the AuthRegistry in via `setAccessTokenResolver` (or the factory
|
|
16
|
+
* option) without another core change.
|
|
17
|
+
*
|
|
18
|
+
* Error isolation: `resolve()` NEVER throws. A failed load or construction
|
|
19
|
+
* marks the entry `failed` with the reason (`getStatus`) and resolves
|
|
20
|
+
* `null`; consumers surface the reason (e.g. into the `__errors` context)
|
|
21
|
+
* instead of crashing the surface.
|
|
22
|
+
*
|
|
23
|
+
* @module @pwfabric/runtime/connectors/engine-registry
|
|
24
|
+
*/
|
|
25
|
+
import { ConnectorModuleLoader } from './module-loader.js';
|
|
26
|
+
// ============================================================
|
|
27
|
+
// HELPERS
|
|
28
|
+
// ============================================================
|
|
29
|
+
/** Duck-check the minimal read contract every engine must satisfy. */
|
|
30
|
+
function isConnectorEngine(value) {
|
|
31
|
+
return (typeof value === 'object' &&
|
|
32
|
+
value !== null &&
|
|
33
|
+
typeof value.query === 'function');
|
|
34
|
+
}
|
|
35
|
+
/** Default token resolver — unauthenticated until a host installs one. */
|
|
36
|
+
const NULL_TOKEN_RESOLVER = async () => null;
|
|
37
|
+
// ============================================================
|
|
38
|
+
// REGISTRY IMPLEMENTATION
|
|
39
|
+
// ============================================================
|
|
40
|
+
export class DefaultConnectorEngineRegistry {
|
|
41
|
+
entries = new Map();
|
|
42
|
+
loader;
|
|
43
|
+
tokenResolver;
|
|
44
|
+
constructor(options) {
|
|
45
|
+
this.loader = options?.loader ?? ConnectorModuleLoader;
|
|
46
|
+
this.tokenResolver = options?.getAccessToken ?? NULL_TOKEN_RESOLVER;
|
|
47
|
+
}
|
|
48
|
+
configure(bindings) {
|
|
49
|
+
// Replace semantics: drop entries whose binding disappeared, keep
|
|
50
|
+
// constructed engines whose binding is unchanged (identity by
|
|
51
|
+
// connection + descriptor + config reference equality is too strict —
|
|
52
|
+
// a reconfigure resets the entry so config changes take effect).
|
|
53
|
+
this.entries.clear();
|
|
54
|
+
for (const binding of bindings) {
|
|
55
|
+
this.entries.set(binding.connection, {
|
|
56
|
+
binding,
|
|
57
|
+
engine: null,
|
|
58
|
+
pending: null,
|
|
59
|
+
failure: null,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
register(connection, engine) {
|
|
64
|
+
this.entries.set(connection, { binding: null, engine, pending: null, failure: null });
|
|
65
|
+
}
|
|
66
|
+
async resolve(connection) {
|
|
67
|
+
const entry = this.entries.get(connection);
|
|
68
|
+
if (!entry)
|
|
69
|
+
return null;
|
|
70
|
+
if (entry.engine)
|
|
71
|
+
return entry.engine;
|
|
72
|
+
if (entry.failure !== null)
|
|
73
|
+
return null; // sticky until reconfigure/clear
|
|
74
|
+
if (entry.pending)
|
|
75
|
+
return entry.pending;
|
|
76
|
+
if (!entry.binding)
|
|
77
|
+
return null;
|
|
78
|
+
const binding = entry.binding;
|
|
79
|
+
const construct = async () => {
|
|
80
|
+
try {
|
|
81
|
+
const factory = await this.loader.load(binding.descriptor);
|
|
82
|
+
// The hook closes over `this.tokenResolver` so a later
|
|
83
|
+
// setAccessTokenResolver takes effect on the NEXT token
|
|
84
|
+
// request without reconstructing engines.
|
|
85
|
+
const getAccessToken = async () => this.tokenResolver(binding);
|
|
86
|
+
const engine = factory(getAccessToken, binding.config ?? {});
|
|
87
|
+
if (!isConnectorEngine(engine)) {
|
|
88
|
+
throw new Error(`Engine for connection '${binding.connection}' does not expose query() — ` +
|
|
89
|
+
`not a usable connector engine`);
|
|
90
|
+
}
|
|
91
|
+
entry.engine = engine;
|
|
92
|
+
return engine;
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
entry.failure = err instanceof Error ? err.message : String(err);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
entry.pending = null;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
entry.pending = construct();
|
|
103
|
+
return entry.pending;
|
|
104
|
+
}
|
|
105
|
+
getStatus(connection) {
|
|
106
|
+
const entry = this.entries.get(connection);
|
|
107
|
+
if (!entry)
|
|
108
|
+
return { state: 'unregistered' };
|
|
109
|
+
if (entry.engine)
|
|
110
|
+
return { state: 'ready' };
|
|
111
|
+
if (entry.failure !== null)
|
|
112
|
+
return { state: 'failed', reason: entry.failure };
|
|
113
|
+
return { state: 'pending' };
|
|
114
|
+
}
|
|
115
|
+
setAccessTokenResolver(resolver) {
|
|
116
|
+
this.tokenResolver = resolver;
|
|
117
|
+
}
|
|
118
|
+
list() {
|
|
119
|
+
return Array.from(this.entries.keys());
|
|
120
|
+
}
|
|
121
|
+
clear() {
|
|
122
|
+
this.entries.clear();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// ============================================================
|
|
126
|
+
// FACTORY
|
|
127
|
+
// ============================================================
|
|
128
|
+
/**
|
|
129
|
+
* Create a connector engine registry. One per world/host render context —
|
|
130
|
+
* deliberately NOT a singleton (per-connection config is world state).
|
|
131
|
+
*/
|
|
132
|
+
export function createConnectorEngineRegistry(options) {
|
|
133
|
+
return new DefaultConnectorEngineRegistry(options);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=engine-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-registry.js","sourceRoot":"","sources":["../../src/connectors/engine-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Connector Engine Registry\n *\n * Connection name → constructed engine instance. Owns the load-and-\n * construct lifecycle: `configure()` declares the world's connections\n * (which engine bundle serves each name + per-connection config), and\n * `resolve()` lazily loads the bundle via `ConnectorModuleLoader` and\n * constructs the engine ONCE with `(getAccessToken, config)` — the exact\n * signature the existing engines (GraphListConnector, the Graph motor)\n * already take.\n *\n * Auth seam: the access-token hook handed to each engine closes over an\n * injectable resolver. By default the resolver answers\n * `null` — engines behave as unauthenticated and degrade honestly. A host\n * plugs the AuthRegistry in via `setAccessTokenResolver` (or the factory\n * option) without another core change.\n *\n * Error isolation: `resolve()` NEVER throws. A failed load or construction\n * marks the entry `failed` with the reason (`getStatus`) and resolves\n * `null`; consumers surface the reason (e.g. into the `__errors` context)\n * instead of crashing the surface.\n *\n * @module @pwfabric/runtime/connectors/engine-registry\n */\n\nimport { ConnectorModuleLoader, type ConnectorModuleLoaderImpl } from './module-loader.js'\nimport type {\n ConnectorAccessTokenResolver,\n ConnectorConnectionBinding,\n ConnectorEngine,\n ConnectorEngineRegistry,\n ConnectorEngineStatus,\n} from './types.js'\n\n// ============================================================\n// HELPERS\n// ============================================================\n\n/** Duck-check the minimal read contract every engine must satisfy. */\nfunction isConnectorEngine(value: unknown): value is ConnectorEngine {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { query?: unknown }).query === 'function'\n )\n}\n\n/** Default token resolver — unauthenticated until a host installs one. */\nconst NULL_TOKEN_RESOLVER: ConnectorAccessTokenResolver = async () => null\n\ninterface RegistryEntry {\n readonly binding: ConnectorConnectionBinding | null\n engine: ConnectorEngine | null\n pending: Promise<ConnectorEngine | null> | null\n failure: string | null\n}\n\n// ============================================================\n// REGISTRY IMPLEMENTATION\n// ============================================================\n\nexport class DefaultConnectorEngineRegistry implements ConnectorEngineRegistry {\n private readonly entries = new Map<string, RegistryEntry>()\n private readonly loader: ConnectorModuleLoaderImpl\n private tokenResolver: ConnectorAccessTokenResolver\n\n constructor(options?: {\n readonly loader?: ConnectorModuleLoaderImpl\n readonly getAccessToken?: ConnectorAccessTokenResolver\n }) {\n this.loader = options?.loader ?? ConnectorModuleLoader\n this.tokenResolver = options?.getAccessToken ?? NULL_TOKEN_RESOLVER\n }\n\n configure(bindings: readonly ConnectorConnectionBinding[]): void {\n // Replace semantics: drop entries whose binding disappeared, keep\n // constructed engines whose binding is unchanged (identity by\n // connection + descriptor + config reference equality is too strict —\n // a reconfigure resets the entry so config changes take effect).\n this.entries.clear()\n for (const binding of bindings) {\n this.entries.set(binding.connection, {\n binding,\n engine: null,\n pending: null,\n failure: null,\n })\n }\n }\n\n register(connection: string, engine: ConnectorEngine): void {\n this.entries.set(connection, { binding: null, engine, pending: null, failure: null })\n }\n\n async resolve(connection: string): Promise<ConnectorEngine | null> {\n const entry = this.entries.get(connection)\n if (!entry) return null\n if (entry.engine) return entry.engine\n if (entry.failure !== null) return null // sticky until reconfigure/clear\n if (entry.pending) return entry.pending\n if (!entry.binding) return null\n\n const binding = entry.binding\n const construct = async (): Promise<ConnectorEngine | null> => {\n try {\n const factory = await this.loader.load(binding.descriptor)\n // The hook closes over `this.tokenResolver` so a later\n // setAccessTokenResolver takes effect on the NEXT token\n // request without reconstructing engines.\n const getAccessToken = async (): Promise<string | null> =>\n this.tokenResolver(binding)\n const engine: unknown = factory(getAccessToken, binding.config ?? {})\n if (!isConnectorEngine(engine)) {\n throw new Error(\n `Engine for connection '${binding.connection}' does not expose query() — ` +\n `not a usable connector engine`\n )\n }\n entry.engine = engine\n return engine\n } catch (err) {\n entry.failure = err instanceof Error ? err.message : String(err)\n return null\n } finally {\n entry.pending = null\n }\n }\n\n entry.pending = construct()\n return entry.pending\n }\n\n getStatus(connection: string): ConnectorEngineStatus {\n const entry = this.entries.get(connection)\n if (!entry) return { state: 'unregistered' }\n if (entry.engine) return { state: 'ready' }\n if (entry.failure !== null) return { state: 'failed', reason: entry.failure }\n return { state: 'pending' }\n }\n\n setAccessTokenResolver(resolver: ConnectorAccessTokenResolver): void {\n this.tokenResolver = resolver\n }\n\n list(): readonly string[] {\n return Array.from(this.entries.keys())\n }\n\n clear(): void {\n this.entries.clear()\n }\n}\n\n// ============================================================\n// FACTORY\n// ============================================================\n\n/**\n * Create a connector engine registry. One per world/host render context —\n * deliberately NOT a singleton (per-connection config is world state).\n */\nexport function createConnectorEngineRegistry(options?: {\n readonly loader?: ConnectorModuleLoaderImpl\n readonly getAccessToken?: ConnectorAccessTokenResolver\n}): ConnectorEngineRegistry {\n return new DefaultConnectorEngineRegistry(options)\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector Engine Module
|
|
3
|
+
*
|
|
4
|
+
* Platform-side loading + registry for CONNECTOR/PROVIDER atom engines,
|
|
5
|
+
* so platform-rendered surfaces can execute the same engines the runner
|
|
6
|
+
* app runs from its bundled pack source.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/connectors
|
|
9
|
+
*/
|
|
10
|
+
export type { ConnectorEngineKind, ConnectorEngineDescriptor, ConnectorAccessTokenHook, ConnectorEngineFactory, ConnectorEngineModule, ConnectorEngine, ConnectorConnectionBinding, ConnectorAccessTokenResolver, ConnectorEngineStatus, ConnectorEngineRegistry, SurfaceConnection, SurfaceConnectionsValue, } from './types.js';
|
|
11
|
+
export { ConnectorModuleLoader, ConnectorModuleLoaderImpl, createConnectorModuleLoader, type ConnectorModuleEvaluator, } from './module-loader.js';
|
|
12
|
+
export { DefaultConnectorEngineRegistry, createConnectorEngineRegistry, } from './engine-registry.js';
|
|
13
|
+
export { createDeclaredConnector, VALUE_TRANSFORMS } from './declared-connector.js';
|
|
14
|
+
export type { DeclaredConnectorOptions } from './declared-connector.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector Engine Module
|
|
3
|
+
*
|
|
4
|
+
* Platform-side loading + registry for CONNECTOR/PROVIDER atom engines,
|
|
5
|
+
* so platform-rendered surfaces can execute the same engines the runner
|
|
6
|
+
* app runs from its bundled pack source.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/connectors
|
|
9
|
+
*/
|
|
10
|
+
export { ConnectorModuleLoader, ConnectorModuleLoaderImpl, createConnectorModuleLoader, } from './module-loader.js';
|
|
11
|
+
export { DefaultConnectorEngineRegistry, createConnectorEngineRegistry, } from './engine-registry.js';
|
|
12
|
+
// an engine built from its declaration; the runtime learns no vendor name.
|
|
13
|
+
export { createDeclaredConnector, VALUE_TRANSFORMS } from './declared-connector.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/connectors/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiBH,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,GAE5B,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,GAC9B,MAAM,sBAAsB,CAAA;AAE7B,2EAA2E;AAC3E,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA","sourcesContent":["/**\n * Connector Engine Module\n *\n * Platform-side loading + registry for CONNECTOR/PROVIDER atom engines,\n * so platform-rendered surfaces can execute the same engines the runner\n * app runs from its bundled pack source.\n *\n * @module @pwfabric/runtime/connectors\n */\n\nexport type {\n ConnectorEngineKind,\n ConnectorEngineDescriptor,\n ConnectorAccessTokenHook,\n ConnectorEngineFactory,\n ConnectorEngineModule,\n ConnectorEngine,\n ConnectorConnectionBinding,\n ConnectorAccessTokenResolver,\n ConnectorEngineStatus,\n ConnectorEngineRegistry,\n SurfaceConnection,\n SurfaceConnectionsValue,\n} from './types.js'\n\nexport {\n ConnectorModuleLoader,\n ConnectorModuleLoaderImpl,\n createConnectorModuleLoader,\n type ConnectorModuleEvaluator,\n} from './module-loader.js'\n\nexport {\n DefaultConnectorEngineRegistry,\n createConnectorEngineRegistry,\n} from './engine-registry.js'\n\n// an engine built from its declaration; the runtime learns no vendor name.\nexport { createDeclaredConnector, VALUE_TRANSFORMS } from './declared-connector.js'\nexport type { DeclaredConnectorOptions } from './declared-connector.js'\n"]}
|