wiki-entity 0.7.0 → 1.0.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 (46) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/LICENSE +15 -0
  3. package/README.md +287 -89
  4. package/dist/index.cjs +1312 -0
  5. package/dist/index.d.cts +370 -0
  6. package/dist/index.d.ts +370 -0
  7. package/dist/index.js +1296 -0
  8. package/package.json +59 -24
  9. package/data/countries.json +0 -1
  10. package/data/entity_types.json +0 -1
  11. package/lib/index.js +0 -129
  12. package/lib/request.js +0 -37
  13. package/lib/simpleEntity/convertToSimpleEntity.js +0 -53
  14. package/lib/simpleEntity/getEntityCountry.js +0 -25
  15. package/lib/simpleEntity/getEntityData.js +0 -27
  16. package/lib/simpleEntity/getEntityInstanceType.js +0 -32
  17. package/lib/simpleEntity/getEntityType.js +0 -62
  18. package/lib/simpleEntity/getEntityTypeByExtract.js +0 -28
  19. package/lib/simpleEntity/index.js +0 -7
  20. package/lib/simpleEntity/simpleEntity.js +0 -20
  21. package/lib/types.js +0 -13
  22. package/lib/utils.js +0 -15
  23. package/lib/wikidata/api.js +0 -106
  24. package/lib/wikidata/get_entity_types.js +0 -60
  25. package/lib/wikidata/index.js +0 -112
  26. package/lib/wikidata/simplify_claims.js +0 -100
  27. package/lib/wikidata/simplify_entity.js +0 -76
  28. package/lib/wikipedia/api.js +0 -146
  29. package/types/index.d.ts +0 -9
  30. package/types/request.d.ts +0 -5
  31. package/types/simpleEntity/convertToSimpleEntity.d.ts +0 -6
  32. package/types/simpleEntity/getEntityCountry.d.ts +0 -2
  33. package/types/simpleEntity/getEntityData.d.ts +0 -3
  34. package/types/simpleEntity/getEntityInstanceType.d.ts +0 -3
  35. package/types/simpleEntity/getEntityType.d.ts +0 -3
  36. package/types/simpleEntity/getEntityTypeByExtract.d.ts +0 -2
  37. package/types/simpleEntity/index.d.ts +0 -2
  38. package/types/simpleEntity/simpleEntity.d.ts +0 -29
  39. package/types/types.d.ts +0 -68
  40. package/types/utils.d.ts +0 -8
  41. package/types/wikidata/api.d.ts +0 -4
  42. package/types/wikidata/get_entity_types.d.ts +0 -1
  43. package/types/wikidata/index.d.ts +0 -3
  44. package/types/wikidata/simplify_claims.d.ts +0 -4
  45. package/types/wikidata/simplify_entity.d.ts +0 -21
  46. package/types/wikipedia/api.d.ts +0 -31
