/** * Shared projection helper: composes installed/active/unmanaged from * declared/resolved/actual plus the installed-pack set. * * Per the projection invariant in the workspace read-model design (Decision 7): * * ```ts * direct = declared.map(withInstallationOrigin("direct")); * implicit = installedPacks * .flatMap(p => p.members) * .filter(notDeclaredByName) // direct (incl. disabled) wins * .map(withInstallationOrigin("pack-member")); * installed = direct + implicit; * active = installed.filter(activation === "enabled"); * unmanaged = actual * .filter(notClaimedByInstalled) * .filter(notClaimedBySubjectPolicy); * ``` * * The helper owns: * - direct-over-pack precedence; * - disabled-direct still claims actual occurrences (excluded from active and * from unmanaged); * - actual-occurrence attachment via `policy.attachActualToInstalled`; * - orphaned-resolved diagnostics — resolved entries with no direct or * pack-member home publish a warning; * - deterministic name-sorted ordering. * * The helper does NOT own subject row shape or subject policy. Both come in as * parameters via `SubjectPolicy<...>`. */ import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import type { Diagnostics, Warning } from "../diagnostics.js"; import type { LockfileReadError, SettingsReadError } from "../errors.js"; import type { ActivationState, InstallationOrigin, InstalledPackRef } from "../types.js"; import { type RowWithKey } from "./indexByName.js"; /** * One entry within a subject's `declared` payload. The shape is opaque to the * helper; the policy callbacks bridge to subject internals. */ export type TDeclaredEntry = TDeclared extends ReadonlyArray ? E : TDeclared; /** One entry within a subject's `resolved` payload. */ export type TResolvedEntry = TResolved extends ReadonlyArray ? E : TResolved; /** One actual occurrence within a subject's `actual` payload. */ export type TActualEntry = TActual extends ReadonlyArray ? E : TActual; /** * Subject-specific policy callbacks the projection helper requires. */ export interface SubjectPolicy { /** Iterate the subject's declared entries from a decoded `declared` payload. */ readonly declaredEntries: (declared: TDeclared) => ReadonlyArray>; /** Extract the subject name from a declared entry. */ readonly declaredName: (entry: TDeclaredEntry) => string; /** * Activation state derived from a declared entry. */ readonly declaredActivation: (entry: TDeclaredEntry) => ActivationState; /** Iterate the subject's resolved entries from a decoded `resolved` payload. */ readonly resolvedEntries: (resolved: TResolved) => ReadonlyArray>; /** Extract the subject name from a resolved entry. */ readonly resolvedName: (entry: TResolvedEntry) => string; /** Iterate the subject's actual occurrences from an `actual` payload. */ readonly actualEntries: (actual: TActual) => ReadonlyArray>; /** Extract the subject name from an actual occurrence. */ readonly actualName: (entry: TActualEntry) => string; /** Extract the subject name from a pack-member entry. */ readonly packMemberName: (member: TPackMember) => string; /** Activation state for a pack-member installed row. */ readonly packMemberActivation: (member: TPackMember) => ActivationState; /** Filter actual occurrences to those that match a given installed name. */ readonly attachActualToInstalled: (name: string, actual: ReadonlyArray>) => ReadonlyArray>; /** * Subject-policy filter for unmanaged actual occurrences. Returns `true` if * the occurrence is NOT claimed by subject-specific policy. */ readonly notClaimedBySubjectPolicy: (entry: TActualEntry) => boolean; /** Build a subject installed row from the assembled facts. */ readonly buildInstalledRow: (input: BuildInstalledRowInput) => TInstalled; /** Build a subject unmanaged row from one actual occurrence. */ readonly buildUnmanagedRow: (entry: TActualEntry) => TUnmanaged; /** Build the orphan-resolved warning published when a resolved entry has no home. */ readonly resolvedOrphanWarning: (name: string) => Warning; } /** * Input passed to `buildInstalledRow`. Carries everything the subject needs * to construct its installed row. */ export interface BuildInstalledRowInput { readonly name: string; readonly installationOrigin: InstallationOrigin, TPackMember>; readonly activation: ActivationState; readonly resolved: Option.Option>; readonly actual: ReadonlyArray>; readonly providingPacks: ReadonlyArray; } export interface ProjectInstalledExtensionsInput { readonly declared: Effect.Effect, SettingsReadError>; readonly resolved: Effect.Effect, LockfileReadError>; readonly actual: Effect.Effect; readonly installedPacks: Effect.Effect, SettingsReadError | LockfileReadError>; readonly packMembers: (pack: TPack) => ReadonlyArray; readonly packRef: (pack: TPack) => InstalledPackRef; readonly policy: SubjectPolicy; readonly diagnostics: Diagnostics; } export interface ProjectInstalledExtensionsOutput { readonly installed: ReadonlyArray; readonly active: ReadonlyArray; readonly unmanaged: ReadonlyArray; } export declare const makeProjectedSubjectCells: , TResolved, TActual, TInstalled extends RowWithKey, TUnmanaged>(args: { readonly declared: Effect.Effect, SettingsReadError>; readonly resolved: Effect.Effect, LockfileReadError>; readonly actual: Effect.Effect; readonly project: Effect.Effect, SettingsReadError | LockfileReadError>; }) => { declared: Effect.Effect, SettingsReadError, never>; resolved: Effect.Effect, LockfileReadError, never>; actual: Effect.Effect; installed: Effect.Effect; byName: (name: string) => Effect.Effect, SettingsReadError | LockfileReadError, never>; declaredByName: (name: string) => Effect.Effect>, SettingsReadError, never>; active: Effect.Effect; unmanaged: Effect.Effect; }; /** * Compose installed, active, and unmanaged rows from declared/resolved/actual * plus the installed-pack set, per the projection invariant. * * Source-read failures propagate so invalid persisted authority is never * treated as absent. */ export declare const projectInstalledExtensions: (input: ProjectInstalledExtensionsInput) => Effect.Effect, SettingsReadError | LockfileReadError>; //# sourceMappingURL=projection.d.ts.map