Skip to content

Frontend: one public entry point (public-api) with an exports map for extensions #886

Closed

Description

@smart-ssh-planner

Goal

The frontend has one public entry point for extensions and embedding apps: src/public-api.ts, reachable by package name (smart-ssh/public-api) through an exports map. Everything an extension may use is exported there; internal module paths are no longer part of the contract, so renaming an internal file does not break anyone outside this repository.

Context

  • apps/smart-ssh-community/frontend/package.json (name smart-ssh) has no exports map. Code outside the frontend that builds on it can only import internal files by relative path (…/frontend/src/api, …/src/components/ui, …).
  • Items such code needs today, by module:
    • extensions (registry functions, useEntitlements, publishFeatureLocked, FeatureLockedDialog, ModalBackdrop, types) – src/extensions/index.ts
    • extensions/entitlements (Entitlements, Feature, Tier, FeatureLockedPayload)
    • api (commandErrorMessage, commandErrorCode, commandErrorFeatureLocked, listServers, getStartupState) – the central invoke wrapper api.ts:95-106 (reports feature_locked to the dialog bus) is not exported, so callers use the raw invoke and lose the locked-feature dialog
    • components/ui (Button, Field, FIELD_CONTROL_CLASS, …), ErrorBoundary (Frontend: isolate every extension contribution and the app root with an error boundary #880)
    • technicalInputProps (TECHNICAL_INPUT_PROPS), hooks/useIsNarrow, resolveSectionLabel, fileDialog (pickAndReadTextFile), languages (SUPPORTED_LANGUAGES), i18n (settingsStore, initI18n), types (DTO types such as AiProviderConfigDto, ChatSessionSummaryDto, StartupStateDto)
    • bootstrap (Frontend: move the start sequence into an exported bootstrap() for embedding apps #882)
    • in tests: test/matchMedia (installMatchMediaMock, setWide)
  • The reactive registry functions come from Extension registry: make document actions and first-run notice extensions reactive #884.

Requirements

  • New src/public-api.ts: a header doc comment (English) saying this is the only supported import surface for extensions and embedding apps, with the stability rule (exports change only with a changelog entry and a spec update). It re-exports:
    • everything exported by src/extensions/index.ts
    • entitlement types from extensions/entitlements
    • bootstrap and BootstrapOptions
    • ErrorBoundary and the components/ui building blocks exported by components/ui/index.ts
    • invokeCommand<T>(cmd, args?) – the central wrapper from api.ts:95-106, exported under this name (same behaviour: reports feature_locked to the dialog bus, rethrows unchanged)
    • commandErrorMessage, commandErrorCode, commandErrorFeatureLocked, commandErrorConnectLog, listServers, getStartupState
    • TECHNICAL_INPUT_PROPS, useIsNarrow, resolveSectionLabel, pickAndReadTextFile, SUPPORTED_LANGUAGES, settingsStore, initI18n
    • the DTO types listed under Context (type-only exports)
  • New src/test-support.ts re-exporting the test helpers installMatchMediaMock, setWide (and nothing used by production code).
  • package.json gets an exports map with exactly "./public-api": "./src/public-api.ts" and "./test-support": "./src/test-support.ts"; the app's own build, dev server, vitest and Playwright keep working (they do not import by package name).
  • A vitest guard src/publicApi.test.ts asserts that every name in an explicit list (the list above) is exported by public-api with the expected kind (function/component/object), so removing or renaming an export fails the test.
  • Spec 0038 section 4 lists smart-ssh/public-api as the supported surface and states that relative imports of internal files are not supported.

Acceptance Criteria

  • src/public-api.ts and src/test-support.ts exist with the exports above.
  • invokeCommand behaves like the internal wrapper: test that a rejected call with feature_locked publishes on the feature-locked bus and rethrows the same error object; a rejection without it publishes nothing.
  • package.json exports contains exactly the two entries; npm run build, npm test, npm run test:e2e pass.
  • A small fixture in the test (import … from "smart-ssh/public-api" resolved through the workspace, or import.meta.resolve) proves the package-name import resolves.
  • src/publicApi.test.ts fails when one listed export is removed (counter-check documented in the test).
  • Spec 0038 section 4 updated.
  • Relevant automated tests pass (npm run lint, npm test, tsc -b).

Technical Notes

  • Files: src/public-api.ts, src/test-support.ts, src/publicApi.test.ts (new), src/api.ts (export the wrapper under invokeCommand, keep the internal name working), package.json, docs/specs/0038-repository-app-shell-ci.md.
  • Re-export only; do not move code. Keep extensions/index.ts as it is (public-api builds on it).
  • scripts/check-tauri-commands.mjs scans invoke("…") call sites; make sure exporting the wrapper does not change what it detects (run npm run check-commands).

Spec impact: update 0038

Open questions

  • none

Related Work

Activity

  1. added this to the 0.8.0 milestone on Oct 26, 2026
  2. smart-ssh-coder commented on Oct 28, 2026

    @smart-ssh-coder
    Contributor

    Implementation ready for review. PR #999. Added src/public-api.ts, src/test-support.ts, invokeCommand export and the package exports map, plus publicApi.test.ts (export list guard, invokeCommand behaviour, package-name resolution) and Spec 0038 section 4 update. Lint, tsc, vitest green locally; CI green on PR #999.

  3. smart-ssh-reviewer commented on Oct 28, 2026

    @smart-ssh-reviewer

    Review passed – moving on to merge. Approved: public-api and test-support entry points, the exports map, invokeCommand and the Spec 0038 update match the issue. The guard test goes red when an export is removed, and CI is green.

  4. smart-ssh-coordinator commented on Oct 28, 2026

    @smart-ssh-coordinator
    Contributor

    Merged and branch deleted: PR #999 (squash) in develop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfrontendFrontend (React/TypeScript)

    Type

    Fields

    Priority

    P2 - Normal

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions