> ## Documentation Index
> Fetch the complete documentation index at: https://knitkit.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# @knitkit/runtime — Full Browser Runtime API Reference

> Full API reference for @knitkit/runtime: registerRemotes, loadRemote, negotiateShared, injectImportMap, validateManifest, getShareInfo, and error types.

The `@knitkit/runtime` package is the browser-side core of knitkit. It fetches remote manifests, negotiates shared dependency versions, injects a native ES module import map, and exposes lazy module loading — all in under 5 KB with zero dependencies. Import from `@knitkit/runtime` in your host application's entry point; every export below is available from that single specifier.

***

## `registerRemotes(remotes, options?)`

`registerRemotes` is the primary entry point for a host application. It fetches (or accepts inline) each remote's manifest, runs shared-dependency version negotiation, injects the resulting import map into the document, and stores registrations so that subsequent `loadRemote` calls can resolve module URLs. Call it once, before any `loadRemote` call or `<script type="module">` that imports federated modules.

```ts theme={null}
registerRemotes(
  remotes: RegisterRemotesInput[],
  options?: RegisterRemotesOptions
): Promise<NegotiationResult>
```

### Parameters

<ParamField body="remotes" type="RegisterRemotesInput[]" required>
  An array of remote descriptors. Each entry identifies a remote by name and tells the runtime where to find its manifest.

  <Expandable title="RegisterRemotesInput fields">
    <ParamField body="name" type="string" required>
      The logical name of the remote. This becomes the prefix used in `loadRemote` specifiers — e.g. a remote named `"checkout"` is loaded as `loadRemote("checkout/CartWidget")`.
    </ParamField>

    <ParamField body="manifest" type="string | Manifest" required>
      Either an absolute or relative URL string pointing to the remote's `knit.manifest.json`, or an already-parsed `Manifest` object for inline/pre-fetched use. When a URL string is provided, the runtime fetches it with `credentials: "same-origin"` and uses the resolved response URL as the base for relative asset paths.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="options" type="RegisterRemotesOptions">
  Optional configuration for the host side of negotiation.

  <Expandable title="RegisterRemotesOptions fields">
    <ParamField body="options.hostShared" type="Record<string, HostSharedDecl>">
      A map of package name to the host's own copy of that shared dependency. Declare this when the host itself has bundled or loaded a shared package (e.g. React) and wants it to participate in version negotiation.

      <Expandable title="HostSharedDecl fields">
        <ParamField body="version" type="string" required>
          The exact semver version the host provides (e.g. `"18.3.1"`).
        </ParamField>

        <ParamField body="requiredVersion" type="string" required>
          The semver range the host requires from any shared copy (e.g. `"^18.2.0"`).
        </ParamField>

        <ParamField body="singleton" type="boolean">
          When `true` (the default), a version conflict with any remote throws `KNIT_ERR_SINGLETON_CONFLICT` rather than falling back to a scoped copy.
        </ParamField>

        <ParamField body="url" type="string" required>
          URL to the host's prebundled ESM asset for this package.
        </ParamField>

        <ParamField body="integrity" type="string">
          Optional SRI hash (`sha384-...`) for the asset.
        </ParamField>

        <ParamField body="importAs" type="string">
          The package name as the host references it in the import map. Defaults to the key name.
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

### Returns

`Promise<NegotiationResult>` — resolves once all manifests are fetched and the import map has been injected.

<ResponseField name="winners" type="Record<string, { version: string; url: string; integrity?: string; source: string }>">
  For each shared package, the single winning version that was selected and the URL that will be used for it. `source` is the remote name (or `"host"`) that contributed the winning copy.
</ResponseField>

<ResponseField name="scopes" type="Record<string, Record<string, string>>">
  Fallback import map scopes for any non-singleton packages where a participant's required version range could not be satisfied by the winner. Each key is a URL scope prefix; its value maps package names to the scoped fallback URL.
</ResponseField>

<ResponseField name="importMap" type="ImportMap">
  The fully computed import map payload — `{ imports, scopes?, integrity? }` — that was injected into the document. Useful for server-side rendering where you need to serialize the map into HTML.
</ResponseField>

<ResponseField name="report" type="ResolutionReport">
  A human-readable resolution report keyed by package name. Each entry contains the winner details, any fallbacks, and a boolean `conflict` flag. Inspect this with `getShareInfo()` during debugging.
</ResponseField>

### Example

```ts theme={null}
import { registerRemotes } from "@knitkit/runtime";

await registerRemotes(
  [
    { name: "checkout", manifest: "https://cdn.example.com/checkout/knit.manifest.json" },
    { name: "recommendations", manifest: "https://cdn.example.com/recs/knit.manifest.json" },
  ],
  {
    hostShared: {
      react: {
        version: "18.3.1",
        requiredVersion: "^18.2.0",
        singleton: true,
        url: "/static/shared/react-18.3.1.js",
      },
    },
  },
);
```

