| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- 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<Cryptography.SigningProvider>();
- container.register_singleton<Cryptography.EncryptionProvider>();
- // 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<StateService>();
- container.register_singleton<HeldSlotResolver>();
- container.register_singleton<EntrypointRouteTable>();
- container.register_singleton<ActionRegistry>();
- // The realtime channel endpoint is the singleton ChannelService.
- container.register_singleton<ChannelEndpoint>()
- .as<ChannelService>()
- .as<Endpoint>()
- .with_metadata<EndpointRoute>(new EndpointRoute("/_statum/channel"));
- container.register_scoped<ChannelSubscriptionEndpoint>()
- .as<Endpoint>()
- .with_metadata<EndpointRoute>(new EndpointRoute("/_statum/channel/{id}", Method.PATCH));
- container.register_scoped<EntrypointEndpoint>()
- .as<Endpoint>()
- .with_metadata<EndpointRoute>(new EndpointRoute("/_statum/entrypoint"));
- container.register_scoped<SlotPostEndpoint>()
- .as<Endpoint>()
- .with_metadata<EndpointRoute>(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<ActionEndpoint>()
- .as<Endpoint>()
- .with_metadata<EndpointRoute>(new EndpointRoute("/_statum/action/{guid}",
- Method.GET, Method.POST, Method.PUT, Method.PATCH, Method.DELETE));
- container.register_scoped<ResourceEndpoint>()
- .as<Endpoint>()
- .with_metadata<EndpointRoute>(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<ClientScript>().as<StatumResource>();
- container.register_startup<ClientWorker>().as<StatumResource>();
- }
- }
- /**
- * Binds application pages, entrypoints and resources into the container.
- *
- * Usage mirrors Spry's {@link Spry.SpryConfigurator}:
- *
- * ```
- * var statum = application.configure_with<StatumConfigurator>();
- * statum.add_entrypoint_page<HomePage, HomeEntrypoint>(new EndpointRoute("/"));
- * statum.add_page<AboutPage>(new EndpointRoute("/about"));
- * statum.add_resource<LogoResource>();
- * ```
- *
- * {@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<Container>();
- private EntrypointRouteTable route_table = inject<EntrypointRouteTable>();
- private ActionRegistry action_registry = inject<ActionRegistry>();
- /**
- * Registers a static page (no entrypoint) served at `route`.
- */
- public void add_page<TPage>(EndpointRoute route) {
- container.register_scoped<TPage>()
- .as<Endpoint>()
- .with_metadata<EndpointRoute>(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<TPage, TEntrypoint>(EndpointRoute route) {
- add_page<TPage>(route);
- container.register_transient<TEntrypoint>();
- route_table.register(route, typeof(TEntrypoint));
- }
- /** Registers a {@link StatumResource} for serving from `/_statum/resource/{name}`. */
- public void add_resource<T>() {
- container.register_startup<T>()
- .as<StatumResource>();
- }
- /**
- * 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<TAction>() {
- container.register_scoped<TAction>();
- action_registry.register<TAction>();
- }
- }
- }
|