GeoLeaf Core API - v3.0.0
    Preparing search index...

    Self-sufficient installer for one in-core capability.

    Regroups the 3 static-anchoring layers of a capability behind a single import site, so a preset embarks a capability by importing ONLY its install.ts:

    • A (facade re-exports) — carried by the preset entry that imports this ;
    • B (window.GeoLeaf.* assignments) — registerGlobals ;
    • C (register + gated module) — declaration + createModule.
    interface CapabilityInstaller {
        declaration: ICapabilityDeclaration;
        registerGlobals(gl: Record<string, unknown>): void;
        createModule?(): ICoreModule;
        moduleGate?: ICapabilityConfigGate;
        sharedLifecycle?(ctx: SharedLifecycleContext): void;
        sharedTeardown?(): void;
    }
    Index

    Properties

    Capability declaration — reuses the existing <cap>-capability.ts (id, gate, schema).

    Gate of createModule when the module's activation key differs from the capability's own gate — the sub-feature case.

    permalink owns no module of its own (two boot hooks drive it), but it embarks share (S13 F7), whose ShareModule is gated on the sub-key modules.permalink.share.enabled (opt-out), NOT on modules.permalink.enabled. Share has no capability declaration of its own by design, and the registry ignores a second register() on the same id — so the gate cannot be expressed as a second declaration. It is expressed here, as an optional FIELD (the contract's doctrine), evaluated by evaluateGate() with exactly the same semantics as a declaration gate.

    Absent → the declaration's own gate applies (the common case).

    Methods

    • Assigns the capability's public facade(s) onto the GeoLeaf namespace. This is the former "layer B" — the window.GeoLeaf.* writes moved out of the monolithic globals.{ui,api,geojson}.ts. Called by Pass 1 of the preset loop (presets/apply-preset.ts), ungated. Must be idempotent and additive — the kernel facades are written first, at import, by the globals.*.ts chain (phase A).

      (This used to say "composed by the boot via the module-setup seam". That was wrong even then — registerGlobals was always driven by apply-preset.ts — and the seam itself is gone since S6 Lot 5.)

      Parameters

      • gl: Record<string, unknown>

        the boot-populated GeoLeaf global namespace.

      Returns void

    • Factory for the capability's lifecycle module (ICoreModule).

      Absent for the three pull-based capabilities — they answer when asked and subscribe to nothing, so there is no lifecycle to drive:

      • cluster — pure resolvers queried on demand ;
      • taxonomy — supplies paint expressions and symbol ids to whoever asks ;
      • vector-tiles — publishes the _VectorTiles seam; the GeoJSON loader reads it back lazily through _loaderDeps.getVectorTiles().

      ⚠️ This list said cluster, taxonomy, permalink until CAPACITÉS S10, and it was wrong in both directions — the same error was copied into ARCHITECTURE.md and the CDC_technique.md §P2-14 table. permalink DOES declare a createModule: it builds ShareModule, the module of its share/ sub-feature, which is exactly why it also declares CapabilityInstaller.moduleGate. What permalink lacks is a lifecycle of its own — that one lives in capabilities/permalink/share/lifecycle.ts. And vector-tiles, the genuine third member, was named nowhere.

      pwa and offline are absent too, but for the other reason: they are app-global and express their lifecycle through CapabilityInstaller.sharedLifecycle below.

      The rule is pinned by __tests__/capabilities/scaffold-taxonomy.test.js, which derives each family from the installers rather than from a list — so this comment cannot drift again without a red test.

      Returns ICoreModule

    • Pre-map lifecycle driven by the kernel shared module — the app-global case.

      pwa (step #7) and offline (step #8) own no ICoreModule: they are app-global, and their lifecycles must run on the post-merge config, inside SharedModule.init() (which runs after the profile resources load). Before S4 that was expressed by shared.module.ts statically importing PwaLifecycle and OfflineLifecycle — a kernel module hard-wired to two optional capabilities, which pinned both into the eager closure of every bundle no matter what its manifest said. That was THE anchor blocking tree-shaking.

      Expressing it as an optional FIELD (the contract's doctrine) inverts the dependency: SharedModule receives the entry's installers and calls whatever contributed itself. An entry that omits pwa simply has no contributor — and the capability tree-shakes out.

      Execution order is the manifest order. ⚠ No pair of contributors depends on it — this line read « pwa MUST precede offline: offline reads modules.pwa.enabled and refuses to start its engine without it » until 08/08/2026. The refusal is real; the ordering it was used to justify is not, because that flag lives in the merged config bag handed to every contributor. Measured: __tests__/presets/shared-lifecycle-order.test.ts (7.4).

      Parameters

      Returns void

    • Tears down what CapabilityInstaller.sharedLifecycle started, called from SharedModule.destroy(). Same inverted dependency: the kernel calls whatever contributed itself and imports no capability.

      Without this, an app-global capability keeps its module-level state across a create → destroy → recreate cycle: the lifecycle runs again on a state that was never reset. Capabilities driven by their own *.module.ts already reset in that module's destroy(); this is the equivalent for the ones SharedModule drives.

      Runs in reverse manifest order (mirror of sharedLifecycle) — a real mechanism, asserted by __tests__/config/s15-modules-storage-init.test.js. ⚠ The reason this line gave for it was not: it read « …, so a capability is torn down before the one it depends on: offline (#8) then pwa (#7) » until 08/08/2026. No sharedTeardown of the shipped manifest reads another's state, and SLO-06 measured the teardown effects invariant under both orders. The mirror is kept for symmetry with sharedLifecycle, not for a dependency.

      Returns void