***

## `loadRemote(specifier)`

`loadRemote` dynamically imports an exposed module from a previously registered remote. It resolves the module URL from the stored registration, delegates to the native `import()` function, and returns the module's `default` export when one is present, or the full namespace object otherwise.

```ts theme={null}
loadRemote<K extends keyof RemoteModules>(specifier: K): Promise<RemoteModules[K]>
loadRemote<T = unknown>(specifier: string): Promise<T>
```

### Parameters

<ParamField body="specifier" type="string" required>
  A string of the form `"<remoteName>/<exposeKey>"`. The `./` prefix on the expose key is optional — `"checkout/CartWidget"` and `"checkout/./CartWidget"` resolve identically. The `remoteName` must match a name passed to `registerRemotes`.
</ParamField>

### Returns

`Promise<T>` — the module's `default` export if the loaded module has one, otherwise the full ES module namespace object.

### Type safety

When you run `knitkit types sync`, the command generates a declaration file that augments the `RemoteModules` interface in `@knitkit/runtime`. After that, specifiers listed in `RemoteModules` are fully typed; unregistered specifiers fall back to `unknown`.

### Example

```ts theme={null}
import { loadRemote } from "@knitkit/runtime";

// Returns CartWidget's default export, typed if RemoteModules is augmented.
const CartWidget = await loadRemote("checkout/CartWidget");

// Generic override for untyped cases.
const data = await loadRemote<{ items: string[] }>("inventory/ProductList");
```

### Error codes

| Code                      | When thrown                                                                                                                                                          |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `KNIT_ERR_NOT_REGISTERED` | The `remoteName` portion of the specifier has not been registered via `registerRemotes`.                                                                             |
| `KNIT_ERR_LOAD_FAILED`    | The specifier format is invalid, the expose key does not exist in the remote's manifest, or the underlying `import()` call fails (network error, CORS, parse error). |

***

## `getLastResult()`

`getLastResult` returns the `NegotiationResult` produced by the most recent `registerRemotes` call, or `null` if `registerRemotes` has not been called yet. Use this for post-boot inspection or logging without needing to hold a reference to the `registerRemotes` promise.

```ts theme={null}
getLastResult(): NegotiationResult | null
```

***

## `getShareInfo()`

`getShareInfo` returns the `ResolutionReport` from the last negotiation. The report is a flat, human-readable summary of which version won for each package, which participants fell back to a scoped copy, and whether any conflicts were detected. Unlike `getLastResult`, this function always returns a valid object (an empty report when negotiation has not run).

```ts theme={null}
getShareInfo(): ResolutionReport
```

### Example

```ts theme={null}
import { getShareInfo } from "@knitkit/runtime";

const report = getShareInfo();
for (const [pkg, info] of Object.entries(report.packages)) {
  console.log(`${pkg}@${info.winner.version} from ${info.winner.source}`);
  if (info.conflict) {
    console.warn(`  ↳ ${info.fallbacks.length} fallback(s)`);
  }
}
```

***

## `negotiateShared(manifests, hostShared?)`

`negotiateShared` is the low-level version negotiation engine. `registerRemotes` calls it internally — most application code never needs to call it directly. It is re-exported from `@knitkit/node` for server-side rendering scenarios where you want to run negotiation in Node.js without touching the DOM.

```ts theme={null}
negotiateShared(
  manifests: Array<{ name: string; manifest: Manifest; baseUrl: string }>,
  hostShared?: Record<string, HostSharedDecl>,
  hostBaseUrl?: string,
): NegotiationResult
```

### Parameters

<ParamField body="manifests" type="Array<{ name: string; manifest: Manifest; baseUrl: string }>" required>
  Parsed manifests to negotiate. `baseUrl` is the absolute URL of the manifest document; relative `shared[].url` values are resolved against it.
</ParamField>

<ParamField body="hostShared" type="Record<string, HostSharedDecl>">
  Optional host-side shared declarations, identical in shape to the `options.hostShared` accepted by `registerRemotes`.
</ParamField>

<ParamField body="hostBaseUrl" type="string">
  Optional base URL for resolving relative URLs within `hostShared` entries. Defaults to `""`.
</ParamField>

### Returns

`NegotiationResult` — synchronously; no network I/O is performed.

***

## `injectImportMap(importMap)`

`injectImportMap` merges a computed import map into the document's `<script type="importmap">` element. If no such element exists it creates one and appends it to `<head>`. If one already exists it merges `imports`, `scopes`, and `integrity` objects into the existing map.

```ts theme={null}
injectImportMap(importMap: ImportMap): void
```

<ParamField body="importMap" type="ImportMap" required>
  An object with `imports` (required), `scopes` (optional), and `integrity` (optional) keys — the standard import map shape.
</ParamField>

<Warning>
  Call `injectImportMap` before any `<script type="module">` that resolves specifiers through the map. Native import maps are immutable once a browser's module graph has started loading, and injecting a map after that point has no effect on already-started loads.
</Warning>

<Note>
  `injectImportMap` requires a DOM (`document` must be defined). In Node.js or edge runtimes, it throws `KNIT_ERR_IMPORT_MAP_INJECTION_FAILED`. Use `@knitkit/node` for server-side rendering, which handles import map serialization without touching the DOM.
</Note>

***

## `validateManifest(input, sourceLabel)`

`validateManifest` parses and validates an unknown value against the knitkit manifest spec. It checks the `spec` version, name format, `exposes` and `shared` structure, semver validity, and field types. On success it returns a fully-typed `Manifest` object; on failure it throws a `FedkitError`.

```ts theme={null}
validateManifest(input: unknown, sourceLabel: string): Manifest
```

<ParamField body="input" type="unknown" required>
  The raw parsed JSON (e.g. the result of `JSON.parse`). Pass it before using any manifest fields so you get structured error messages rather than runtime property access failures.
</ParamField>

<ParamField body="sourceLabel" type="string" required>
  A human-readable label included in error messages — typically the manifest URL or file path. Used for diagnostics only.
</ParamField>

Throws `FedkitError` with code `KNIT_ERR_MANIFEST_INVALID` when any field is missing or invalid. The error's `.message` identifies the specific field and the `.suggestion` field provides a corrective action.

### Example

```ts theme={null}
import { validateManifest, isFedkitError } from "@knitkit/runtime";

const raw = await fetch("/federation/knit.manifest.json").then((r) => r.json());
try {
  const manifest = validateManifest(raw, "/federation/knit.manifest.json");
  console.log("Loaded manifest for remote:", manifest.name);
} catch (e) {
  if (isFedkitError(e)) {
    console.error(e.message);
    console.info("Suggestion:", e.suggestion);
  }
}
```

***

## `FedkitError` and `isFedkitError`

All errors thrown by `@knitkit/runtime` are instances of `FedkitError`. Use `isFedkitError` to distinguish them from unexpected runtime errors in a catch block.

```ts theme={null}
class FedkitError extends Error {
  readonly code: FedkitErrorCode;
  readonly suggestion?: string;
}

function isFedkitError(e: unknown): e is FedkitError
```

### `FedkitError` fields

<ResponseField name="code" type="FedkitErrorCode">
  A stable string code identifying the error category. See the full list below.
</ResponseField>

<ResponseField name="message" type="string">
  A human-readable description including the specific package name, URL, or field that caused the error.
</ResponseField>

<ResponseField name="suggestion" type="string | undefined">
  An actionable suggestion for resolving the error, when one is available.
</ResponseField>

### Error codes

| Code                                   | Thrown by                       | Cause                                                                                                           |
| -------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `KNIT_ERR_NEGOTIATION_CONFLICT`        | `negotiateShared`               | Reserved for future multi-phase conflict detection.                                                             |
| `KNIT_ERR_SINGLETON_CONFLICT`          | `negotiateShared`               | A `singleton: true` package has no version that satisfies every participant's `requiredVersion` range.          |
| `KNIT_ERR_MANIFEST_INVALID`            | `validateManifest`              | A manifest field is missing, has the wrong type, or contains an invalid semver string or range.                 |
| `KNIT_ERR_LOAD_FAILED`                 | `loadRemote`, `registerRemotes` | Manifest fetch failed (network error or non-2xx status), or the underlying `import()` call for a module failed. |
| `KNIT_ERR_NOT_REGISTERED`              | `loadRemote`                    | The remote name in the specifier has not been registered via `registerRemotes`.                                 |
| `KNIT_ERR_IMPORT_MAP_INJECTION_FAILED` | `injectImportMap`               | `document` is not defined (non-browser environment).                                                            |

### Example

```ts theme={null}
import { registerRemotes, isFedkitError } from "@knitkit/runtime";

try {
  await registerRemotes([{ name: "checkout", manifest: "/federation/knit.manifest.json" }]);
} catch (e) {
  if (isFedkitError(e)) {
    switch (e.code) {
      case "KNIT_ERR_SINGLETON_CONFLICT":
        console.error("Version conflict — align shared dep versions across remotes.");
        break;
      case "KNIT_ERR_LOAD_FAILED":
        console.error("Could not reach manifest:", e.message);
        if (e.suggestion) console.info(e.suggestion);
        break;
      default:
        console.error(`[${e.code}] ${e.message}`);
    }
  } else {
    throw e;
  }
}
```
