# core/converters/search.converter.ts

> 37 lines of code and 16 definitions.

Tree: Bane's Lab Deploy
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/deploy#file-deploy-core-converters-search-converter-ts
Source text: https://banes-lab.com/source/deploy/core/converters/search.converter.ts.txt

Listed in [core/converters](https://banes-lab.com/api/source/deploy/core/converters.md), after [core/converters/nginx.converter.ts](https://banes-lab.com/source/deploy/core/converters/nginx.converter.ts.md) and before [core/converters/text.converter.ts](https://banes-lab.com/source/deploy/core/converters/text.converter.ts.md).

## Definitions

- `stampsOf` (lexical_declaration, line 17, exported)
- `taggedText` (lexical_declaration, line 8)
- `changedLocations` (lexical_declaration, line 35, exported)
- `URL_OPEN` (lexical_declaration, line 1)
- `URL_CLOSE` (lexical_declaration, line 2)
- `LOC_OPEN` (lexical_declaration, line 3)
- `LOC_CLOSE` (lexical_declaration, line 4)
- `LASTMOD_OPEN` (lexical_declaration, line 5)
- `LASTMOD_CLOSE` (lexical_declaration, line 6)
- `start` (lexical_declaration, line 9)
- `stamps` (lexical_declaration, line 18, exported)
- `cursor` (lexical_declaration, line 19, exported)
- `end` (lexical_declaration, line 21, exported)
- `entry` (lexical_declaration, line 25, exported)
- `location` (lexical_declaration, line 26, exported)
- `held` (lexical_declaration, line 36, exported)

## Contained in

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

## Linked from

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

## Source

```typescript
const URL_OPEN = "<url>";
const URL_CLOSE = "</url>";
const LOC_OPEN = "<loc>";
const LOC_CLOSE = "</loc>";
const LASTMOD_OPEN = "<lastmod>";
const LASTMOD_CLOSE = "</lastmod>";

const taggedText = function taggedText(text: string, open: string, close: string): string | null {
    const start = text.indexOf(open);
    if (start === -1) {
        return null;
    }
    const end = text.indexOf(close, start + open.length);
    return end === -1 ? null : text.slice(start + open.length, end);
};

export const stampsOf = function stampsOf(xml: string): ReadonlyMap<string, string | null> {
    const stamps = new Map<string, string | null>();
    let cursor = xml.indexOf(URL_OPEN);
    while (cursor !== -1) {
        const end = xml.indexOf(URL_CLOSE, cursor);
        if (end === -1) {
            break;
        }
        const entry = xml.slice(cursor, end);
        const location = taggedText(entry, LOC_OPEN, LOC_CLOSE);
        if (location !== null) {
            stamps.set(location, taggedText(entry, LASTMOD_OPEN, LASTMOD_CLOSE));
        }
        cursor = xml.indexOf(URL_OPEN, end);
    }
    return stamps;
};

export const changedLocations = function changedLocations(before: string | null, after: string): readonly string[] {
    const held = before === null ? new Map<string, string | null>() : stampsOf(before);
    return [...stampsOf(after)]
        .filter(([location, lastmod]) => !held.has(location) || held.get(location) !== lastmod)
        .map(([location]) => location);
};
```
