@crazygiscool/cap 1.1.9 → 1.2.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.
Files changed (37) hide show
  1. package/dist/__tests__/crawler.test.d.ts +1 -0
  2. package/dist/__tests__/crawler.test.js +175 -0
  3. package/dist/__tests__/crawler.test.js.map +1 -0
  4. package/dist/__tests__/merger.test.d.ts +1 -0
  5. package/dist/__tests__/merger.test.js +136 -0
  6. package/dist/__tests__/merger.test.js.map +1 -0
  7. package/dist/crawler/index.d.ts +10 -0
  8. package/dist/crawler/index.js +116 -0
  9. package/dist/crawler/index.js.map +1 -0
  10. package/dist/crawler/mediawiki.d.ts +12 -0
  11. package/dist/crawler/mediawiki.js +64 -0
  12. package/dist/crawler/mediawiki.js.map +1 -0
  13. package/dist/crawler/merger.d.ts +3 -0
  14. package/dist/crawler/merger.js +128 -0
  15. package/dist/crawler/merger.js.map +1 -0
  16. package/dist/crawler/parser.d.ts +8 -0
  17. package/dist/crawler/parser.js +95 -0
  18. package/dist/crawler/parser.js.map +1 -0
  19. package/dist/crawler/parsers/fandom.d.ts +2 -0
  20. package/dist/crawler/parsers/fandom.js +5 -0
  21. package/dist/crawler/parsers/fandom.js.map +1 -0
  22. package/dist/crawler/parsers/index.d.ts +5 -0
  23. package/dist/crawler/parsers/index.js +17 -0
  24. package/dist/crawler/parsers/index.js.map +1 -0
  25. package/dist/crawler/parsers/tfwiki.d.ts +2 -0
  26. package/dist/crawler/parsers/tfwiki.js +5 -0
  27. package/dist/crawler/parsers/tfwiki.js.map +1 -0
  28. package/dist/crawler/parsers/wikipedia.d.ts +2 -0
  29. package/dist/crawler/parsers/wikipedia.js +5 -0
  30. package/dist/crawler/parsers/wikipedia.js.map +1 -0
  31. package/dist/crawler/types.d.ts +61 -0
  32. package/dist/crawler/types.js +2 -0
  33. package/dist/crawler/types.js.map +1 -0
  34. package/dist/index.d.ts +150 -1
  35. package/dist/index.js +26 -0
  36. package/dist/index.js.map +1 -1
  37. package/package.json +14 -1
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,175 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseTFWiki } from "../crawler/parsers/tfwiki.js";
3
+ import { parseWikipedia } from "../crawler/parsers/wikipedia.js";
4
+ import { parseFandom } from "../crawler/parsers/fandom.js";
5
+ import { extractFirstParagraph, extractCategories, extractPageTitle } from "../crawler/parser.js";
6
+ import * as cheerio from "cheerio";
7
+ const TFWIKI_HTML = `<!DOCTYPE html>
8
+ <html>
9
+ <head><title>Optimus Prime (G1) — TFWiki</title></head>
10
+ <body>
11
+ <div id="mw-content-text">
12
+ <h1 id="firstHeading">Optimus Prime (G1)</h1>
13
+ <table class="infobox">
14
+ <tr><th colspan="2">Optimus Prime</th></tr>
15
+ <tr><th>Affiliation</th><td>Autobot</td></tr>
16
+ <tr><th>Function</th><td>Leader of the Autobots</td></tr>
17
+ <tr><th>Motto</th><td>"Freedom is the right of all sentient beings"</td></tr>
18
+ <tr><th>Alt Mode</th><td>Freightliner FLT Flat-Nose Semi-Truck</td></tr>
19
+ <tr><th>Color Scheme</th><td>Red, Blue, Silver</td></tr>
20
+ <tr><th>Firepower</th><td>10</td></tr>
21
+ <tr><th>Strength</th><td>10</td></tr>
22
+ <tr><th>Intelligence</th><td>10</td></tr>
23
+ <tr><th>Speed</th><td>8</td></tr>
24
+ </table>
25
+ <p>Optimus Prime is the heroic leader of the Autobots. He is the reincarnation of the original Optimus Prime.</p>
26
+ </div>
27
+ <div id="mw-normal-catlinks">
28
+ <ul>
29
+ <li><a>Autobots</a></li>
30
+ <li><a>Generation 1</a></li>
31
+ <li><a>Primes</a></li>
32
+ </ul>
33
+ </div>
34
+ </body>
35
+ </html>`;
36
+ const WIKIPEDIA_HTML = `<!DOCTYPE html>
37
+ <html>
38
+ <head><title>Optimus Prime - Wikipedia</title></head>
39
+ <body>
40
+ <div id="mw-content-text">
41
+ <h1 id="firstHeading">Optimus Prime</h1>
42
+ <table class="infobox">
43
+ <tr><th colspan="2">Optimus Prime</th></tr>
44
+ <tr><th scope="row">Affiliation</th><td>Autobot</td></tr>
45
+ <tr><th scope="row">Function</th><td>Leader</td></tr>
46
+ <tr><th scope="row">Motto</th><td>"Freedom is the right of all sentient beings"</td></tr>
47
+ <tr><th scope="row">Series</th><td>Generation 1</td></tr>
48
+ </table>
49
+ <p>Optimus Prime is a fictional character and the main protagonist of the Transformers franchise.</p>
50
+ </div>
51
+ <div id="mw-normal-catlinks">
52
+ <ul>
53
+ <li><a>Autobots</a></li>
54
+ <li><a>Fictional characters</a></li>
55
+ </ul>
56
+ </div>
57
+ </body>
58
+ </html>`;
59
+ const FANDOM_HTML = `<!DOCTYPE html>
60
+ <html>
61
+ <head><title>Optimus Prime | Transformers Wiki | Fandom</title></head>
62
+ <body>
63
+ <div id="mw-content-text">
64
+ <h1 id="firstHeading">Optimus Prime</h1>
65
+ <aside class="portable-infobox">
66
+ <div class="pi-item pi-data">
67
+ <h3>Affiliation</h3>
68
+ <div>Autobot</div>
69
+ </div>
70
+ <div class="pi-item pi-data">
71
+ <h3>Function</h3>
72
+ <div>Supreme Commander</div>
73
+ </div>
74
+ <div class="pi-item pi-data">
75
+ <h3>Motto</h3>
76
+ <div>"Freedom is the right of all sentient beings"</div>
77
+ </div>
78
+ </aside>
79
+ <p>Optimus Prime is the noble leader of the Autobots in the Transformers franchise.</p>
80
+ </div>
81
+ <div id="mw-normal-catlinks">
82
+ <ul>
83
+ <li><a>Autobots</a></li>
84
+ <li><a>Leaders</a></li>
85
+ </ul>
86
+ </div>
87
+ </body>
88
+ </html>`;
89
+ const NO_INFOBOX_HTML = `<!DOCTYPE html>
90
+ <html>
91
+ <head><title>Some random page</title></head>
92
+ <body>
93
+ <div id="mw-content-text">
94
+ <h1 id="firstHeading">Some Random Page</h1>
95
+ <p>This is a page with no infobox at all.</p>
96
+ </div>
97
+ </body>
98
+ </html>`;
99
+ describe("parseTFWiki", () => {
100
+ it("extracts infobox fields from TFWiki HTML", () => {
101
+ const result = parseTFWiki(TFWIKI_HTML);
102
+ expect(result.title).toBe("Optimus Prime (G1)");
103
+ const labels = result.infoboxFields.map((f) => f.label);
104
+ expect(labels).toContain("Affiliation");
105
+ expect(labels).toContain("Function");
106
+ expect(labels).toContain("Motto");
107
+ expect(labels).toContain("Firepower");
108
+ const aff = result.infoboxFields.find((f) => f.label === "Affiliation");
109
+ expect(aff?.value).toBe("Autobot");
110
+ const fp = result.infoboxFields.find((f) => f.label === "Firepower");
111
+ expect(fp?.value).toBe("10");
112
+ });
113
+ it("extracts description from first paragraph", () => {
114
+ const result = parseTFWiki(TFWIKI_HTML);
115
+ expect(result.description).toContain("heroic leader");
116
+ });
117
+ it("extracts categories", () => {
118
+ const result = parseTFWiki(TFWIKI_HTML);
119
+ expect(result.categories).toContain("Autobots");
120
+ expect(result.categories).toContain("Generation 1");
121
+ });
122
+ it("returns empty data for pages with no infobox", () => {
123
+ const result = parseTFWiki(NO_INFOBOX_HTML);
124
+ expect(result.infoboxFields).toHaveLength(0);
125
+ expect(result.title).toBe("Some Random Page");
126
+ });
127
+ });
128
+ describe("parseWikipedia", () => {
129
+ it("extracts infobox fields from Wikipedia HTML", () => {
130
+ const result = parseWikipedia(WIKIPEDIA_HTML);
131
+ expect(result.title).toBe("Optimus Prime");
132
+ const aff = result.infoboxFields.find((f) => f.label === "Affiliation");
133
+ expect(aff?.value).toBe("Autobot");
134
+ });
135
+ it("extracts description and categories", () => {
136
+ const result = parseWikipedia(WIKIPEDIA_HTML);
137
+ expect(result.description).toContain("main protagonist");
138
+ expect(result.categories).toContain("Autobots");
139
+ });
140
+ });
141
+ describe("parseFandom", () => {
142
+ it("extracts infobox fields from Fandom portable infobox", () => {
143
+ const result = parseFandom(FANDOM_HTML);
144
+ expect(result.title).toBe("Optimus Prime");
145
+ const aff = result.infoboxFields.find((f) => f.label === "Affiliation");
146
+ expect(aff?.value).toBe("Autobot");
147
+ const fn = result.infoboxFields.find((f) => f.label === "Function");
148
+ expect(fn?.value).toBe("Supreme Commander");
149
+ });
150
+ it("extracts description and categories", () => {
151
+ const result = parseFandom(FANDOM_HTML);
152
+ expect(result.description).toContain("noble leader");
153
+ expect(result.categories).toContain("Autobots");
154
+ });
155
+ });
156
+ describe("utility functions", () => {
157
+ it("extractPageTitle returns h1 text", () => {
158
+ const $ = cheerio.load('<h1 id="firstHeading">Test Title</h1>');
159
+ expect(extractPageTitle($)).toBe("Test Title");
160
+ });
161
+ it("extractPageTitle falls back to title tag", () => {
162
+ const $ = cheerio.load("<title>Fallback Title</title>");
163
+ expect(extractPageTitle($)).toBe("Fallback Title");
164
+ });
165
+ it("extractFirstParagraph skips short paragraphs", () => {
166
+ const html = '<div><p>Short</p><p>This is a sufficiently long paragraph for extraction.</p></div>';
167
+ const $ = cheerio.load(html);
168
+ expect(extractFirstParagraph($)).toBe("This is a sufficiently long paragraph for extraction.");
169
+ });
170
+ it("extractCategories returns empty array when no catlinks", () => {
171
+ const $ = cheerio.load("<body></body>");
172
+ expect(extractCategories($)).toEqual([]);
173
+ });
174
+ });
175
+ //# sourceMappingURL=crawler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crawler.test.js","sourceRoot":"","sources":["../../src/__tests__/crawler.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,136 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mergeEntry, defaultMergeRules } from "../crawler/merger.js";
3
+ const source1 = {
4
+ url: "https://tfwiki.net/wiki/Optimus_Prime_(G1)",
5
+ wiki: "tfwiki",
6
+ fetchedAt: new Date("2025-01-01"),
7
+ };
8
+ const source2 = {
9
+ url: "https://en.wikipedia.org/wiki/Optimus_Prime",
10
+ wiki: "wikipedia",
11
+ fetchedAt: new Date("2025-01-02"),
12
+ };
13
+ function makeCrawled(overrides = {}) {
14
+ return {
15
+ name: "Optimus Prime",
16
+ description: "Heroic leader of the Autobots",
17
+ continuityId: "Generation 1",
18
+ factions: ["Autobot"],
19
+ customFields: {
20
+ primaryFunction: "Leader",
21
+ sparkId: "SPARK-001",
22
+ },
23
+ source: source1,
24
+ ...overrides,
25
+ };
26
+ }
27
+ function makeExisting(overrides = {}) {
28
+ return {
29
+ id: "existing-id",
30
+ name: "Optimus Prime",
31
+ description: "Leader of the Autobots",
32
+ continuityId: "Generation 1",
33
+ factions: ["Autobot"],
34
+ sparkId: "MANUAL-001",
35
+ primaryFunction: "Supreme Commander",
36
+ sources: [source2],
37
+ updatedAt: new Date("2024-01-01"),
38
+ ...overrides,
39
+ };
40
+ }
41
+ describe("mergeEntry — new entry (no existing)", () => {
42
+ it("returns action=new with all crawled data", () => {
43
+ const result = mergeEntry(null, makeCrawled(), defaultMergeRules());
44
+ expect(result.action).toBe("new");
45
+ expect(result.doc?.name).toBe("Optimus Prime");
46
+ expect(result.doc?.description).toBe("Heroic leader of the Autobots");
47
+ expect(result.doc?.continuityId).toBe("Generation 1");
48
+ expect(result.doc.primaryFunction).toBe("Leader");
49
+ expect(result.doc.sources).toEqual([source1]);
50
+ });
51
+ it("skips if name is empty and skip-if-no-name is true", () => {
52
+ const result = mergeEntry(null, makeCrawled({ name: "" }), defaultMergeRules());
53
+ expect(result.action).toBe("skipped");
54
+ });
55
+ });
56
+ describe("mergeEntry — duplicate (existing)", () => {
57
+ it("skips if source already exists and skip-if-source-exists is true", () => {
58
+ const crawled = makeCrawled({ source: source2 });
59
+ const existing = makeExisting();
60
+ const result = mergeEntry(existing, crawled, defaultMergeRules());
61
+ expect(result.action).toBe("skipped");
62
+ });
63
+ it("merges sources when new source is different", () => {
64
+ const result = mergeEntry(makeExisting(), makeCrawled(), defaultMergeRules());
65
+ expect(result.action).toBe("merged");
66
+ expect(result.doc.sources).toHaveLength(2);
67
+ expect(result.doc.sources[0]).toEqual(source2);
68
+ expect(result.doc.sources[1]).toEqual(source1);
69
+ });
70
+ it("keeps longer description when longer-description is true", () => {
71
+ const crawled = makeCrawled({ description: "Short" });
72
+ const existing = makeExisting({ description: "This is a much longer description for the character" });
73
+ const result = mergeEntry(existing, crawled, defaultMergeRules());
74
+ expect(result.doc.description).toBe("This is a much longer description for the character");
75
+ });
76
+ it("prefers crawled description when longer", () => {
77
+ const crawled = makeCrawled({ description: "This is a much longer crawled description" });
78
+ const existing = makeExisting({ description: "Short" });
79
+ const result = mergeEntry(existing, crawled, defaultMergeRules());
80
+ expect(result.doc.description).toBe("This is a much longer crawled description");
81
+ });
82
+ it("unions factions", () => {
83
+ const crawled = makeCrawled({ factions: ["Autobot", "Prime"] });
84
+ const existing = makeExisting({ factions: ["Autobot", "Leader"] });
85
+ const result = mergeEntry(existing, crawled, defaultMergeRules());
86
+ const factions = result.doc.factions;
87
+ expect(factions).toContain("Autobot");
88
+ expect(factions).toContain("Prime");
89
+ expect(factions).toContain("Leader");
90
+ expect(factions).toHaveLength(3);
91
+ });
92
+ it("fills empty custom fields when fill-empty-fields is true", () => {
93
+ const existing = makeExisting({ sparkId: undefined });
94
+ const crawled = makeCrawled({ customFields: { sparkId: "SPARK-001" } });
95
+ const result = mergeEntry(existing, crawled, defaultMergeRules());
96
+ expect(result.doc.sparkId).toBe("SPARK-001");
97
+ });
98
+ it("preserves existing custom field when fill-empty-fields is true", () => {
99
+ const existing = makeExisting({ sparkId: "EXISTING-SPARK" });
100
+ const crawled = makeCrawled({ customFields: { sparkId: "CRAWLED-SPARK" } });
101
+ const result = mergeEntry(existing, crawled, defaultMergeRules());
102
+ expect(result.doc.sparkId).toBe("EXISTING-SPARK");
103
+ });
104
+ it("overwrites when crawled-wins is true", () => {
105
+ const existing = makeExisting({ sparkId: "EXISTING" });
106
+ const crawled = makeCrawled({ customFields: { sparkId: "CRAWLED" } });
107
+ const rules = { ...defaultMergeRules(), "crawled-wins": true, "fill-empty-fields": false };
108
+ const result = mergeEntry(existing, crawled, rules);
109
+ expect(result.doc.sparkId).toBe("CRAWLED");
110
+ });
111
+ it("preserves existing continuity when continuity-existing is true", () => {
112
+ const existing = makeExisting({ continuityId: "Beast Wars" });
113
+ const crawled = makeCrawled({ continuityId: "Generation 1" });
114
+ const result = mergeEntry(existing, crawled, defaultMergeRules());
115
+ expect(result.doc.continuityId).toBe("Beast Wars");
116
+ });
117
+ it("overwrites continuity when continuity-existing is false", () => {
118
+ const existing = makeExisting({ continuityId: "Beast Wars" });
119
+ const crawled = makeCrawled({ continuityId: "Generation 1" });
120
+ const rules = { ...defaultMergeRules(), "continuity-existing": false };
121
+ const result = mergeEntry(existing, crawled, rules);
122
+ expect(result.doc.continuityId).toBe("Generation 1");
123
+ });
124
+ });
125
+ describe("defaultMergeRules", () => {
126
+ it("returns all defaults", () => {
127
+ const rules = defaultMergeRules();
128
+ expect(rules["sources-merge"]).toBe(true);
129
+ expect(rules["longer-description"]).toBe(true);
130
+ expect(rules["factions-union"]).toBe(true);
131
+ expect(rules["fill-empty-fields"]).toBe(true);
132
+ expect(rules["skip-if-no-name"]).toBe(true);
133
+ expect(rules["skip-if-source-exists"]).toBe(true);
134
+ });
135
+ });
136
+ //# sourceMappingURL=merger.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merger.test.js","sourceRoot":"","sources":["../../src/__tests__/merger.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { CrawlSourceConfig, CrawledEntry, CrawlProgress, MergeableDocument } from "./types.js";
2
+ export declare class Crawler<T extends MergeableDocument> {
3
+ private client;
4
+ private parserFn;
5
+ private sourceConfig;
6
+ constructor(config: CrawlSourceConfig);
7
+ discover(categoryPath: string, maxPages?: number): Promise<string[]>;
8
+ scrape(pageTitle: string): Promise<CrawledEntry<T> | null>;
9
+ crawlCategory(categoryPath: string, onProgress?: (progress: CrawlProgress) => void, maxPages?: number): Promise<CrawledEntry<T>[]>;
10
+ }
@@ -0,0 +1,116 @@
1
+ import { MediaWikiClient } from "./mediawiki.js";
2
+ import { getParser } from "./parsers/index.js";
3
+ function castValue(raw, type) {
4
+ switch (type) {
5
+ case "number": {
6
+ const n = parseInt(raw, 10);
7
+ return isNaN(n) ? raw : n;
8
+ }
9
+ case "boolean":
10
+ return raw.toLowerCase() === "yes" || raw.toLowerCase() === "true";
11
+ default:
12
+ return raw;
13
+ }
14
+ }
15
+ function mapFields(parsed, fieldMapping, continuityId) {
16
+ const result = {
17
+ description: parsed.description,
18
+ continuityId,
19
+ factions: [],
20
+ customFields: {},
21
+ };
22
+ // Try to infer continuity from categories
23
+ for (const cat of parsed.categories) {
24
+ const lower = cat.toLowerCase();
25
+ if (lower.includes("generation 1") || lower.includes("g1") ||
26
+ lower.includes("beast wars") || lower.includes("beast machines") ||
27
+ lower.includes("armada") || lower.includes("energon") ||
28
+ lower.includes("cybertron") || lower.includes("animated") ||
29
+ lower.includes("prime") || lower.includes("robots in disguise") ||
30
+ lower.includes("aligned") || lower.includes("cyberverse") ||
31
+ lower.includes("earthspark") || lower.includes("wfc") ||
32
+ lower.includes("siege") || lower.includes("earthrise") ||
33
+ lower.includes("kingdom")) {
34
+ result.continuityId = cat.replace(/^Category:/, "");
35
+ break;
36
+ }
37
+ }
38
+ for (const field of parsed.infoboxFields) {
39
+ const mapping = fieldMapping[field.label];
40
+ if (!mapping)
41
+ continue;
42
+ if (mapping.target === "factions") {
43
+ result.factions = field.value
44
+ .split(/[,;/\n]+/)
45
+ .map((s) => s.trim())
46
+ .filter(Boolean);
47
+ }
48
+ else if (mapping.target === "description") {
49
+ if (field.value.length > result.description.length) {
50
+ result.description = field.value;
51
+ }
52
+ }
53
+ else if (mapping.target === "continuityId") {
54
+ if (!result.continuityId)
55
+ result.continuityId = field.value;
56
+ }
57
+ else {
58
+ result.customFields[mapping.target] = castValue(field.value, mapping.type);
59
+ }
60
+ }
61
+ return result;
62
+ }
63
+ export class Crawler {
64
+ client;
65
+ parserFn;
66
+ sourceConfig;
67
+ constructor(config) {
68
+ this.client = new MediaWikiClient(config);
69
+ this.parserFn = getParser(config.parser);
70
+ this.sourceConfig = config;
71
+ }
72
+ async discover(categoryPath, maxPages = 500) {
73
+ return this.client.listCategoryMembers(categoryPath, maxPages);
74
+ }
75
+ async scrape(pageTitle) {
76
+ const html = await this.client.fetchPageHtml(pageTitle);
77
+ const parsed = this.parserFn(html);
78
+ if (!parsed.title)
79
+ return null;
80
+ const mapped = mapFields(parsed, this.sourceConfig.fieldMapping, "");
81
+ const source = {
82
+ url: `${this.sourceConfig.baseUrl}/wiki/${encodeURIComponent(pageTitle.replace(/ /g, "_"))}`,
83
+ wiki: this.sourceConfig.id,
84
+ fetchedAt: new Date(),
85
+ };
86
+ return {
87
+ name: parsed.title,
88
+ description: mapped.description,
89
+ continuityId: mapped.continuityId,
90
+ factions: mapped.factions,
91
+ customFields: mapped.customFields,
92
+ source,
93
+ };
94
+ }
95
+ async crawlCategory(categoryPath, onProgress, maxPages = 500) {
96
+ const titles = await this.discover(categoryPath, maxPages);
97
+ const entries = [];
98
+ for (let i = 0; i < titles.length; i++) {
99
+ onProgress?.({
100
+ current: i + 1,
101
+ total: titles.length,
102
+ pageTitle: titles[i],
103
+ });
104
+ try {
105
+ const entry = await this.scrape(titles[i]);
106
+ if (entry)
107
+ entries.push(entry);
108
+ }
109
+ catch {
110
+ // skip pages that fail to parse
111
+ }
112
+ }
113
+ return entries;
114
+ }
115
+ }
116
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/crawler/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ interface MediaWikiConfig {
2
+ baseUrl: string;
3
+ apiUrl: string;
4
+ }
5
+ export declare class MediaWikiClient {
6
+ private config;
7
+ constructor(config: MediaWikiConfig);
8
+ listCategoryMembers(categoryPath: string, maxPages?: number): Promise<string[]>;
9
+ fetchPageHtml(pageTitle: string): Promise<string>;
10
+ searchPages(query: string, maxResults?: number): Promise<string[]>;
11
+ }
12
+ export {};
@@ -0,0 +1,64 @@
1
+ export class MediaWikiClient {
2
+ config;
3
+ constructor(config) {
4
+ this.config = config;
5
+ }
6
+ async listCategoryMembers(categoryPath, maxPages = 500) {
7
+ const params = new URLSearchParams({
8
+ action: "query",
9
+ format: "json",
10
+ list: "categorymembers",
11
+ cmtitle: categoryPath,
12
+ cmlimit: String(Math.min(maxPages, 500)),
13
+ cmtype: "page",
14
+ });
15
+ const url = `${this.config.apiUrl}?${params}`;
16
+ const res = await fetch(url);
17
+ if (!res.ok) {
18
+ throw new Error(`MediaWiki API error: ${res.status} ${res.statusText}`);
19
+ }
20
+ const data = await res.json();
21
+ if (data.error) {
22
+ throw new Error(`MediaWiki API: ${data.error.code} — ${data.error.info}`);
23
+ }
24
+ return (data.query?.categorymembers ?? []).map((m) => m.title);
25
+ }
26
+ async fetchPageHtml(pageTitle) {
27
+ const params = new URLSearchParams({
28
+ action: "parse",
29
+ format: "json",
30
+ page: pageTitle,
31
+ prop: "text",
32
+ });
33
+ const url = `${this.config.apiUrl}?${params}`;
34
+ const res = await fetch(url);
35
+ if (!res.ok) {
36
+ throw new Error(`MediaWiki API error: ${res.status} for page "${pageTitle}"`);
37
+ }
38
+ const data = (await res.json());
39
+ if (data.error) {
40
+ throw new Error(`MediaWiki API: ${data.error.code} — ${data.error.info}`);
41
+ }
42
+ return data.parse?.text?.["*"] ?? "";
43
+ }
44
+ async searchPages(query, maxResults = 50) {
45
+ const params = new URLSearchParams({
46
+ action: "query",
47
+ format: "json",
48
+ list: "search",
49
+ srsearch: query,
50
+ srlimit: String(Math.min(maxResults, 500)),
51
+ });
52
+ const url = `${this.config.apiUrl}?${params}`;
53
+ const res = await fetch(url);
54
+ if (!res.ok) {
55
+ throw new Error(`MediaWiki API error: ${res.status} ${res.statusText}`);
56
+ }
57
+ const data = (await res.json());
58
+ if (data.error) {
59
+ throw new Error(`MediaWiki API: ${data.error.code} — ${data.error.info}`);
60
+ }
61
+ return (data.query?.search ?? []).map((s) => s.title);
62
+ }
63
+ }
64
+ //# sourceMappingURL=mediawiki.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mediawiki.js","sourceRoot":"","sources":["../../src/crawler/mediawiki.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { CrawledEntry, MergeableDocument, MergeConfig, MergeResult } from "./types.js";
2
+ export declare function defaultMergeRules(): MergeConfig;
3
+ export declare function mergeEntry<T extends MergeableDocument>(existing: T | null, crawled: CrawledEntry<T>, rules: MergeConfig): MergeResult<T>;
@@ -0,0 +1,128 @@
1
+ function sourceExists(sources, url) {
2
+ return (sources ?? []).some((s) => s.url === url);
3
+ }
4
+ export function defaultMergeRules() {
5
+ return {
6
+ "sources-merge": true,
7
+ "longer-description": true,
8
+ "factions-union": true,
9
+ "fill-empty-fields": true,
10
+ "existing-wins": false,
11
+ "crawled-wins": false,
12
+ "continuity-existing": true,
13
+ "bump-updated-at": true,
14
+ "skip-if-no-name": true,
15
+ "skip-if-source-exists": true,
16
+ };
17
+ }
18
+ export function mergeEntry(existing, crawled, rules) {
19
+ const mergedRules = { ...defaultMergeRules(), ...rules };
20
+ const changes = [];
21
+ const doc = {};
22
+ if (mergedRules["skip-if-no-name"] && !crawled.name) {
23
+ return { action: "skipped", changes: ["no name"] };
24
+ }
25
+ if (mergedRules["skip-if-source-exists"] && existing) {
26
+ if (sourceExists(existing.sources, crawled.source.url)) {
27
+ return { action: "skipped", changes: ["source already exists"] };
28
+ }
29
+ }
30
+ if (!existing) {
31
+ // New entry — set everything
32
+ doc.name = crawled.name;
33
+ doc.description = crawled.description;
34
+ doc.continuityId = crawled.continuityId;
35
+ doc.factions = crawled.factions;
36
+ doc.sources = [crawled.source];
37
+ for (const [key, value] of Object.entries(crawled.customFields)) {
38
+ doc[key] = value;
39
+ }
40
+ return { action: "new", doc: doc, changes: ["new entry"] };
41
+ }
42
+ const mergedSources = [...(existing.sources ?? [])];
43
+ if (mergedRules["sources-merge"]) {
44
+ if (!sourceExists(mergedSources, crawled.source.url)) {
45
+ mergedSources.push(crawled.source);
46
+ changes.push("source added");
47
+ }
48
+ }
49
+ if (mergedRules["longer-description"]) {
50
+ if (crawled.description &&
51
+ crawled.description.length > (existing.description ?? "").length) {
52
+ doc.description = crawled.description;
53
+ changes.push("description updated");
54
+ }
55
+ else if ((existing.description ?? "").length > 0) {
56
+ doc.description = existing.description;
57
+ }
58
+ }
59
+ else {
60
+ doc.description = crawled.description || existing.description;
61
+ }
62
+ if (mergedRules["factions-union"]) {
63
+ const all = new Set([
64
+ ...(existing.factions ?? []).map((f) => f.toLowerCase()),
65
+ ...crawled.factions.map((f) => f.toLowerCase()),
66
+ ]);
67
+ const mergedFactions = [...all].map((f) => existing.factions?.find((ef) => ef.toLowerCase() === f) ??
68
+ crawled.factions.find((cf) => cf.toLowerCase() === f));
69
+ // Use original casing from existing if available
70
+ const existingLookup = new Map((existing.factions ?? []).map((f) => [f.toLowerCase(), f]));
71
+ const crawledLookup = new Map(crawled.factions.map((f) => [f.toLowerCase(), f]));
72
+ const union = [];
73
+ for (const lower of all) {
74
+ union.push(existingLookup.get(lower) ?? crawledLookup.get(lower));
75
+ }
76
+ if (union.length !== (existing.factions ?? []).length) {
77
+ changes.push("factions updated");
78
+ }
79
+ doc.factions = union;
80
+ }
81
+ else {
82
+ doc.factions = existing.factions ?? crawled.factions;
83
+ }
84
+ if (mergedRules["continuity-existing"] && existing.continuityId) {
85
+ doc.continuityId = existing.continuityId;
86
+ }
87
+ else {
88
+ doc.continuityId = crawled.continuityId || existing.continuityId;
89
+ }
90
+ // Custom fields
91
+ for (const [key, value] of Object.entries(crawled.customFields)) {
92
+ const existingValue = existing[key];
93
+ if (mergedRules["crawled-wins"]) {
94
+ doc[key] = value;
95
+ }
96
+ else if (mergedRules["existing-wins"]) {
97
+ if (existingValue !== undefined && existingValue !== null) {
98
+ doc[key] = existingValue;
99
+ }
100
+ else {
101
+ doc[key] = value;
102
+ if (value !== undefined && value !== null)
103
+ changes.push(`${key} filled`);
104
+ }
105
+ }
106
+ else if (mergedRules["fill-empty-fields"]) {
107
+ // Default behavior: fill if existing is null/undefined, keep if set
108
+ if (existingValue === undefined || existingValue === null) {
109
+ doc[key] = value;
110
+ if (value !== undefined && value !== null)
111
+ changes.push(`${key} filled`);
112
+ }
113
+ else {
114
+ doc[key] = existingValue;
115
+ }
116
+ }
117
+ }
118
+ doc.sources = mergedSources;
119
+ if (changes.length === 0 && mergedRules["bump-updated-at"]) {
120
+ changes.push("metadata only");
121
+ }
122
+ return {
123
+ action: changes.length > 0 ? "merged" : "skipped",
124
+ doc: doc,
125
+ changes,
126
+ };
127
+ }
128
+ //# sourceMappingURL=merger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merger.js","sourceRoot":"","sources":["../../src/crawler/merger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import * as cheerio from "cheerio";
2
+ import type { ParsedWikiPage } from "./types.js";
3
+ export type { ParsedWikiPage };
4
+ export declare function extractFirstParagraph($: cheerio.CheerioAPI): string;
5
+ export declare function extractCategories($: cheerio.CheerioAPI): string[];
6
+ export declare function extractPageTitle($: cheerio.CheerioAPI): string;
7
+ export declare function parseGenericInfobox(html: string, tableSelector: string, labelSelector: string, valueSelector: string, titleSelector?: string): ParsedWikiPage;
8
+ export declare function parsePortableInfobox(html: string): ParsedWikiPage;
@@ -0,0 +1,95 @@
1
+ import * as cheerio from "cheerio";
2
+ export function extractFirstParagraph($) {
3
+ const content = $("#mw-content-text")[0] ?? $("body")[0];
4
+ if (!content)
5
+ return "";
6
+ const paragraphs = $(content).find("p");
7
+ for (const p of paragraphs) {
8
+ const text = $(p).text().trim();
9
+ if (text.length > 20)
10
+ return text;
11
+ }
12
+ return "";
13
+ }
14
+ export function extractCategories($) {
15
+ const cats = [];
16
+ const catLinks = $("#mw-normal-catlinks ul li a");
17
+ if (catLinks.length) {
18
+ catLinks.each((_, el) => {
19
+ const text = $(el).text().trim();
20
+ if (text)
21
+ cats.push(text);
22
+ });
23
+ }
24
+ return cats;
25
+ }
26
+ export function extractPageTitle($) {
27
+ const h1 = $("#firstHeading").text().trim();
28
+ if (h1)
29
+ return h1;
30
+ return $("title").text().replace(/ – .+$/, "").replace(/ - .+$/, "").trim();
31
+ }
32
+ function cleanValue(value) {
33
+ return value.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
34
+ }
35
+ export function parseGenericInfobox(html, tableSelector, labelSelector, valueSelector, titleSelector = "") {
36
+ const $ = cheerio.load(html);
37
+ const title = extractPageTitle($);
38
+ const description = extractFirstParagraph($);
39
+ const categories = extractCategories($);
40
+ const infoboxFields = [];
41
+ const table = $(tableSelector).first();
42
+ if (table.length) {
43
+ if (titleSelector) {
44
+ table.find(titleSelector).each((_, el) => {
45
+ const colspan = $(el).attr("colspan");
46
+ if (colspan) {
47
+ const val = $(el).text().trim();
48
+ if (val) {
49
+ infoboxFields.push({ label: "_title", value: val });
50
+ }
51
+ }
52
+ });
53
+ }
54
+ const rows = table.find("tr");
55
+ rows.each((_, row) => {
56
+ const labelEl = $(row).find(labelSelector).first();
57
+ const valueEl = $(row).find(valueSelector).first();
58
+ if (labelEl.length && valueEl.length) {
59
+ const label = labelEl.text().trim();
60
+ const rawHtml = valueEl.html() ?? "";
61
+ const value = cleanValue(rawHtml);
62
+ if (label && label !== "_title" && !label.startsWith("Image")) {
63
+ infoboxFields.push({ label, value });
64
+ }
65
+ }
66
+ });
67
+ }
68
+ return { title, description, categories, infoboxFields };
69
+ }
70
+ export function parsePortableInfobox(html) {
71
+ const $ = cheerio.load(html);
72
+ const title = extractPageTitle($);
73
+ const description = extractFirstParagraph($);
74
+ const categories = extractCategories($);
75
+ const infoboxFields = [];
76
+ const aside = $("aside.portable-infobox").first();
77
+ if (aside.length) {
78
+ const titleEl = aside.find(".pi-title, .pi-data-value.pi-item");
79
+ if (titleEl.length) {
80
+ infoboxFields.push({ label: "_title", value: titleEl.first().text().trim() });
81
+ }
82
+ aside.find('[class*="pi-item"][class*="pi-data"]').each((_, item) => {
83
+ const labelEl = $(item).find("h3");
84
+ const valueEl = $(item).find("div").last();
85
+ if (labelEl.length && valueEl.length) {
86
+ const label = labelEl.text().trim();
87
+ const value = cleanValue(valueEl.html() ?? "");
88
+ if (label)
89
+ infoboxFields.push({ label, value });
90
+ }
91
+ });
92
+ }
93
+ return { title, description, categories, infoboxFields };
94
+ }
95
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/crawler/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type ParsedWikiPage } from "../parser.js";
2
+ export declare function parseFandom(html: string): ParsedWikiPage;
@@ -0,0 +1,5 @@
1
+ import { parsePortableInfobox } from "../parser.js";
2
+ export function parseFandom(html) {
3
+ return parsePortableInfobox(html);
4
+ }
5
+ //# sourceMappingURL=fandom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fandom.js","sourceRoot":"","sources":["../../../src/crawler/parsers/fandom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAuB,MAAM,cAAc,CAAC;AAEzE,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { parseTFWiki } from "./tfwiki.js";
2
+ export { parseWikipedia } from "./wikipedia.js";
3
+ export { parseFandom } from "./fandom.js";
4
+ import type { ParsedWikiPage } from "../parser.js";
5
+ export declare function getParser(name: "tfwiki" | "wikipedia" | "fandom"): (html: string) => ParsedWikiPage;
@@ -0,0 +1,17 @@
1
+ export { parseTFWiki } from "./tfwiki.js";
2
+ export { parseWikipedia } from "./wikipedia.js";
3
+ export { parseFandom } from "./fandom.js";
4
+ import { parseTFWiki } from "./tfwiki.js";
5
+ import { parseWikipedia } from "./wikipedia.js";
6
+ import { parseFandom } from "./fandom.js";
7
+ export function getParser(name) {
8
+ switch (name) {
9
+ case "tfwiki":
10
+ return parseTFWiki;
11
+ case "wikipedia":
12
+ return parseWikipedia;
13
+ case "fandom":
14
+ return parseFandom;
15
+ }
16
+ }
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/crawler/parsers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,SAAS,CACvB,IAAuC;IAEvC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,WAAW,CAAC;QACrB,KAAK,WAAW;YACd,OAAO,cAAc,CAAC;QACxB,KAAK,QAAQ;YACX,OAAO,WAAW,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type ParsedWikiPage } from "../parser.js";
2
+ export declare function parseTFWiki(html: string): ParsedWikiPage;
@@ -0,0 +1,5 @@
1
+ import { parseGenericInfobox } from "../parser.js";
2
+ export function parseTFWiki(html) {
3
+ return parseGenericInfobox(html, "table.infobox", "th", "td", "th[colspan]");
4
+ }
5
+ //# sourceMappingURL=tfwiki.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tfwiki.js","sourceRoot":"","sources":["../../../src/crawler/parsers/tfwiki.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,cAAc,CAAC;AAExE,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,mBAAmB,CACxB,IAAI,EACJ,eAAe,EACf,IAAI,EACJ,IAAI,EACJ,aAAa,CACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type ParsedWikiPage } from "../parser.js";
2
+ export declare function parseWikipedia(html: string): ParsedWikiPage;
@@ -0,0 +1,5 @@
1
+ import { parseGenericInfobox } from "../parser.js";
2
+ export function parseWikipedia(html) {
3
+ return parseGenericInfobox(html, "table.infobox", "th", "td", "th[colspan]");
4
+ }
5
+ //# sourceMappingURL=wikipedia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wikipedia.js","sourceRoot":"","sources":["../../../src/crawler/parsers/wikipedia.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,cAAc,CAAC;AAExE,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,mBAAmB,CACxB,IAAI,EACJ,eAAe,EACf,IAAI,EACJ,IAAI,EACJ,aAAa,CACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,61 @@
1
+ export interface CrawlSourceConfig {
2
+ id: string;
3
+ label: string;
4
+ baseUrl: string;
5
+ apiUrl: string;
6
+ parser: "tfwiki" | "wikipedia" | "fandom";
7
+ categories: CrawlCategory[];
8
+ fieldMapping: Record<string, FieldMappingEntry>;
9
+ }
10
+ export interface CrawlCategory {
11
+ id: string;
12
+ label: string;
13
+ path: string;
14
+ }
15
+ export interface FieldMappingEntry {
16
+ target: string;
17
+ type: "string" | "number" | "boolean" | "string-array";
18
+ }
19
+ export interface CrawlSourceRecord {
20
+ url: string;
21
+ wiki: string;
22
+ fetchedAt: Date;
23
+ }
24
+ export interface CrawledEntry<T = Record<string, unknown>> {
25
+ name: string;
26
+ description: string;
27
+ continuityId: string;
28
+ factions: string[];
29
+ customFields: Record<string, unknown>;
30
+ source: CrawlSourceRecord;
31
+ }
32
+ export type MergeConfig = Record<string, boolean>;
33
+ export interface MergeResult<T> {
34
+ action: "new" | "merged" | "skipped";
35
+ doc?: Partial<T>;
36
+ changes?: string[];
37
+ }
38
+ export interface MergeableDocument {
39
+ id?: string;
40
+ name: string;
41
+ description?: string;
42
+ continuityId?: string;
43
+ factions?: string[];
44
+ updatedAt?: Date;
45
+ sources?: CrawlSourceRecord[];
46
+ [key: string]: unknown;
47
+ }
48
+ export interface ParsedWikiPage {
49
+ title: string;
50
+ description: string;
51
+ categories: string[];
52
+ infoboxFields: {
53
+ label: string;
54
+ value: string;
55
+ }[];
56
+ }
57
+ export interface CrawlProgress {
58
+ current: number;
59
+ total: number;
60
+ pageTitle: string;
61
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/crawler/types.ts"],"names":[],"mappings":""}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  import { z } from "zod";
2
- import { Collection, Db } from "mongodb";
2
+ import type { Collection, Db } from "mongodb";
3
+ export { Crawler } from "./crawler/index.js";
4
+ export { MediaWikiClient } from "./crawler/mediawiki.js";
5
+ export { mergeEntry, defaultMergeRules } from "./crawler/merger.js";
6
+ export { getParser, parseTFWiki, parseWikipedia, parseFandom } from "./crawler/parsers/index.js";
7
+ export { parseGenericInfobox, parsePortableInfobox, extractFirstParagraph, extractCategories, extractPageTitle } from "./crawler/parser.js";
8
+ export type { CrawlSourceConfig, CrawlCategory, FieldMappingEntry, CrawlSourceRecord, CrawledEntry, MergeConfig, MergeResult, MergeableDocument, ParsedWikiPage, CrawlProgress, } from "./crawler/types.js";
9
+ import type { CrawlSourceConfig } from "./crawler/types.js";
3
10
  export interface ICAPBaseDocument {
4
11
  id: string;
5
12
  name: string;
@@ -52,9 +59,14 @@ export interface EntryFormatConfig {
52
59
  requiredFields?: string[];
53
60
  customFields?: CustomFieldConfig[];
54
61
  }
62
+ export interface CrawlingConfig {
63
+ sources: CrawlSourceConfig[];
64
+ mergeRules: Record<string, boolean>;
65
+ }
55
66
  export interface SettingsConfig {
56
67
  databaseName: string;
57
68
  entryFormat: EntryFormatConfig;
69
+ crawling?: CrawlingConfig;
58
70
  }
59
71
  export declare const CAPSettingsSchema: z.ZodObject<{
60
72
  databaseName: z.ZodString;
@@ -88,6 +100,105 @@ export declare const CAPSettingsSchema: z.ZodObject<{
88
100
  label: string;
89
101
  }[] | undefined;
90
102
  }>;
103
+ crawling: z.ZodOptional<z.ZodObject<{
104
+ sources: z.ZodArray<z.ZodObject<{
105
+ id: z.ZodString;
106
+ label: z.ZodString;
107
+ baseUrl: z.ZodString;
108
+ apiUrl: z.ZodString;
109
+ parser: z.ZodEnum<["tfwiki", "wikipedia", "fandom"]>;
110
+ categories: z.ZodArray<z.ZodObject<{
111
+ id: z.ZodString;
112
+ label: z.ZodString;
113
+ path: z.ZodString;
114
+ }, "strip", z.ZodTypeAny, {
115
+ id: string;
116
+ path: string;
117
+ label: string;
118
+ }, {
119
+ id: string;
120
+ path: string;
121
+ label: string;
122
+ }>, "many">;
123
+ fieldMapping: z.ZodRecord<z.ZodString, z.ZodObject<{
124
+ target: z.ZodString;
125
+ type: z.ZodEnum<["string", "number", "boolean", "string-array"]>;
126
+ }, "strip", z.ZodTypeAny, {
127
+ type: "string" | "number" | "boolean" | "string-array";
128
+ target: string;
129
+ }, {
130
+ type: "string" | "number" | "boolean" | "string-array";
131
+ target: string;
132
+ }>>;
133
+ }, "strip", z.ZodTypeAny, {
134
+ id: string;
135
+ label: string;
136
+ baseUrl: string;
137
+ apiUrl: string;
138
+ parser: "tfwiki" | "wikipedia" | "fandom";
139
+ categories: {
140
+ id: string;
141
+ path: string;
142
+ label: string;
143
+ }[];
144
+ fieldMapping: Record<string, {
145
+ type: "string" | "number" | "boolean" | "string-array";
146
+ target: string;
147
+ }>;
148
+ }, {
149
+ id: string;
150
+ label: string;
151
+ baseUrl: string;
152
+ apiUrl: string;
153
+ parser: "tfwiki" | "wikipedia" | "fandom";
154
+ categories: {
155
+ id: string;
156
+ path: string;
157
+ label: string;
158
+ }[];
159
+ fieldMapping: Record<string, {
160
+ type: "string" | "number" | "boolean" | "string-array";
161
+ target: string;
162
+ }>;
163
+ }>, "many">;
164
+ mergeRules: z.ZodRecord<z.ZodString, z.ZodBoolean>;
165
+ }, "strip", z.ZodTypeAny, {
166
+ sources: {
167
+ id: string;
168
+ label: string;
169
+ baseUrl: string;
170
+ apiUrl: string;
171
+ parser: "tfwiki" | "wikipedia" | "fandom";
172
+ categories: {
173
+ id: string;
174
+ path: string;
175
+ label: string;
176
+ }[];
177
+ fieldMapping: Record<string, {
178
+ type: "string" | "number" | "boolean" | "string-array";
179
+ target: string;
180
+ }>;
181
+ }[];
182
+ mergeRules: Record<string, boolean>;
183
+ }, {
184
+ sources: {
185
+ id: string;
186
+ label: string;
187
+ baseUrl: string;
188
+ apiUrl: string;
189
+ parser: "tfwiki" | "wikipedia" | "fandom";
190
+ categories: {
191
+ id: string;
192
+ path: string;
193
+ label: string;
194
+ }[];
195
+ fieldMapping: Record<string, {
196
+ type: "string" | "number" | "boolean" | "string-array";
197
+ target: string;
198
+ }>;
199
+ }[];
200
+ mergeRules: Record<string, boolean>;
201
+ }>>;
91
202
  }, "strip", z.ZodTypeAny, {
92
203
  databaseName: string;
93
204
  entryFormat: {
@@ -98,6 +209,25 @@ export declare const CAPSettingsSchema: z.ZodObject<{
98
209
  label: string;
99
210
  }[] | undefined;
100
211
  };
212
+ crawling?: {
213
+ sources: {
214
+ id: string;
215
+ label: string;
216
+ baseUrl: string;
217
+ apiUrl: string;
218
+ parser: "tfwiki" | "wikipedia" | "fandom";
219
+ categories: {
220
+ id: string;
221
+ path: string;
222
+ label: string;
223
+ }[];
224
+ fieldMapping: Record<string, {
225
+ type: "string" | "number" | "boolean" | "string-array";
226
+ target: string;
227
+ }>;
228
+ }[];
229
+ mergeRules: Record<string, boolean>;
230
+ } | undefined;
101
231
  }, {
102
232
  databaseName: string;
103
233
  entryFormat: {
@@ -108,6 +238,25 @@ export declare const CAPSettingsSchema: z.ZodObject<{
108
238
  label: string;
109
239
  }[] | undefined;
110
240
  };
241
+ crawling?: {
242
+ sources: {
243
+ id: string;
244
+ label: string;
245
+ baseUrl: string;
246
+ apiUrl: string;
247
+ parser: "tfwiki" | "wikipedia" | "fandom";
248
+ categories: {
249
+ id: string;
250
+ path: string;
251
+ label: string;
252
+ }[];
253
+ fieldMapping: Record<string, {
254
+ type: "string" | "number" | "boolean" | "string-array";
255
+ target: string;
256
+ }>;
257
+ }[];
258
+ mergeRules: Record<string, boolean>;
259
+ } | undefined;
111
260
  }>;
112
261
  export declare function generateDynamicSchema(customFields: CustomFieldConfig[]): z.ZodObject<z.ZodRawShape>;
113
262
  export declare function loadSettings(path: string): SettingsConfig;
package/dist/index.js CHANGED
@@ -1,6 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { z } from "zod";
4
+ // Re-export crawler
5
+ export { Crawler } from "./crawler/index.js";
6
+ export { MediaWikiClient } from "./crawler/mediawiki.js";
7
+ export { mergeEntry, defaultMergeRules } from "./crawler/merger.js";
8
+ export { getParser, parseTFWiki, parseWikipedia, parseFandom } from "./crawler/parsers/index.js";
9
+ export { parseGenericInfobox, parsePortableInfobox, extractFirstParagraph, extractCategories, extractPageTitle } from "./crawler/parser.js";
4
10
  // Runtime validation baseline
5
11
  export const CAPBaseSchema = z.object({
6
12
  name: z.string().min(1),
@@ -48,12 +54,32 @@ const CustomFieldSchema = z.object({
48
54
  type: z.enum(["string", "number", "boolean", "date"]),
49
55
  label: z.string().min(1),
50
56
  });
57
+ const CrawlSourceSchema = z.object({
58
+ id: z.string().min(1),
59
+ label: z.string().min(1),
60
+ baseUrl: z.string().url(),
61
+ apiUrl: z.string().url(),
62
+ parser: z.enum(["tfwiki", "wikipedia", "fandom"]),
63
+ categories: z.array(z.object({
64
+ id: z.string().min(1),
65
+ label: z.string().min(1),
66
+ path: z.string().min(1),
67
+ })),
68
+ fieldMapping: z.record(z.string(), z.object({
69
+ target: z.string().min(1),
70
+ type: z.enum(["string", "number", "boolean", "string-array"]),
71
+ })),
72
+ });
51
73
  export const CAPSettingsSchema = z.object({
52
74
  databaseName: z.string().min(1),
53
75
  entryFormat: z.object({
54
76
  requiredFields: z.array(z.string()).optional(),
55
77
  customFields: z.array(CustomFieldSchema).optional(),
56
78
  }),
79
+ crawling: z.object({
80
+ sources: z.array(CrawlSourceSchema),
81
+ mergeRules: z.record(z.string(), z.boolean()),
82
+ }).optional(),
57
83
  });
58
84
  function zodTypeForFieldType(fieldType) {
59
85
  switch (fieldType) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,19 @@
1
1
  {
2
2
  "name": "@crazygiscool/cap",
3
- "version": "1.1.9",
3
+ "version": "1.2.0",
4
4
  "description": "The Central Archive Protocol core library for lore documentation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ },
12
+ "./crawler": {
13
+ "import": "./dist/crawler/index.js",
14
+ "types": "./dist/crawler/index.d.ts"
15
+ }
16
+ },
7
17
  "files": [
8
18
  "dist",
9
19
  "README.md",
@@ -44,5 +54,8 @@
44
54
  "typescript": "^5.4.5",
45
55
  "vitest": "^2.1.9",
46
56
  "zod": "^3.22.4"
57
+ },
58
+ "dependencies": {
59
+ "cheerio": "^1.2.0"
47
60
  }
48
61
  }