@galaxy-foundry/content-reader 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +139 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 John Chilton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @galaxy-foundry/content-reader
|
|
2
|
+
|
|
3
|
+
The headless content-reading plumbing shared by Foundry-pattern instances.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pnpm add @galaxy-foundry/content-reader
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
An instance owns its kinds, zod schemas, collection table, routes, registries, identity, theme,
|
|
10
|
+
and domain-specific page furniture. Once it has those, every content site asks the same mechanical
|
|
11
|
+
questions: which files does a collection select, what are their route IDs, which notes may wiki
|
|
12
|
+
links target, and how does the Markdown renderer bind those links?
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { createContentReader } from '@galaxy-foundry/content-reader';
|
|
16
|
+
|
|
17
|
+
export const contentReader = createContentReader({
|
|
18
|
+
collections: COLLECTIONS,
|
|
19
|
+
contentPath,
|
|
20
|
+
aliases: (meta, id, collection) =>
|
|
21
|
+
meta.type === 'cli-command' && typeof meta.tool === 'string' && typeof meta.command === 'string'
|
|
22
|
+
? [`${meta.tool} ${meta.command}`]
|
|
23
|
+
: meta.type === 'mold' && typeof meta.name === 'string'
|
|
24
|
+
? [meta.name]
|
|
25
|
+
: [],
|
|
26
|
+
targetOf: (collection, id, meta) => {
|
|
27
|
+
const target = { path: `${collection}/${id}` };
|
|
28
|
+
return typeof meta?.summary === 'string' ? { ...target, title: meta.summary } : target;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
contentReader.noteFiles('papers');
|
|
33
|
+
contentReader.noteIds('papers');
|
|
34
|
+
contentReader.wikiLinkMap();
|
|
35
|
+
contentReader.remarkWikiLinks({ base: '/my-foundry' });
|
|
36
|
+
contentReader.resolveMarkdown(source, { base: '/my-foundry' });
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Extra content targets, such as design documents outside the typed collection table, are passed as
|
|
40
|
+
`{ key, target }` entries.
|
|
41
|
+
|
|
42
|
+
`aliases` is the instance vocabulary seam: the package reads each routed note's YAML frontmatter
|
|
43
|
+
once and registers the returned second addresses. The same frontmatter is passed to `targetOf`, so
|
|
44
|
+
route targets can carry a `summary` tooltip without another filesystem walk. Readers that omit
|
|
45
|
+
`aliases` continue to touch directory entries only; set `readFrontmatter: true` when `targetOf`
|
|
46
|
+
needs metadata but no aliases are required.
|
|
47
|
+
|
|
48
|
+
Address precedence is deterministic:
|
|
49
|
+
|
|
50
|
+
1. Primary note addresses are registered in collection property order, then sorted note-path
|
|
51
|
+
order. A later primary overwrites an earlier primary, so collection declaration order is a
|
|
52
|
+
contractual part of a corpus with basename collisions.
|
|
53
|
+
2. Aliases fill empty addresses and never overwrite a primary. The first routed note wins when
|
|
54
|
+
two aliases collide.
|
|
55
|
+
3. Explicit `extraTargets` are applied last and may deliberately override either.
|
|
56
|
+
|
|
57
|
+
Only files admitted by the collection table become primary or alias targets. Markdown companions
|
|
58
|
+
and other adjacent files remain visible to `markdownFiles()` but cannot leak into `wikiLinkMap()`.
|
|
59
|
+
|
|
60
|
+
## Boundary
|
|
61
|
+
|
|
62
|
+
This package deliberately does not assemble zod schemas or Astro collections. Astro preserves the
|
|
63
|
+
frontmatter type of a collection only when each schema-bearing export is written out; mapping a
|
|
64
|
+
heterogeneous catalog collapses the inferred shapes. It also does not decide how a Package differs
|
|
65
|
+
from a Paper or how either is rendered. Those are the instance's content model.
|
|
66
|
+
|
|
67
|
+
The package touches the filesystem, so it belongs in build, validation, and Astro server code—not
|
|
68
|
+
browser bundles. Reusable Astro presentation lives in `@galaxy-foundry/site-kit`.
|
|
69
|
+
|
|
70
|
+
See the [content-reader boundary](../../docs/architecture/content-reader-boundary.md) for the
|
|
71
|
+
cross-package and instance ownership map.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type CollectionTable } from '@galaxy-foundry/kind-schema/collections';
|
|
2
|
+
import remarkWikiLinks from '@galaxy-foundry/wiki-links/remark';
|
|
3
|
+
export interface ContentTarget {
|
|
4
|
+
/** Site-relative path without the deployment base or trailing slash. */
|
|
5
|
+
path: string;
|
|
6
|
+
/** Optional tooltip carried by rendered wiki links. */
|
|
7
|
+
title?: string;
|
|
8
|
+
}
|
|
9
|
+
/** A note's YAML frontmatter before an instance schema has parsed it. */
|
|
10
|
+
export type Frontmatter = Record<string, unknown>;
|
|
11
|
+
/** Extra wiki-link addresses derived from an instance's own note vocabulary. */
|
|
12
|
+
export type ContentAliases<Collections extends CollectionTable> = (meta: Frontmatter, id: string, collection: keyof Collections & string) => readonly string[];
|
|
13
|
+
export interface ExtraContentTarget<Target extends ContentTarget = ContentTarget> {
|
|
14
|
+
/** Author-facing key before wiki-link slugification. */
|
|
15
|
+
key: string;
|
|
16
|
+
target: Target;
|
|
17
|
+
}
|
|
18
|
+
export interface ContentLink {
|
|
19
|
+
href: string | null;
|
|
20
|
+
label: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ContentLinkOptions<Target extends ContentTarget> {
|
|
23
|
+
base?: string;
|
|
24
|
+
extraTargets?: readonly ExtraContentTarget<Target>[];
|
|
25
|
+
}
|
|
26
|
+
export interface ContentReaderOptions<Collections extends CollectionTable, Target extends ContentTarget> {
|
|
27
|
+
/**
|
|
28
|
+
* Routed collections in primary-address precedence order. When two notes have the same
|
|
29
|
+
* primary slug, the later collection wins.
|
|
30
|
+
*/
|
|
31
|
+
collections: Collections;
|
|
32
|
+
/** Resolve a content-relative path to the filesystem frame used by this process. */
|
|
33
|
+
contentPath: (relativePath: string) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Read YAML frontmatter even when no aliases are configured. Supplying aliases already opts
|
|
36
|
+
* into frontmatter reads.
|
|
37
|
+
*/
|
|
38
|
+
readFrontmatter?: boolean;
|
|
39
|
+
/** Instance-owned second addresses for a note. Aliases never overwrite a primary address. */
|
|
40
|
+
aliases?: ContentAliases<Collections>;
|
|
41
|
+
/** Map a typed note to its content route. Route policy stays with the instance. */
|
|
42
|
+
targetOf: (collection: keyof Collections & string, id: string, meta: Frontmatter | undefined) => Target;
|
|
43
|
+
}
|
|
44
|
+
export interface ContentReader<Collections extends CollectionTable, Target extends ContentTarget> {
|
|
45
|
+
markdownFiles(): string[];
|
|
46
|
+
noteFiles<Name extends keyof Collections & string>(name: Name): string[];
|
|
47
|
+
noteIds<Name extends keyof Collections & string>(name: Name): string[];
|
|
48
|
+
wikiLinkMap(extraTargets?: readonly ExtraContentTarget<Target>[]): Map<string, Target>;
|
|
49
|
+
resolveLink(value: string, options?: ContentLinkOptions<Target>): ContentLink;
|
|
50
|
+
remarkWikiLinks(options?: ContentLinkOptions<Target>): ReturnType<typeof remarkWikiLinks>;
|
|
51
|
+
resolveMarkdown(source: string, options?: ContentLinkOptions<Target>): string;
|
|
52
|
+
}
|
|
53
|
+
/** Remove the two note layouts from a collection-relative path. */
|
|
54
|
+
export declare function noteIdFromPath(relativePath: string): string;
|
|
55
|
+
export declare function resolveContentLink<Target extends ContentTarget>(value: string, map: ReadonlyMap<string, Target>, base?: string): ContentLink;
|
|
56
|
+
export declare function remarkContentWikiLinks<Target extends ContentTarget>(map: ReadonlyMap<string, Target>, base?: string): ReturnType<typeof remarkWikiLinks>;
|
|
57
|
+
export declare function resolveContentMarkdown<Target extends ContentTarget>(source: string, map: ReadonlyMap<string, Target>, base?: string): string;
|
|
58
|
+
export declare function createContentReader<Collections extends CollectionTable, Target extends ContentTarget = ContentTarget>(options: ContentReaderOptions<Collections, Target>): ContentReader<Collections, Target>;
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAOlG,OAAO,eAAe,MAAM,mCAAmC,CAAC;AAEhE,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,yEAAyE;AACzE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAElD,gFAAgF;AAChF,MAAM,MAAM,cAAc,CAAC,WAAW,SAAS,eAAe,IAAI,CAChE,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,WAAW,GAAG,MAAM,KACnC,SAAS,MAAM,EAAE,CAAC;AAEvB,MAAM,WAAW,kBAAkB,CAAC,MAAM,SAAS,aAAa,GAAG,aAAa;IAC9E,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB,CAAC,MAAM,SAAS,aAAa;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,SAAS,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB,CACnC,WAAW,SAAS,eAAe,EACnC,MAAM,SAAS,aAAa;IAE5B;;;OAGG;IACH,WAAW,EAAE,WAAW,CAAC;IACzB,oFAAoF;IACpF,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,6FAA6F;IAC7F,OAAO,CAAC,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;IACtC,mFAAmF;IACnF,QAAQ,EAAE,CACR,UAAU,EAAE,MAAM,WAAW,GAAG,MAAM,EACtC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,WAAW,GAAG,SAAS,KAC1B,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa,CAAC,WAAW,SAAS,eAAe,EAAE,MAAM,SAAS,aAAa;IAC9F,aAAa,IAAI,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,IAAI,SAAS,MAAM,WAAW,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAAC;IACzE,OAAO,CAAC,IAAI,SAAS,MAAM,WAAW,GAAG,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAAC;IACvE,WAAW,CAAC,YAAY,CAAC,EAAE,SAAS,kBAAkB,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvF,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;IAC9E,eAAe,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;IAC1F,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CAC/E;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE3D;AAsCD,wBAAgB,kBAAkB,CAAC,MAAM,SAAS,aAAa,EAC7D,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,IAAI,SAAK,GACR,WAAW,CAQb;AAED,wBAAgB,sBAAsB,CAAC,MAAM,SAAS,aAAa,EACjE,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,IAAI,SAAK,GACR,UAAU,CAAC,OAAO,eAAe,CAAC,CAOpC;AAED,wBAAgB,sBAAsB,CAAC,MAAM,SAAS,aAAa,EACjE,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,IAAI,SAAK,GACR,MAAM,CAOR;AAED,wBAAgB,mBAAmB,CACjC,WAAW,SAAS,eAAe,EACnC,MAAM,SAAS,aAAa,GAAG,aAAa,EAC5C,OAAO,EAAE,oBAAoB,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG,aAAa,CAAC,WAAW,EAAE,MAAM,CAAC,CAwFxF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import yaml from 'js-yaml';
|
|
3
|
+
import { matchesCollection } from '@galaxy-foundry/kind-schema/collections';
|
|
4
|
+
import { parseWikiLink, resolveWikiLink, resolveWikiLinksInMarkdown, slugify, } from '@galaxy-foundry/wiki-links';
|
|
5
|
+
import remarkWikiLinks from '@galaxy-foundry/wiki-links/remark';
|
|
6
|
+
/** Remove the two note layouts from a collection-relative path. */
|
|
7
|
+
export function noteIdFromPath(relativePath) {
|
|
8
|
+
return relativePath.replace(/(?:\/index)?\.md$/, '');
|
|
9
|
+
}
|
|
10
|
+
const FRONTMATTER_RE = /^(?:\uFEFF)?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/;
|
|
11
|
+
const normalizeFrontmatterValue = (value) => {
|
|
12
|
+
if (value instanceof Date)
|
|
13
|
+
return value.toISOString().slice(0, 10);
|
|
14
|
+
if (Array.isArray(value))
|
|
15
|
+
return value.map(normalizeFrontmatterValue);
|
|
16
|
+
if (value !== null && typeof value === 'object') {
|
|
17
|
+
return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, normalizeFrontmatterValue(nested)]));
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
const readNoteFrontmatter = (filePath) => {
|
|
22
|
+
const source = fs.readFileSync(filePath, 'utf8');
|
|
23
|
+
const document = FRONTMATTER_RE.exec(source)?.[1];
|
|
24
|
+
if (document === undefined)
|
|
25
|
+
return {};
|
|
26
|
+
const parsed = yaml.load(document);
|
|
27
|
+
if (parsed === undefined || parsed === null)
|
|
28
|
+
return {};
|
|
29
|
+
if (typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
30
|
+
throw new TypeError(`${filePath}: YAML frontmatter must be a mapping`);
|
|
31
|
+
}
|
|
32
|
+
return normalizeFrontmatterValue(parsed);
|
|
33
|
+
};
|
|
34
|
+
const hrefFor = (base, target) => {
|
|
35
|
+
const normalizedBase = base.replace(/\/$/, '');
|
|
36
|
+
const normalizedPath = target.path.replace(/^\//, '').replace(/\/$/, '');
|
|
37
|
+
return `${normalizedBase}/${normalizedPath}/`;
|
|
38
|
+
};
|
|
39
|
+
const destinationFor = (base, target) => {
|
|
40
|
+
const href = hrefFor(base, target);
|
|
41
|
+
return target.title === undefined ? { href } : { href, title: target.title };
|
|
42
|
+
};
|
|
43
|
+
export function resolveContentLink(value, map, base = '') {
|
|
44
|
+
const parsed = parseWikiLink(value);
|
|
45
|
+
const target = resolveWikiLink(value, map);
|
|
46
|
+
const label = parsed?.display ?? parsed?.target ?? value;
|
|
47
|
+
return {
|
|
48
|
+
href: target ? `${hrefFor(base, target)}${parsed?.anchor ?? ''}` : null,
|
|
49
|
+
label,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function remarkContentWikiLinks(map, base = '') {
|
|
53
|
+
return remarkWikiLinks({
|
|
54
|
+
resolve: (link) => {
|
|
55
|
+
const target = resolveWikiLink(link.target, map);
|
|
56
|
+
return target ? destinationFor(base, target) : null;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export function resolveContentMarkdown(source, map, base = '') {
|
|
61
|
+
return resolveWikiLinksInMarkdown(source, {
|
|
62
|
+
resolve: (link) => {
|
|
63
|
+
const target = resolveWikiLink(link.target, map);
|
|
64
|
+
return target ? destinationFor(base, target) : null;
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export function createContentReader(options) {
|
|
69
|
+
const { aliases, collections, contentPath, readFrontmatter = false, targetOf } = options;
|
|
70
|
+
const walk = (directory) => {
|
|
71
|
+
const absoluteDirectory = contentPath(directory);
|
|
72
|
+
if (!fs.existsSync(absoluteDirectory))
|
|
73
|
+
return [];
|
|
74
|
+
const files = [];
|
|
75
|
+
for (const entry of fs.readdirSync(absoluteDirectory, { withFileTypes: true })) {
|
|
76
|
+
const relativePath = directory ? `${directory}/${entry.name}` : entry.name;
|
|
77
|
+
if (entry.isDirectory())
|
|
78
|
+
files.push(...walk(relativePath));
|
|
79
|
+
else if (entry.isFile())
|
|
80
|
+
files.push(relativePath);
|
|
81
|
+
}
|
|
82
|
+
return files;
|
|
83
|
+
};
|
|
84
|
+
const noteFiles = (name) => {
|
|
85
|
+
const row = collections[name];
|
|
86
|
+
return walk(row.base)
|
|
87
|
+
.filter((relativePath) => matchesCollection(relativePath, row))
|
|
88
|
+
.sort();
|
|
89
|
+
};
|
|
90
|
+
const noteIds = (name) => {
|
|
91
|
+
const prefix = `${collections[name].base}/`;
|
|
92
|
+
return noteFiles(name).map((relativePath) => noteIdFromPath(relativePath.slice(prefix.length)));
|
|
93
|
+
};
|
|
94
|
+
const wikiLinkMap = (extraTargets = []) => {
|
|
95
|
+
const map = new Map();
|
|
96
|
+
// Object property order is the collection-precedence contract. Keep primary registration in
|
|
97
|
+
// this first pass so no alias can take an address that belongs to a routed note. Within a
|
|
98
|
+
// collection noteFiles is sorted, making the full precedence deterministic.
|
|
99
|
+
const notes = [];
|
|
100
|
+
for (const name of Object.keys(collections)) {
|
|
101
|
+
const prefix = `${collections[name].base}/`;
|
|
102
|
+
for (const relativePath of noteFiles(name)) {
|
|
103
|
+
const id = noteIdFromPath(relativePath.slice(prefix.length));
|
|
104
|
+
const meta = aliases || readFrontmatter ? readNoteFrontmatter(contentPath(relativePath)) : undefined;
|
|
105
|
+
const target = targetOf(name, id, meta);
|
|
106
|
+
notes.push({ collection: name, id, meta, target });
|
|
107
|
+
map.set(slugify(id.replace(/\//g, '-')), target);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Aliases fill empty addresses only. In an alias/alias collision, the first routed note wins;
|
|
111
|
+
// in an alias/primary collision, the primary wins regardless of collection order.
|
|
112
|
+
if (aliases) {
|
|
113
|
+
for (const note of notes) {
|
|
114
|
+
for (const alias of aliases(note.meta, note.id, note.collection)) {
|
|
115
|
+
const key = slugify(alias);
|
|
116
|
+
if (!map.has(key))
|
|
117
|
+
map.set(key, note.target);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Explicit extra targets are the caller's escape hatch and retain their existing final-write
|
|
122
|
+
// behavior, including the ability to override a routed target deliberately.
|
|
123
|
+
for (const { key, target } of extraTargets)
|
|
124
|
+
map.set(slugify(key), target);
|
|
125
|
+
return map;
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
markdownFiles: () => walk('')
|
|
129
|
+
.filter((file) => file.endsWith('.md'))
|
|
130
|
+
.sort(),
|
|
131
|
+
noteFiles,
|
|
132
|
+
noteIds,
|
|
133
|
+
wikiLinkMap,
|
|
134
|
+
resolveLink: (value, linkOptions = {}) => resolveContentLink(value, wikiLinkMap(linkOptions.extraTargets), linkOptions.base),
|
|
135
|
+
remarkWikiLinks: (linkOptions = {}) => remarkContentWikiLinks(wikiLinkMap(linkOptions.extraTargets), linkOptions.base),
|
|
136
|
+
resolveMarkdown: (source, linkOptions = {}) => resolveContentMarkdown(source, wikiLinkMap(linkOptions.extraTargets), linkOptions.base),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@galaxy-foundry/content-reader",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Collection-backed content and wiki-link plumbing shared by Foundry sites; schemas, routes, and domain presentation stay per-instance.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/jmchilton/foundry-lib.git",
|
|
20
|
+
"directory": "packages/content-reader"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"provenance": true
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"foundry",
|
|
28
|
+
"content-reader",
|
|
29
|
+
"content",
|
|
30
|
+
"wiki-links"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"js-yaml": "^4.1.0",
|
|
35
|
+
"@galaxy-foundry/wiki-links": "^0.4.0",
|
|
36
|
+
"@galaxy-foundry/kind-schema": "^0.5.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/js-yaml": "^4.0.9",
|
|
40
|
+
"@types/node": "^22.10.0",
|
|
41
|
+
"typescript": "^5.7.0",
|
|
42
|
+
"vitest": "^4.1.10"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc",
|
|
49
|
+
"typecheck": "tsc -p tsconfig.test.json",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest"
|
|
52
|
+
}
|
|
53
|
+
}
|