Skip to content

Re-export the private workspace type surfaces (kube-object, kube-api) through the extension API #2365

Description

@dex4er

Goal

Make the whole @freelensapp/kube-object type surface reachable through
@freelensapp/extensions, so extensions no longer need it as a direct dependency.

Surfaced while checking the real extensions against the dependency audit in #2360
(comment).

Why

The v2 packaging contract (#2304, D4/D5) says @freelensapp/extensions is the only
published package and every other @freelensapp/* is private and must not be a direct
dependency of an extension. Today that contract cannot be honoured: both maintained
extensions import types straight from @freelensapp/kube-object, and those types are not
re-exported by the API.

Extension Files importing @freelensapp/kube-object Types used
freelens-fluxcd-extension 5.3.1 41 LocalObjectReference
freelens-gateway-api-extension 0.2.1 4 Condition, LabelSelector, ObjectReference

These are plain building blocks for describing CRD specs — exactly what an extension that
adds resource views needs. All four exist today:

packages/kube-object/src/api-types.ts:332   ObjectReference
packages/kube-object/src/api-types.ts:342   LocalObjectReference
packages/kube-object/src/api-types.ts:374   LabelSelector
packages/kube-object/src/types/condition.ts:7   Condition

@freelensapp/kube-object is published on npm at 1.10.3 (2026-07-07), from the v1 era
where @freelensapp/core was itself a real dependency of every extension. In v2 the
package goes private, so unless the API re-exports it, both of these extensions are
blocked from porting.

Scope of the gap

@freelensapp/kube-object exports 419 top-level symbols.
extensions/common-api/k8s-api.ts re-exports 55 of them, so 364 are missing
including the four above. The re-exported set is essentially the concrete kube objects
(Pod, Deployment, Secret, …), KubeObject, KubeObjectMetadata, OwnerReference
and the JSON-API guards; what is missing is most of the shared spec vocabulary
(Affinity, Capabilities, ContainerPort, Probe, ResourceRequirements,
SecurityContext, Toleration, the types/ directory in general).

Picking symbols one by one as extensions ask for them does not scale. A blanket
export * from "@freelensapp/kube-object" in the K8sApi namespace is the obvious fix.

There is exactly one name collision

A collision check across common-api/k8s-api.ts, renderer-api/k8s-api.ts and
main-api/k8s-api.ts against all 419 names finds a single clash — and it is a real one,
two different types sharing a name:

// packages/kube-object/src/api-types.ts:287 — a Kubernetes resource status shape
export interface KubeObjectStatus {
  conditions?: BaseKubeObjectCondition[];
}

// packages/core/src/common/k8s-api/kube-object-status.ts:7 — the extension-facing
// status registration type, exported today via renderer-api/k8s-api.ts:188
export interface KubeObjectStatus {
  level: KubeObjectStatusLevel;
  text: string;
  timestamp?: string;
}

The second one is the v1 Renderer.K8sApi.KubeObjectStatus that extensions register status
providers against, so it must keep the bare name. The kube-object one needs a rename or an
explicit exclusion from the star export.

The same gap exists in @freelensapp/kube-api, in a subtler form

kube-object is the visible case because extensions import from it directly. kube-api
has the same problem one level down: the API exports the things, but not the types that
appear in their own signatures
.

@freelensapp/kube-api exports 81 symbols; the API re-exports 34 of them (the concrete
*Api classes). What is exported as a usable value but whose parameter types are not
nameable:

Exported by the API Its signature needs Nameable by an extension?
KubeApi (local alias) KubeApiOptions & ExternalKubeApiOptions ExternalKubeApiOptions yes, KubeApiOptions no
PodsApi, NodesApi, … DerivedKubeApiOptions no
KubeObjectStore KubeObjectStoreOptions no

Also missing and plausibly wanted by anyone writing CRD support: IKubeWatchEvent,
KubeApiListOptions, DeleteOptions, PropagationPolicy, ResourceDescriptor,
KubeApiQueryParams, parseKubeApi, createKubeApiURL.

Because the workspace packages are private and inlined, these declarations are physically
present in the bundled d.ts (rollup-plugin-dts inlines whatever the exported declarations
reference) but they are not exported from it. An author can call the API and rely on
contextual typing for an inline object literal, yet cannot write const opts: Renderer.K8sApi.KubeApiOptions<MyObject> = {...} or type a variable holding one. The
escape hatch is ConstructorParameters<typeof Renderer.K8sApi.KubeApi>[0], which is not a
reasonable thing to ask of extension authors.

Neither maintained extension hits this today: both freelens-fluxcd-extension and
freelens-gateway-api-extension declare their CRD APIs as empty subclasses
(export class GatewayClassApi extends Renderer.K8sApi.KubeApi<GatewayClass> {}) and never
pass options, so they never need to name an option type. The gap is latent, not blocking —
but it is the same defect, and the fix is the same.

Tasks

  • Add export * from "@freelensapp/kube-object" to the K8sApi namespace, resolving
    the KubeObjectStatus clash (rename the kube-object one on export, or keep an
    explicit exclusion list — decide which reads better in the d.ts).
  • Drop the now-redundant explicit re-export block in common-api/k8s-api.ts, keeping
    only the deliberate v1-compat aliases (SecretReference as ISecretRef,
    PodContainerStatus as IPodContainerStatus, …).
  • Confirm @freelensapp/kube-object still inlines into the bundled
    extension-api.d.ts rather than becoming a declared dependency — it is a private
    workspace package and rollup.dts.config.mjs maps every @freelensapp/* specifier
    to its emitted declarations. Blocked on @freelensapp/extensions build:dist is broken: TypeScript 7 removed the JS compiler API #2363.
  • Verify against both extensions that dropping their direct @freelensapp/kube-object
    dependency still type-checks.
  • Do the same for @freelensapp/kube-api: export the option and descriptor types that
    appear in already-exported signatures (KubeApiOptions, DerivedKubeApiOptions,
    KubeObjectStoreOptions, IKubeWatchEvent, KubeApiListOptions, DeleteOptions,
    PropagationPolicy, ResourceDescriptor, KubeApiQueryParams), plus parseKubeApi
    and createKubeApiURL.
  • Add a check that no exported signature references a type the bundle does not also
    export — this class of gap is mechanical and should not depend on someone noticing.
  • Record the decision in the namespace-enumeration contract in Extensions API v2 specification (contracts + v1 to v2 migration howto) #2304, and note in the
    migration guide that @freelensapp/kube-object imports move to the API namespace.

Risk notes

  • Star-exporting 419 symbols enlarges the public API surface considerably, and everything
    in it becomes something the v2 spec promises. That is the intended trade — the
    alternative is extension authors depending on a private package — but the symbols are
    worth a skim for anything that should stay internal.
  • The bundled d.ts grows; worth measuring after @freelensapp/extensions build:dist is broken: TypeScript 7 removed the JS compiler API #2363 is fixed.
  • Adding Condition and ObjectReference as top-level names in K8sApi may shadow
    same-named local types in existing extensions. Type-only, so the failure mode is a
    compile error in the extension, not a runtime break — but it belongs in the migration
    notes.

Found via #2360. Contract lives in #2304.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions