# core/plugins/manifest.surface.plugin.ts

> 43 lines of code and 11 definitions.

Tree: GovLab Docs
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-plugins-manifest-surface-plugin-ts
Source text: https://banes-lab.com/source/docs/core/plugins/manifest.surface.plugin.ts.txt

Listed in [core/plugins](https://banes-lab.com/api/source/docs/core/plugins.md), after [core/plugins/manifest.scope.plugin.ts](https://banes-lab.com/source/docs/core/plugins/manifest.scope.plugin.ts.md) and before [core/plugins/syntax.branch.plugin.ts](https://banes-lab.com/source/docs/core/plugins/syntax.branch.plugin.ts.md).

## Definitions

- `coversErrors` (lexical_declaration, line 23)
- `surfaceIds` (lexical_declaration, line 9)
- `coverageGap` (lexical_declaration, line 18, exported)
- `validate` (method_definition, line 44, exported)
- `SECTION` (lexical_declaration, line 6)
- `known` (lexical_declaration, line 7)
- `declared` (lexical_declaration, line 19, exported)
- `ids` (lexical_declaration, line 30)
- `plugin` (lexical_declaration, line 34, exported)
- `contribute` (method_definition, line 35, exported)
- `covers` (lexical_declaration, line 36, exported)

## Contained in

- [core/plugins](https://banes-lab.com/anatomy/docs/folder-docs-core-plugins.md)

## Uses

- [core/selectors/record.selector.ts](https://banes-lab.com/source/docs/core/selectors/record.selector.ts.md)

## Linked from

- [core/plugins](https://banes-lab.com/anatomy/docs/folder-docs-core-plugins.md)
- [core/selectors](https://banes-lab.com/anatomy/docs/folder-docs-core-selectors.md)

## Source

```typescript
import { MANIFEST_ERRORS, unknownSurface } from "#configuration/strings/manifest.strings";
import { isNonEmptyStringArray, stringsOf } from "#core/selectors/record.selector";
import type { ManifestPlugin } from "#types/manifest.types";
import { createReason } from "@govlab/context";

const SECTION = "coversSurfaces";
const known: { ids?: ReadonlySet<string> } = {};

const surfaceIds = function surfaceIds(): ReadonlySet<string> {
    known.ids ??= new Set(
        createReason()
            .testSurfaces()
            .map((surface) => surface.id),
    );
    return known.ids;
};

export const coverageGap = function coverageGap(covered: unknown): string[] {
    const declared = new Set(stringsOf(covered));
    return [...surfaceIds()].filter((id) => !declared.has(id)).toSorted((left, right) => left.localeCompare(right));
};

const coversErrors = function coversErrors(covers: unknown): string[] {
    if (covers === undefined) {
        return [];
    }
    if (!isNonEmptyStringArray(covers)) {
        return [MANIFEST_ERRORS.coversShape];
    }
    const ids = surfaceIds();
    return ids.size === 0 ? [] : covers.filter((id) => !ids.has(id)).map(unknownSurface);
};

export const plugin: ManifestPlugin = {
    contribute(manifest, entry) {
        const covers = stringsOf(manifest[SECTION]);
        if (covers.length > 0) {
            entry[SECTION] = covers;
        }
    },
    name: "covers-surfaces",
    section: {
        keys: [SECTION],
        validate(manifest) {
            return coversErrors(manifest[SECTION]);
        },
    },
};
```