package/CHANGELOG.md ADDED
@@ -0,0 +1,172 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ This project adheres to [Semantic Versioning](https://semver.org/).
5
+
6
+ ## 1.0.0
7
+
8
+ A full modernization pass: the package is now dependency-free, dual ESM/CJS,
9
+ strictly typed, and covered by a real test suite. Several long-standing data
10
+ bugs are fixed, and the generated Wikidata tables have been rebuilt.
11
+
12
+ ### Breaking changes
13
+
14
+ - **Node.js >= 20.19 is required.** `axios` is gone; the package uses the
15
+ platform `fetch`.
16
+ - **Dual ESM/CommonJS build.** The package now has an `exports` map and ships
17
+ from `dist/`. Deep imports such as
18
+ `require("wiki-entity/lib/simpleEntity/getEntityCountry")` no longer resolve.
19
+ - **The public API is down to 14 exports.** It had grown to 40, many of which
20
+ were internals with no standalone use. Removed: the array helpers (`chunk`,
21
+ `uniq`), the id predicates (`isItemId`, `isPropertyId`, `isEntityId`), the
22
+ claim renderers (`stringifyTime`, `stringifyCoordinates`), the pieces of
23
+ `convertToSimpleEntity` (`getEntityData`, `getEntityCountryCode`,
24
+ `getEntityTypeByExtract`, and the two `getEntityType` variants), the
25
+ low-level claim simplifiers (`simplifyClaim`, `simplifyClaims`,
26
+ `simplifyPropertyClaims`, `exploreEntityClaims`), the deprecated
27
+ `WikipediaApi` builder and its `getExtract`/`getExtracts`/`getRedirects`
28
+ wrappers (use `queryPages`), the singular `getEntityTypesByName` (use
29
+ `getEntityTypesByNames`), and the constants `KNOWN_TYPE_PREFIXES`,
30
+ `SIMPLE_ENTITY_TYPES` and `WIKIDATA_PROPS`.
31
+ - **`redirect` is now `followEntityRedirects: boolean`.** `redirect: "yes"|"no"`
32
+ sat one letter away from the unrelated `redirects: boolean`, which fetches
33
+ Wikipedia redirect titles. There is no alias; rename at the call site.
34
+ - **`InvalidParamsError` is gone**; bad arguments now throw a plain `TypeError`.
35
+ - **`WikidataPropsParam` (enum) and `WIKIDATA_PROPS` (const) are gone.** `props`
36
+ takes plain strings, typed by the `WikidataProp` union.
37
+ - **`PlainObject`, `AnyPlainObject` and `StringPlainObject` are gone** — they
38
+ were aliases for `Record<string, T>`. The `Raw*` claim types no longer leak
39
+ either; `simplifyEntity` takes the documented `WikibaseEntityJson` shape.
40
+ - `ApiResult` is renamed `WikipediaPage`.
41
+ - **`SimpleEntity.lang` and `SimpleEntity.wikiDataId` are now required.**
42
+ `convertToSimpleEntity` always sets both, so typing them optional forced
43
+ callers into null checks that could never fire.
44
+ - **`SimpleEntity.abbr` is removed.** Nothing in the package ever assigned it.
45
+ - `WikiEntityToEntityOptions` is renamed `ConvertToSimpleEntityOptions`, after
46
+ the function it belongs to rather than one that no longer exists.
47
+ - **`pageid` now always means the Wikipedia page id.** It used to fall back to
48
+ Wikidata's own page id whenever the Wikipedia lookup was skipped
49
+ (`wikiPageId: false`, no sitelink, missing article), which silently produced a
50
+ wrong `wikiPageId` in `SimpleEntity`. It is now left unset in those cases.
51
+ - **Deprecated statements are dropped** and **preferred statements sort first**,
52
+ so `claims[p].values[0]` is the value Wikidata recommends. Pass
53
+ `keepDeprecatedClaims` to `simplifyEntity` to keep them.
54
+ - **`wikibase-property` claim values** are now the property id (`"P31"`) rather
55
+ than the raw Wikibase object, which used to stringify to `"[object Object]"`.
56
+ - `simplifySitelinks` and friends return `{}` instead of `null` for empty input.
57
+ - `convertToSimpleEntity` omits empty optional fields (`categories`, `data`,
58
+ `types`) instead of setting them to `[]`/`{}`.
59
+ - `getEntityType`, `getEntityTypeByExtract` and related helpers return
60
+ `undefined` rather than `null` when nothing matches.
61
+ - `getManyEntities` returns `{}` instead of `null` when nothing was found —
62
+ the `null` used to crash the caller.
63
+
64
+ ### Migrating
65
+
66
+ `getExtract`, `getExtracts` and `getRedirects` are replaced by `queryPages`,
67
+ which batches titles and follows paginated responses instead of sending one
68
+ request and truncating:
69
+
70
+ ```ts
71
+ // before // now
72
+ getExtracts({ lang, titles, sentences: 2 }); // queryPages({ lang, titles, extract: 2 })
73
+ getExtract(lang, title, 2); // queryPages({ lang, titles: [title], extract: 2 })
74
+ getRedirects(lang, title); // queryPages({ lang, titles: [title], redirects: true })
75
+ ```
76
+
77
+ `queryPages` returns `{ pages, resolved, requestedTitleOf }`. `pages` is not in
78
+ the order the titles were given, so use `requestedTitleOf` to map a result back
79
+ to the title you asked for. See the README for details.
80
+
81
+ The `WikipediaApi` builder maps onto the same call:
82
+
83
+ ```ts
84
+ // before
85
+ new WikipediaApi().extract(2).redirects().query("en", { titles: "A|B" });
86
+ // now
87
+ queryPages({ lang: "en", titles: ["A", "B"], extract: 2, redirects: true });
88
+ ```
89
+
90
+ ### Fixed
91
+
92
+ - **Labels went missing for a large share of items.** Wikidata has been moving
93
+ names that are identical across languages to the `mul` ("multiple languages")
94
+ code and deleting the per-language duplicates, which left `label` undefined
95
+ for entities such as Albert Einstein, Douglas Adams and iPhone 5. Labels and
96
+ aliases now fall back to `mul`, and `mul` is requested automatically alongside
97
+ any `languages` filter.
98
+ - **Only the first entity got an extract.** Without `exintro`, MediaWiki
99
+ silently lowers `exlimit` to 1, so `getEntities({ titles: [a, b, c], extract })`
100
+ returned an extract for `a` only.
101
+ - **`languages` was ignored.** It was serialized as PHP array syntax
102
+ (`languages[]=en&languages[]=ru`), which MediaWiki rejects with a warning and
103
+ then returns _every_ language for.
104
+ - **BCE dates lost their sign.** `-0044-03-15` (Julius Caesar's death) rendered
105
+ as `0044-03-15`.
106
+ - **Coordinates lost integer digits.** Trailing-zero trimming turned latitude
107
+ `10` into `1`.
108
+ - **`claims: "property"` never worked.** Property entities (`P31`, …) were
109
+ filtered out as invalid ids, so property labels and descriptions were never
110
+ populated.
111
+ - **The `types` prefix filter was ignored.** `types: ["dbo", "schema"]` computed
112
+ the allowed prefixes and then never applied them.
113
+ - **Wikipedia results were mismatched to entities.** MediaWiki normalizes titles
114
+ and follows redirects, so the returned title often differs from the requested
115
+ one; results are now mapped back through the normalization and redirect
116
+ chains. Previously this attached data to the wrong entity, or threw.
117
+ - **Titles that are redirects found no entity.** Single-title lookups now use
118
+ `normalize=1`, and multi-title lookups resolve anything Wikidata missed
119
+ through Wikipedia's canonical title.
120
+ - **Romanian extract patterns never matched.** They relied on `\b`, which is
121
+ ASCII-only, so words ending in a diacritic (`oraș`, `cântăreț`, `comună`)
122
+ could never match. Patterns are now Unicode-aware; English patterns added.
123
+ - More than 50 Wikipedia titles are now batched instead of being sent in one
124
+ over-long request, and paginated (`continue`) responses are followed, so
125
+ `redirects` and `categories` are no longer truncated at the first page.
126
+ - `getEntityData` no longer throws on a structured claim value with no readable
127
+ rendering; it skips it.
128
+ - DBpedia is queried over HTTPS, with the resource IRI properly escaped.
129
+
130
+ ### Added
131
+
132
+ - `getSimpleEntities(params, options?)` — `getEntities` plus
133
+ `convertToSimpleEntity` in one call, taking the language from `params` so it
134
+ cannot diverge from the language the entities were fetched in.
135
+ - `AbortSignal` support on every public call (`signal` param).
136
+ - Automatic retries with exponential backoff and jitter on 429/5xx/network
137
+ errors, honouring `Retry-After`.
138
+ - Typed error classes: `WikiEntityError`, `HttpError`, `ApiError`,
139
+ `InvalidParamsError`.
140
+ - `getEntityTypesByNames()` resolves DBpedia types for many entities in a single
141
+ `VALUES` query instead of one request per entity.
142
+ - `queryPages()` — the batching, continuation-following Wikipedia query used
143
+ internally, exported for direct use.
144
+ - `setDbpediaEndpoint()` / `getDbpediaEndpoint()` to use a DBpedia mirror.
145
+ - `rank` on claim values; `descriptions` map on entities; `datatype` on property
146
+ entities.
147
+ - `chunk`, `isItemId`, `isPropertyId`, `isEntityId` utilities.
148
+ - Unit tests (fully mocked) and an integration suite that runs against the live
149
+ APIs, plus ESLint, Prettier and strict TypeScript.
150
+
151
+ ### Changed
152
+
153
+ - **Claim resolution is batched.** `claims: "item"` used to issue one request
154
+ per entity, serially; all referenced items across all entities are now fetched
155
+ together, 50 per request.
156
+ - **Regenerated `data/entity-types.json` and `data/countries.json`** from the
157
+ live Wikidata Query Service. The type tables dated from 2018 and missed how
158
+ Wikidata now models products, recurring events and administrative entities.
159
+ The generator expands the class graph breadth-first with batched queries, and
160
+ verifies known classifications before writing.
161
+ - The type table is delta-encoded, cutting it from 456 KB to ~130 KB and
162
+ expanding lazily on first use.
163
+ - `isValidWikiId` is deprecated in favour of `isItemId` / `isEntityId`.
164
+
165
+ ## 0.7.0
166
+
167
+ - Added a default `User-Agent` built from `package.json`, plus `setUserAgent()`
168
+ and the `WIKI_ENTITY_USER_AGENT` environment variable.
169
+
170
+ ## 0.6.0
171
+
172
+ - Added `mapRedirects()`.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2016-present, Dumitru Cantea
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,24 +1,52 @@
1
1
  # wiki-entity
2
2
 
3
- Wiki(pedia/data) entity extractor.
3
+ Fetch and normalize entities from **Wikidata**, **Wikipedia** and **DBpedia**.
4
+
5
+ - Zero runtime dependencies — built on `fetch`, ESM + CommonJS, fully typed.
6
+ - Batches, retries and rate-limit aware by default.
7
+ - Flattens Wikidata's deeply nested claim format into something you can read.
8
+
9
+ ```bash
10
+ npm install wiki-entity
11
+ ```
12
+
13
+ Requires Node.js >= 20.19.
4
14
 
5
15
  ## Usage
6
16
 
17
+ ```ts
18
+ import { getEntities, setUserAgent } from "wiki-entity";
19
+
20
+ setUserAgent("MyApp/1.0 (https://myapp.example; contact@myapp.example)");
21
+
22
+ // by Wikipedia article title
23
+ const [europe] = await getEntities({ language: "en", titles: ["Europe"] });
24
+
25
+ // by Wikidata id, with a Wikipedia summary and resolved claim labels
26
+ const [einstein] = await getEntities({
27
+ language: "en",
28
+ ids: ["Q937"],
29
+ extract: 2,
30
+ claims: "all"
31
+ });
32
+
33
+ einstein.label; // "Albert Einstein"
34
+ einstein.extract; // "Albert Einstein was a German-born theoretical physicist…"
35
+ einstein.claims.P569.label; // "date of birth"
36
+ einstein.claims.P569.values[0].value_string; // "1879-03-14"
7
37
  ```
8
- import { getEntities } from 'wiki-entity';
9
38
 
10
- // get Europe by title
11
- getEntities({ language: 'en', titles: ['Europe'] }).then(entities => {});
39
+ CommonJS works too:
12
40
 
13
- // get Europe by id
14
- getEntities({ language: 'en', ids: ['Q46'] }).then(entities => {});
41
+ ```js
42
+ const { getEntities } = require("wiki-entity");
15
43
  ```
16
44
 
17
45
  ## User-Agent
18
46
 
19
- [Wikimedia's User-Agent policy](https://meta.wikimedia.org/wiki/User-Agent_policy) requires every client to send an identifying `User-Agent` header. Generic UAs are rate-limited or blocked (HTTP `429`).
20
-
21
- Since `0.7.0`, `wiki-entity` sends a default UA built from `package.json`, but you should override it with your app's name and a contact URL/email:
47
+ [Wikimedia's User-Agent policy](https://foundation.wikimedia.org/wiki/Policy:Wikimedia_Foundation_User-Agent_Policy)
48
+ requires every client to identify itself. Generic agents get throttled or
49
+ blocked with HTTP `429`. Set yours once at startup:
22
50
 
23
51
  ```ts
24
52
  import { setUserAgent } from "wiki-entity";
@@ -26,115 +54,285 @@ import { setUserAgent } from "wiki-entity";
26
54
  setUserAgent("MyApp/1.0 (https://myapp.example; contact@myapp.example)");
27
55
  ```
28
56
 
29
- Or set it via environment variable:
57
+ or via the environment:
30
58
 
31
- ```
59
+ ```bash
32
60
  WIKI_ENTITY_USER_AGENT="MyApp/1.0 (https://myapp.example; contact@myapp.example)"
33
61
  ```
34
62
 
35
63
  ## API
36
64
 
37
- ### getEntities(params): Promise<WikiEntity[]>
65
+ ### `getEntities(params): Promise<WikiEntity[]>`
66
+
67
+ Fetches entities from Wikidata and enriches them from Wikipedia and DBpedia.
68
+ Results follow the order of the requested `ids`/`titles`; entities that do not
69
+ exist are omitted.
70
+
71
+ | Param | Type | Default | Description |
72
+ | ----------------------- | --------------------- | ------- | ---------------------------------------------------------------------------------------------- |
73
+ | `ids` | `string[]` | — | Wikidata ids, max 500. Either this or `titles` is required. |
74
+ | `titles` | `string[]` | — | Wikipedia article titles in `language`, max 500. |
75
+ | `language` | `string` | `"en"` | Language of `titles` and of the resulting `label`/`description`. |
76
+ | `languages` | `string[]` | — | Extra languages to populate `labels` with. |
77
+ | `props` | `string[]` | all | `info`, `sitelinks`, `aliases`, `labels`, `descriptions`, `claims`, `datatype`. |
78
+ | `claims` | `string` | `none` | `none`, `item`, `property` or `all` — how deeply to resolve claim labels. |
79
+ | `extract` | `number` | — | Sentences of the Wikipedia lead section to fetch. |
80
+ | `types` | `boolean \| string[]` | `false` | `true` for DBpedia ontology types, or an array of prefixes to keep (e.g. `["dbo", "schema"]`). |
81
+ | `redirects` | `boolean` | `false` | Titles of _Wikipedia articles_ that redirect to this entity. |
82
+ | `categories` | `boolean` | `false` | The article's non-hidden categories. |
83
+ | `wikiPageId` | `boolean` | `true` | Fetch the Wikipedia `pageid`. |
84
+ | `httpTimeout` | `number` | `15000` | Per-request timeout in milliseconds. |
85
+ | `signal` | `AbortSignal` | — | Cancels every underlying request. |
86
+ | `followEntityRedirects` | `boolean` | `true` | Resolve _Wikidata items_ that were merged into another item. |
87
+
88
+ `extract`, `redirects` and `categories` need the entity to have a sitelink for
89
+ `language`, so keep `sitelinks` in `props` when you narrow it.
90
+
91
+ Note `redirects` and `followEntityRedirects` are unrelated: the first fetches
92
+ the titles of Wikipedia articles pointing at this entity, the second controls
93
+ whether a merged-away Wikidata item resolves to the item that replaced it.
94
+
95
+ ### `mapRedirects(titles, lang): Promise<Record<string, string>>`
96
+
97
+ Resolves Wikipedia redirect titles to the articles they point at. Only titles
98
+ that really are redirects appear in the result.
38
99
 
39
- Gets entities from wikidata and wikipedia.
100
+ ```ts
101
+ await mapRedirects(["Brashov"], "ro"); // { Brashov: "Brașov" }
102
+ ```
40
103
 
41
- #### params
104
+ ### `getSimpleEntities(params, options?): Promise<SimpleEntity[]>`
42
105
 
43
- Required. `ids` or `titles` require. Params properties:
106
+ `getEntities` followed by `convertToSimpleEntity`, in one call. Takes the same
107
+ params, plus an optional `{ defaultType }`.
44
108
 
45
- - **ids** :*string[]* - wikidata ids; (max 500)
46
- - **titles** :*string[]* - wikipedia article titles; (max 500)
47
- - **language** :*string* - language of `titles` param and of the result object info; default: `en`
48
- - **props** :*string[]* - entity props to get. Can by: `info`|`sitelinks`|`aliases`|`labels`|`descriptions`|`claims`|`datatype`;
49
- - **claims** :*string* - How to resolve the claims. Can be: `none`, `all`, `item`, `property`. Default: `none`. `all` resolves `item` and `property` types.
50
- - **extract** :*number* - Sentences in the extract. Default: `0`. Works only if `sitelinks` is present.
51
- - **types** :*boolean* | *string*[] - `true` to get entity types. Filter types by [prefixes](https://dbpedia.org/sparql?nsdecl). Example: [`dbo`, `schema`] will return only types defined by `dbpedia.org/ontology/` and `schema.org`. Default: `false`.
52
- - **redirects** : *boolean* - get wikipedia redirects titles. Default: `false`. Works only if `sitelinks` is present.
53
- - **categories** : *boolean* - get wikipedia article categories. Default: `false`. Works only if `sitelinks` is present.
54
- - **httpTimeout**: *number* - http requests timeout
55
- - **languages**: *string[]* - languages for `WikiEntity.labels`
56
- - **wikiPageId**: *boolean* - get wikiPageId, Default `true`
109
+ ```ts
110
+ const entities = await getSimpleEntities({
111
+ language: "en",
112
+ ids: ["Q937"],
113
+ types: true,
114
+ extract: 2
115
+ });
116
+
117
+ entities[0].name; // "Albert Einstein"
118
+ entities[0].type; // SimpleEntityType.PERSON
119
+ ```
57
120
 
58
- ### convertToSimpleEntity(wikiEntity: WikiEntity): SimpleEntity
121
+ Prefer this over calling the two by hand: it takes the language from `params`,
122
+ so it cannot drift. Converting with a different language than you fetched with
123
+ silently mixes them — `name` and `about` come from the fetch, while
124
+ `wikiPageTitle` is read from the sitelinks of whichever language you passed.
59
125
 
60
- Convert a complex WikiEntity object to SimpleEntity object.
126
+ ### `convertToSimpleEntity(wikiEntity, lang, options?): SimpleEntity`
61
127
 
62
- ## WikiEntity
128
+ Flattens a single `WikiEntity` into the compact shape. Use it when you already
129
+ have entities from `getEntities`.
63
130
 
64
- `WikiEntity` is a simple version of an item returned by Wikidata API. It also may include some extra properties from Wikipadia API.
131
+ ### Escape hatches
132
+
133
+ For what `getEntities` does not cover. Everything else in the package is an
134
+ implementation detail and may change without a major release.
135
+
136
+ | Export | Purpose |
137
+ | ------------------------------------------- | --------------------------------------------------------------- |
138
+ | `queryPages(options)` | Query Wikipedia articles directly — batched, continuation-safe. |
139
+ | `simplifyEntity(lang, raw, options?)` | Flatten a raw `wbgetentities` entity you fetched yourself. |
140
+ | `getEntityTypesByNames(names, options?)` | DBpedia ontology types for English Wikipedia titles. |
141
+ | `setDbpediaEndpoint` / `getDbpediaEndpoint` | Point type lookups at a DBpedia mirror. |
142
+ | `setUserAgent` / `getUserAgent` | The `User-Agent` sent with every request. |
143
+
144
+ #### `queryPages(options)`
145
+
146
+ Talks to Wikipedia only — no Wikidata lookup, no entity ids. Use it when you
147
+ have article titles and want their summaries, redirects or categories.
65
148
 
66
149
  ```ts
150
+ import { queryPages } from "wiki-entity";
67
151
 
68
- type WikiEntity = {
69
- id: string;
70
- label?: string;
71
- labels?: PlainObject<string>;
72
- description?: string;
73
- extract?: string;
74
- pageid?: number;
75
- aliases?: string[];
76
- sitelinks?: PlainObject<string>;
77
- claims?: PlainObject<WikidataProperty>;
78
- types?: string[];
79
- redirects?: string[];
80
- categories?: string[];
81
- redirectsToId?: string;
82
- redirectsFromId?: string;
152
+ const { pages } = await queryPages({
153
+ lang: "en",
154
+ titles: ["Chișinău", "Italy"],
155
+ extract: 2,
156
+ followRedirects: true
157
+ });
158
+
159
+ for (const page of pages) {
160
+ console.log(page.pageid, page.title, page.extract);
83
161
  }
162
+ ```
163
+
164
+ | Option | Type | Description |
165
+ | ----------------- | ------------- | ------------------------------------------------ |
166
+ | `lang` | `string` | Wikipedia language code, e.g. `"en"`. Required. |
167
+ | `titles` | `string[]` | Article titles. Required; batched automatically. |
168
+ | `extract` | `number` | Sentences of the lead section to fetch. |
169
+ | `redirects` | `boolean` | Titles of articles redirecting to each page. |
170
+ | `categories` | `boolean` | Each article's non-hidden categories. |
171
+ | `followRedirects` | `boolean` | Resolve titles that are themselves redirects. |
172
+ | `httpTimeout` | `number` | Per-request timeout in milliseconds. |
173
+ | `signal` | `AbortSignal` | Cancels every underlying request. |
174
+
175
+ It handles the parts of the MediaWiki API that are easy to get wrong: titles are
176
+ batched (50 per request, or 20 when `extract` is set, since the API refuses more
177
+ intro extracts than that), and paginated `continue` responses are followed, so
178
+ long `redirects` and `categories` lists are not truncated at the first page.
179
+
180
+ **`pages` is not in the order you asked for**, and MediaWiki normalizes titles
181
+ and follows redirects, so the title that comes back may not be the one you sent.
182
+ Two maps tie the results back to your input:
183
+
184
+ ```ts
185
+ const { pages, resolved, requestedTitleOf } = await queryPages({
186
+ lang: "en",
187
+ titles: ["Kishinev"],
188
+ categories: true,
189
+ followRedirects: true
190
+ });
191
+
192
+ pages[0].title; // "Chișinău" — the redirect target
193
+ resolved.get("Kishinev"); // { title: "Chișinău", redirected: true }
194
+ requestedTitleOf.get("Chișinău"); // "Kishinev"
195
+ ```
196
+
197
+ Earlier releases had `getExtract`, `getExtracts` and `getRedirects` for this.
198
+ They were thin wrappers that only ever handled one request's worth of titles:
199
+
200
+ ```ts
201
+ // before // now
202
+ getExtracts({ lang, titles, sentences: 2 }); // queryPages({ lang, titles, extract: 2 })
203
+ getExtract(lang, title, 2); // queryPages({ lang, titles: [title], extract: 2 })
204
+ getRedirects(lang, title); // queryPages({ lang, titles: [title], redirects: true })
205
+ ```
206
+
207
+ `getExtract` returned a single object or `null`; the equivalent is
208
+ `(await queryPages({ lang, titles: [title], extract: 2 })).pages[0] ?? null`.
209
+
210
+ ### Errors
211
+
212
+ All errors extend `WikiEntityError`:
213
+
214
+ - `HttpError` — non-2xx status, network failure or timeout. `status`, `url` and
215
+ `retryable` describe it; 429 and 5xx are retried automatically.
216
+ - `ApiError` — the request succeeded but MediaWiki reported an error (`code`).
217
+
218
+ Bad arguments throw a plain `TypeError`.
219
+
220
+ DBpedia types are best-effort enrichment: if the endpoint is unreachable,
221
+ `types` is simply left unset instead of failing the call.
222
+
223
+ ## WikiEntity
224
+
225
+ `WikiEntity` is a flattened Wikidata item, plus the extras pulled from
226
+ Wikipedia and DBpedia.
227
+
228
+ ```ts
229
+ type WikiEntity = {
230
+ id: string;
231
+ label?: string;
232
+ labels?: Record<string, string>;
233
+ description?: string;
234
+ descriptions?: Record<string, string>;
235
+ aliases?: string[];
236
+ sitelinks?: Record<string, string>;
237
+ claims?: Record<string, WikidataProperty>;
238
+ /** Wikipedia page id (not Wikidata's). */
239
+ pageid?: number;
240
+ extract?: string;
241
+ types?: string[];
242
+ redirects?: string[];
243
+ categories?: string[];
244
+ redirectsToId?: string;
245
+ redirectsFromId?: string;
246
+ };
84
247
 
85
248
  type WikidataProperty = {
86
- id: string;
87
- label?: string;
88
- description?: string;
89
- values: WikidataPropertyValue[];
90
- }
249
+ id: string;
250
+ label?: string;
251
+ description?: string;
252
+ values: WikidataPropertyValue[];
253
+ };
91
254
 
92
255
  type WikidataPropertyValue = {
93
- datatype: string;
94
- value: any;
95
- pageid?: number;
96
- value_string?: string;
97
- label?: string;
98
- description?: string;
99
- qualifiers?: WikidataEntityClaims | null;
100
- }
256
+ datatype: string;
257
+ /** Scalar for simple datatypes, the raw object for time/quantity/coordinates. */
258
+ value: string | number | object;
259
+ /** Readable rendering of a structured `value`. */
260
+ value_string?: string;
261
+ rank?: "preferred" | "normal" | "deprecated";
262
+ label?: string;
263
+ description?: string;
264
+ qualifiers?: Record<string, WikidataProperty> | null;
265
+ };
266
+ ```
101
267
 
102
- type PlainObject<T> = {
103
- [index: string]: T
104
- }
268
+ Notes on the claim values:
105
269
 
106
- ```
270
+ - Statements Wikidata marks **deprecated** are dropped, and **preferred** ones
271
+ are sorted first, so `values[0]` is the value Wikidata recommends.
272
+ - Dates render at their stated precision and keep the sign of BCE years:
273
+ `-0044-03-15`.
274
+ - Coordinates render as `"47.0105,28.8638"`.
275
+ - Labels fall back to Wikidata's `mul` ("multiple languages") code, which is
276
+ where many items now keep a name that is spelled the same everywhere.
107
277
 
108
278
  ## SimpleEntity
109
279
 
110
280
  ```ts
111
-
112
- export enum SimpleEntityType {
113
- EVENT = 'E',
114
- ORG = 'O',
115
- PERSON = 'H',
116
- PLACE = 'P',
117
- PRODUCT = 'R',
118
- WORK = 'W',
281
+ enum SimpleEntityType {
282
+ EVENT = "E",
283
+ ORG = "O",
284
+ PERSON = "H",
285
+ PLACE = "P",
286
+ PRODUCT = "R",
287
+ WORK = "W"
119
288
  }
120
289
 
121
- export type SimpleEntityData = { [prop: string]: string[] }
122
-
123
- export type SimpleEntity = {
124
- lang?: string
125
- wikiDataId?: string
126
- name?: string
127
- abbr?: string
128
- description?: string
129
- about?: string
130
- wikiPageId?: number
131
- wikiPageTitle?: string
132
- type?: SimpleEntityType
133
- types?: string[]
134
- countryCodes?: string[]
135
- data?: SimpleEntityData
136
- categories?: string[]
137
- redirectsToId?: string;
138
- }
290
+ type SimpleEntity = {
291
+ /** Always set. */
292
+ lang: string;
293
+ /** Always set. */
294
+ wikiDataId: string;
295
+ name?: string;
296
+ description?: string;
297
+ about?: string;
298
+ wikiPageId?: number;
299
+ wikiPageTitle?: string;
300
+ type?: SimpleEntityType;
301
+ types?: string[];
302
+ countryCodes?: string[];
303
+ data?: Record<string, string[]>;
304
+ categories?: string[];
305
+ redirectsToId?: string;
306
+ redirectsFromId?: string;
307
+ };
308
+ ```
309
+
310
+ The entity `type` is resolved from the DBpedia ontology types first, then from
311
+ the `P31` claims against the generated Wikidata class tables, and finally from
312
+ the opening words of the extract.
313
+
314
+ ## Generated data
139
315
 
316
+ `data/countries.json` and `data/entity-types.json` are derived from Wikidata
317
+ and checked in. Regenerate them against the live query service with:
318
+
319
+ ```bash
320
+ npm run data:countries
321
+ npm run data:entity-types
140
322
  ```
323
+
324
+ `data:entity-types` verifies a set of known classifications before writing, so
325
+ a bad expansion fails loudly instead of silently degrading type detection.
326
+
327
+ ## Development
328
+
329
+ ```bash
330
+ npm install
331
+ npm test # unit tests, fully mocked
332
+ npm run test:integration # hits the live Wikimedia and DBpedia APIs
333
+ npm run check # format, lint, typecheck, test, build
334
+ ```
335
+
336
+ ## License
337
+
338
+ ISC