using Inversion; using Invercargill.Expressions; using Astralis; namespace Statum { /** * Inversion module that registers the Statum runtime services and framework * endpoints. * * Register this module with the application container (after which a * {@link StatumConfigurator} binds pages/entrypoints/resources) to wire up * the {@link StateService}, signing/encryption providers, realtime channel, * entrypoint route table, held-slot resolver and the `/_statum/*` endpoints. */ public class StatumModule : Object, Module { public void register_components(Container container) throws Error { container.register_singleton(); container.register_singleton(); // Enable predicate member-access on Properties (slot public/private // data) for the runtime `pstm-if`/`pstm-guard` evaluator. Invercargill.Expressions.TypeAccessorRegistry.get_instance() .register_property_accessor(new StatumPropertiesAccessorFactory()); container.register_singleton(); container.register_singleton(); container.register_singleton(); container.register_singleton(); // The realtime channel endpoint is the singleton ChannelService. container.register_singleton() .as() .as() .with_metadata(new EndpointRoute("/_statum/channel")); container.register_scoped() .as() .with_metadata(new EndpointRoute("/_statum/channel/{id}", Method.PATCH)); container.register_scoped() .as() .with_metadata(new EndpointRoute("/_statum/entrypoint")); container.register_scoped() .as() .with_metadata(new EndpointRoute("/_statum/slots", Method.POST)); // The single action endpoint resolves /_statum/action/{guid} to the // action type via ActionRegistry. Registered for all common verbs so // an action may be authored with whichever method suits it. container.register_scoped() .as() .with_metadata(new EndpointRoute("/_statum/action/{guid}", Method.GET, Method.POST, Method.PUT, Method.PATCH, Method.DELETE)); container.register_scoped() .as() .with_metadata(new EndpointRoute("/_statum/resource/{name}")); // Embed the Statum client scripts as default resources, served from // /_statum/resource/statum.js and /_statum/resource/statum-worker.js // (precompressed at build time). Apps get them for free and may add // their own resources via StatumConfigurator.add_resource. container.register_startup().as(); container.register_startup().as(); } } /** * Binds application pages, entrypoints and resources into the container. * * Usage mirrors Spry's {@link Spry.SpryConfigurator}: * * ``` * var statum = application.configure_with(); * statum.add_entrypoint_page(new EndpointRoute("/")); * statum.add_page(new EndpointRoute("/about")); * statum.add_resource(); * ``` * * {@link add_entrypoint_page} registers the generated {@link StatumPage} * (served at its URI) and adds the entrypoint to the {@link EntrypointRouteTable} * so `/_statum/entrypoint?uri=…` dispatches to it. The page route is supplied * explicitly (mirroring Spry) rather than read reflectively from the class. */ public class StatumConfigurator : Object { private Container container = inject(); private EntrypointRouteTable route_table = inject(); private ActionRegistry action_registry = inject(); /** * Registers a static page (no entrypoint) served at `route`. */ public void add_page(EndpointRoute route) { container.register_scoped() .as() .with_metadata(route); } /** * Registers a page served at `route` AND binds `TEntrypoint` to that route * in the entrypoint table (so `/_statum/entrypoint?uri=route` dispatches to * it). * * (Named distinctly from {@link add_page} because Vala does not permit * methods that overload on generic arity.) */ public void add_entrypoint_page(EndpointRoute route) { add_page(route); container.register_transient(); route_table.register(route, typeof(TEntrypoint)); } /** Registers a {@link StatumResource} for serving from `/_statum/resource/{name}`. */ public void add_resource() { container.register_startup() .as(); } /** * Registers a {@link StatumAction}, auto-assigning it a GUID endpoint at * `/_statum/action/{guid}` (Spry-style — no hand-picked URI). Invoke the * action from the client by embedding an authored reference in state (see * {@link ActionRegistry.author}) and binding it with `stm-action`. */ public void action() { container.register_scoped(); action_registry.register(); } } }