@miichom/lodestone 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.md +21 -0
- package/README.md +133 -0
- package/dist/index.d.ts +746 -0
- package/dist/index.js +1 -0
- package/package.json +63 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 miichom
|
|
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,133 @@
|
|
|
1
|
+
# @miichom/lodestone
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@miichom/lodestone)
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
A **minimal, fully typed [Lodestone](https://na.finalfantasyxiv.com/lodestone/) client** for _[Final Fantasy XIV](https://www.finalfantasyxiv.com/)_, providing access to **all endpoints exposed by the Lodestone** through a consistent, schema-driven API.
|
|
7
|
+
|
|
8
|
+
Designed for **server-side and worker runtimes**: [Node.js 20+](https://nodejs.org/), [Bun](https://bun.sh/), [Cloudflare Workers](https://developers.cloudflare.com/workers/), and [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).
|
|
9
|
+
|
|
10
|
+
## Why?
|
|
11
|
+
|
|
12
|
+
Most Lodestone scrapers rely on DOM emulation (e.g. [JSDOM](https://github.com/jsdom/jsdom), [cheerio](https://cheerio.js.org/)), which increases bundle size, slows cold starts, and often breaks in edge or serverless environments.
|
|
13
|
+
|
|
14
|
+
`@miichom/lodestone` avoids DOM dependencies entirely, using a **predictable, schema-driven parsing model** that works consistently across modern runtimes.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- 🪶 Lightweight and dependency-minimal
|
|
19
|
+
- 🧭 Fully typed [TypeScript](https://www.typescriptlang.org/) API
|
|
20
|
+
- ⚡ Edge- and serverless-friendly
|
|
21
|
+
- 🧱 No DOM, no JSDOM, no cheerio
|
|
22
|
+
- 🌐 Access to all Lodestone endpoints
|
|
23
|
+
- 🔍 Search and lookup across supported resources
|
|
24
|
+
- 🧩 Optional column-based data fetching
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @miichom/lodestone
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Example usage
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import Lodestone from "@miichom/lodestone";
|
|
36
|
+
|
|
37
|
+
const ls = new Lodestone();
|
|
38
|
+
|
|
39
|
+
// fetch a character
|
|
40
|
+
const character = await ls.character.get(12345678);
|
|
41
|
+
|
|
42
|
+
// fetch specific columns
|
|
43
|
+
const partial = await ls.character.get(12345678, {
|
|
44
|
+
columns: ["mount"],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// search characters
|
|
48
|
+
const results = await ls.character.find({
|
|
49
|
+
q: "Y'shtola",
|
|
50
|
+
worldname: "Twintania",
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> Additional Lodestone endpoints follow the same API pattern and are exposed through their respective namespaces.
|
|
55
|
+
|
|
56
|
+
## Options
|
|
57
|
+
|
|
58
|
+
The `Lodestone` constructor accepts a small set of configuration options.
|
|
59
|
+
These apply globally to all endpoints (`character`, `cwls`, `freecompany`, `linkshell`, `pvpteam`).
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const ls = new Lodestone({
|
|
63
|
+
locale: "eu",
|
|
64
|
+
headers: {
|
|
65
|
+
"user-agent": "my-xiv-tool/1.0",
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### `locale?: "de" | "eu" | "fr" | "jp" | "na"`
|
|
71
|
+
|
|
72
|
+
Selects which Lodestone region to target. Defaults to **`"na"`**.
|
|
73
|
+
|
|
74
|
+
Each locale maps to its own Lodestone instance:
|
|
75
|
+
|
|
76
|
+
- `na` → https://na.finalfantasyxiv.com/lodestone
|
|
77
|
+
- `eu` → https://eu.finalfantasyxiv.com/lodestone
|
|
78
|
+
- `jp` → https://jp.finalfantasyxiv.com/lodestone
|
|
79
|
+
- `fr` → https://fr.finalfantasyxiv.com/lodestone
|
|
80
|
+
- `de` → https://de.finalfantasyxiv.com/lodestone
|
|
81
|
+
|
|
82
|
+
All requests made through the client automatically use the selected locale.
|
|
83
|
+
|
|
84
|
+
### `headers?: Record<string, string>`
|
|
85
|
+
|
|
86
|
+
Optional request headers applied to every Lodestone request.
|
|
87
|
+
|
|
88
|
+
- Header keys are normalized to lowercase.
|
|
89
|
+
- A default User‑Agent is always prepended:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
curl/0.1.0 (+https://github.com/miichom/lodestone)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If you provide your own `user-agent`, it is appended:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
headers: {
|
|
99
|
+
"user-agent": "my-app/1.0",
|
|
100
|
+
}
|
|
101
|
+
// → curl/0.1.0 (+https://github.com/miichom/lodestone) my-app/1.0
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For more information on the `User-Agent` header, please see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/User-Agent.
|
|
105
|
+
|
|
106
|
+
> While optional, providing a custom User‑Agent is recommended for any automated or high‑volume usage.
|
|
107
|
+
|
|
108
|
+
### Column options (for `get` only)
|
|
109
|
+
|
|
110
|
+
Some endpoints (notably **characters**) expose additional “column” pages on Lodestone.
|
|
111
|
+
You can request them via the `columns` option:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const profile = await ls.character.get(12345678, {
|
|
115
|
+
columns: ["mount", "minion"],
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Columns are fetched lazily and merged into the returned object.
|
|
120
|
+
|
|
121
|
+
## Attribution
|
|
122
|
+
|
|
123
|
+
_Final Fantasy XIV_ and all related assets, including data accessed through the [Lodestone](https://na.finalfantasyxiv.com/lodestone/), are the intellectual property of © [SQUARE ENIX CO., LTD.](https://www.square-enix.com/) All rights reserved.
|
|
124
|
+
|
|
125
|
+
This project is not affiliated with or endorsed by Square Enix.
|
|
126
|
+
|
|
127
|
+
# Contributing
|
|
128
|
+
|
|
129
|
+
See [`CONTRIBUTING.md`](.github/CONTRIBUTING.md).
|
|
130
|
+
|
|
131
|
+
# License
|
|
132
|
+
|
|
133
|
+
See [`LICENSE.md`](./LICENSE.md).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
type Primitives = {
|
|
2
|
+
boolean: boolean;
|
|
3
|
+
number: number;
|
|
4
|
+
string: string;
|
|
5
|
+
date: Date;
|
|
6
|
+
url: URL;
|
|
7
|
+
};
|
|
8
|
+
type Primitive = keyof Primitives;
|
|
9
|
+
type Selector = {
|
|
10
|
+
type: `${Primitive}[]` | Primitive;
|
|
11
|
+
selector: string;
|
|
12
|
+
attribute?: string;
|
|
13
|
+
regex?: RegExp;
|
|
14
|
+
};
|
|
15
|
+
type SelectorShape = {
|
|
16
|
+
shape: {
|
|
17
|
+
root: Selector;
|
|
18
|
+
} & Selectors;
|
|
19
|
+
type: "object[]";
|
|
20
|
+
} | {
|
|
21
|
+
shape: Selectors;
|
|
22
|
+
type: "object";
|
|
23
|
+
};
|
|
24
|
+
type Selectors = {
|
|
25
|
+
[key: string]: Selector | SelectorShape;
|
|
26
|
+
};
|
|
27
|
+
type QueryShape = {
|
|
28
|
+
type: Primitive;
|
|
29
|
+
pattern?: RegExp;
|
|
30
|
+
required?: boolean;
|
|
31
|
+
};
|
|
32
|
+
type Registry = {
|
|
33
|
+
path: string;
|
|
34
|
+
item: {
|
|
35
|
+
fields: Selectors;
|
|
36
|
+
columns?: Selectors;
|
|
37
|
+
};
|
|
38
|
+
list: {
|
|
39
|
+
query: Record<string, QueryShape>;
|
|
40
|
+
fields: Selectors;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
type ExtractPrimitive<T extends Primitive> = Primitives[T];
|
|
44
|
+
type ExtractQuery<T extends QueryShape> = T["type"];
|
|
45
|
+
type RequiredKeys<T> = {
|
|
46
|
+
[K in keyof T]: T[K] extends {
|
|
47
|
+
required: true;
|
|
48
|
+
} ? K : never;
|
|
49
|
+
}[keyof T];
|
|
50
|
+
type InferQuery<R extends Registry> = Partial<{
|
|
51
|
+
[K in keyof R["list"]["query"]]: ExtractPrimitive<ExtractQuery<R["list"]["query"][K]>>;
|
|
52
|
+
}> & {
|
|
53
|
+
[K in RequiredKeys<R["list"]["query"]>]: ExtractPrimitive<ExtractQuery<R["list"]["query"][K]>>;
|
|
54
|
+
};
|
|
55
|
+
type InferSelectors<T extends Selectors> = {
|
|
56
|
+
[K in keyof T]: InferSelector<T[K]>;
|
|
57
|
+
};
|
|
58
|
+
type InferSelector<T> = T extends {
|
|
59
|
+
type: infer U;
|
|
60
|
+
} ? U extends keyof Primitives ? Primitives[U] : U extends `${infer Base}[]` ? Base extends keyof Primitives ? Primitives[Base][] : Base extends "object" ? InferSelectors<T extends SelectorShape ? T["shape"] : never>[] : never : U extends "object" ? InferSelectors<T extends SelectorShape ? T["shape"] : never> : never : never;
|
|
61
|
+
type InferItem<R extends Registry> = InferSelectors<R["item"]["fields"]>;
|
|
62
|
+
type InferList<R extends Registry> = InferSelectors<R["list"]["fields"]>;
|
|
63
|
+
type InferColumns<R extends Registry> = R["item"]["columns"] extends Selectors ? {
|
|
64
|
+
[K in keyof R["item"]["columns"]]: InferSelector<R["item"]["columns"][K]>;
|
|
65
|
+
} : never;
|
|
66
|
+
declare const character: {
|
|
67
|
+
item: {
|
|
68
|
+
columns: {
|
|
69
|
+
achievement: {
|
|
70
|
+
shape: {
|
|
71
|
+
score: {
|
|
72
|
+
selector: string;
|
|
73
|
+
type: "number";
|
|
74
|
+
};
|
|
75
|
+
total: {
|
|
76
|
+
regex: RegExp;
|
|
77
|
+
selector: string;
|
|
78
|
+
type: "number";
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
type: "object";
|
|
82
|
+
};
|
|
83
|
+
faceaccessory: {
|
|
84
|
+
selector: string;
|
|
85
|
+
type: "number";
|
|
86
|
+
};
|
|
87
|
+
minion: {
|
|
88
|
+
selector: string;
|
|
89
|
+
type: "number";
|
|
90
|
+
};
|
|
91
|
+
mount: {
|
|
92
|
+
selector: string;
|
|
93
|
+
type: "number";
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
fields: {
|
|
97
|
+
avatar: {
|
|
98
|
+
attribute: string;
|
|
99
|
+
selector: string;
|
|
100
|
+
type: "string";
|
|
101
|
+
};
|
|
102
|
+
bio: {
|
|
103
|
+
selector: string;
|
|
104
|
+
type: "string";
|
|
105
|
+
};
|
|
106
|
+
data_center: {
|
|
107
|
+
regex: RegExp;
|
|
108
|
+
selector: string;
|
|
109
|
+
type: "string";
|
|
110
|
+
};
|
|
111
|
+
free_company: {
|
|
112
|
+
shape: {
|
|
113
|
+
crest: {
|
|
114
|
+
attribute: string;
|
|
115
|
+
selector: string;
|
|
116
|
+
type: "string[]";
|
|
117
|
+
};
|
|
118
|
+
id: {
|
|
119
|
+
attribute: string;
|
|
120
|
+
regex: RegExp;
|
|
121
|
+
selector: string;
|
|
122
|
+
type: "string";
|
|
123
|
+
};
|
|
124
|
+
name: {
|
|
125
|
+
selector: string;
|
|
126
|
+
type: "string";
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
type: "object";
|
|
130
|
+
};
|
|
131
|
+
grand_company: {
|
|
132
|
+
shape: {
|
|
133
|
+
name: {
|
|
134
|
+
regex: RegExp;
|
|
135
|
+
selector: string;
|
|
136
|
+
type: "string";
|
|
137
|
+
};
|
|
138
|
+
rank: {
|
|
139
|
+
regex: RegExp;
|
|
140
|
+
selector: string;
|
|
141
|
+
type: "string";
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
type: "object";
|
|
145
|
+
};
|
|
146
|
+
id: {
|
|
147
|
+
attribute: string;
|
|
148
|
+
regex: RegExp;
|
|
149
|
+
selector: string;
|
|
150
|
+
type: "string";
|
|
151
|
+
};
|
|
152
|
+
name: {
|
|
153
|
+
selector: string;
|
|
154
|
+
type: "string";
|
|
155
|
+
};
|
|
156
|
+
portrait: {
|
|
157
|
+
attribute: string;
|
|
158
|
+
selector: string;
|
|
159
|
+
type: "string";
|
|
160
|
+
};
|
|
161
|
+
pvp_team: {
|
|
162
|
+
shape: {
|
|
163
|
+
crest: {
|
|
164
|
+
attribute: string;
|
|
165
|
+
selector: string;
|
|
166
|
+
type: "string[]";
|
|
167
|
+
};
|
|
168
|
+
id: {
|
|
169
|
+
attribute: string;
|
|
170
|
+
regex: RegExp;
|
|
171
|
+
selector: string;
|
|
172
|
+
type: "string";
|
|
173
|
+
};
|
|
174
|
+
name: {
|
|
175
|
+
selector: string;
|
|
176
|
+
type: "string";
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
type: "object";
|
|
180
|
+
};
|
|
181
|
+
title: {
|
|
182
|
+
selector: string;
|
|
183
|
+
type: "string";
|
|
184
|
+
};
|
|
185
|
+
world_name: {
|
|
186
|
+
regex: RegExp;
|
|
187
|
+
selector: string;
|
|
188
|
+
type: "string";
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
list: {
|
|
193
|
+
fields: {
|
|
194
|
+
avatar: {
|
|
195
|
+
attribute: string;
|
|
196
|
+
selector: string;
|
|
197
|
+
type: "string";
|
|
198
|
+
};
|
|
199
|
+
data_center: {
|
|
200
|
+
regex: RegExp;
|
|
201
|
+
selector: string;
|
|
202
|
+
type: "string";
|
|
203
|
+
};
|
|
204
|
+
grand_company: {
|
|
205
|
+
shape: {
|
|
206
|
+
name: {
|
|
207
|
+
attribute: string;
|
|
208
|
+
regex: RegExp;
|
|
209
|
+
selector: string;
|
|
210
|
+
type: "string";
|
|
211
|
+
};
|
|
212
|
+
rank: {
|
|
213
|
+
attribute: string;
|
|
214
|
+
regex: RegExp;
|
|
215
|
+
selector: string;
|
|
216
|
+
type: "string";
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
type: "object";
|
|
220
|
+
};
|
|
221
|
+
id: {
|
|
222
|
+
attribute: string;
|
|
223
|
+
regex: RegExp;
|
|
224
|
+
selector: string;
|
|
225
|
+
type: "string";
|
|
226
|
+
};
|
|
227
|
+
name: {
|
|
228
|
+
selector: string;
|
|
229
|
+
type: "string";
|
|
230
|
+
};
|
|
231
|
+
world_name: {
|
|
232
|
+
regex: RegExp;
|
|
233
|
+
selector: string;
|
|
234
|
+
type: "string";
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
query: {
|
|
238
|
+
blog_lang: {
|
|
239
|
+
pattern: RegExp;
|
|
240
|
+
type: "string";
|
|
241
|
+
};
|
|
242
|
+
classjob: {
|
|
243
|
+
pattern: RegExp;
|
|
244
|
+
type: "string";
|
|
245
|
+
};
|
|
246
|
+
gcid: {
|
|
247
|
+
pattern: RegExp;
|
|
248
|
+
type: "string";
|
|
249
|
+
};
|
|
250
|
+
order: {
|
|
251
|
+
pattern: RegExp;
|
|
252
|
+
type: "string";
|
|
253
|
+
};
|
|
254
|
+
q: {
|
|
255
|
+
required: true;
|
|
256
|
+
type: "string";
|
|
257
|
+
};
|
|
258
|
+
race_tribe: {
|
|
259
|
+
pattern: RegExp;
|
|
260
|
+
type: "string";
|
|
261
|
+
};
|
|
262
|
+
worldname: {
|
|
263
|
+
pattern: RegExp;
|
|
264
|
+
type: "string";
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
path: string;
|
|
269
|
+
};
|
|
270
|
+
declare const cwls: {
|
|
271
|
+
item: {
|
|
272
|
+
fields: {
|
|
273
|
+
data_center: {
|
|
274
|
+
selector: string;
|
|
275
|
+
type: "string";
|
|
276
|
+
};
|
|
277
|
+
formed: {
|
|
278
|
+
regex: RegExp;
|
|
279
|
+
selector: string;
|
|
280
|
+
type: "string";
|
|
281
|
+
};
|
|
282
|
+
members: {
|
|
283
|
+
regex: RegExp;
|
|
284
|
+
selector: string;
|
|
285
|
+
type: "number";
|
|
286
|
+
};
|
|
287
|
+
name: {
|
|
288
|
+
regex: RegExp;
|
|
289
|
+
selector: string;
|
|
290
|
+
type: "string";
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
};
|
|
294
|
+
list: {
|
|
295
|
+
fields: {
|
|
296
|
+
data_center: {
|
|
297
|
+
selector: string;
|
|
298
|
+
type: "string";
|
|
299
|
+
};
|
|
300
|
+
id: {
|
|
301
|
+
attribute: string;
|
|
302
|
+
regex: RegExp;
|
|
303
|
+
selector: string;
|
|
304
|
+
type: "string";
|
|
305
|
+
};
|
|
306
|
+
members: {
|
|
307
|
+
selector: string;
|
|
308
|
+
type: "string";
|
|
309
|
+
};
|
|
310
|
+
name: {
|
|
311
|
+
selector: string;
|
|
312
|
+
type: "string";
|
|
313
|
+
};
|
|
314
|
+
};
|
|
315
|
+
query: {
|
|
316
|
+
cf_public: {
|
|
317
|
+
type: "boolean";
|
|
318
|
+
};
|
|
319
|
+
character_count: {
|
|
320
|
+
pattern: RegExp;
|
|
321
|
+
type: "string";
|
|
322
|
+
};
|
|
323
|
+
dcname: {
|
|
324
|
+
pattern: RegExp;
|
|
325
|
+
type: "string";
|
|
326
|
+
};
|
|
327
|
+
order: {
|
|
328
|
+
pattern: RegExp;
|
|
329
|
+
type: "string";
|
|
330
|
+
};
|
|
331
|
+
q: {
|
|
332
|
+
required: true;
|
|
333
|
+
type: "string";
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
path: string;
|
|
338
|
+
};
|
|
339
|
+
declare const freecompany: {
|
|
340
|
+
item: {
|
|
341
|
+
fields: {
|
|
342
|
+
crest: {
|
|
343
|
+
attribute: string;
|
|
344
|
+
selector: string;
|
|
345
|
+
type: "string[]";
|
|
346
|
+
};
|
|
347
|
+
data_center: {
|
|
348
|
+
regex: RegExp;
|
|
349
|
+
selector: string;
|
|
350
|
+
type: "string";
|
|
351
|
+
};
|
|
352
|
+
estate: {
|
|
353
|
+
shape: {
|
|
354
|
+
greeting: {
|
|
355
|
+
selector: string;
|
|
356
|
+
type: "string";
|
|
357
|
+
};
|
|
358
|
+
name: {
|
|
359
|
+
selector: string;
|
|
360
|
+
type: "string";
|
|
361
|
+
};
|
|
362
|
+
plot: {
|
|
363
|
+
selector: string;
|
|
364
|
+
type: "string";
|
|
365
|
+
};
|
|
366
|
+
};
|
|
367
|
+
type: "object";
|
|
368
|
+
};
|
|
369
|
+
formed: {
|
|
370
|
+
regex: RegExp;
|
|
371
|
+
selector: string;
|
|
372
|
+
type: "string";
|
|
373
|
+
};
|
|
374
|
+
grand_company: {
|
|
375
|
+
shape: {
|
|
376
|
+
name: {
|
|
377
|
+
regex: RegExp;
|
|
378
|
+
selector: string;
|
|
379
|
+
type: "string";
|
|
380
|
+
};
|
|
381
|
+
rank: {
|
|
382
|
+
regex: RegExp;
|
|
383
|
+
selector: string;
|
|
384
|
+
type: "string";
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
type: "object";
|
|
388
|
+
};
|
|
389
|
+
id: {
|
|
390
|
+
regex: RegExp;
|
|
391
|
+
selector: string;
|
|
392
|
+
type: "string";
|
|
393
|
+
};
|
|
394
|
+
members: {
|
|
395
|
+
selector: string;
|
|
396
|
+
type: "number";
|
|
397
|
+
};
|
|
398
|
+
name: {
|
|
399
|
+
selector: string;
|
|
400
|
+
type: "string";
|
|
401
|
+
};
|
|
402
|
+
rank: {
|
|
403
|
+
selector: string;
|
|
404
|
+
type: "number";
|
|
405
|
+
};
|
|
406
|
+
rankings: {
|
|
407
|
+
shape: {
|
|
408
|
+
monthly: {
|
|
409
|
+
regex: RegExp;
|
|
410
|
+
selector: string;
|
|
411
|
+
type: "number";
|
|
412
|
+
};
|
|
413
|
+
weekly: {
|
|
414
|
+
regex: RegExp;
|
|
415
|
+
selector: string;
|
|
416
|
+
type: "number";
|
|
417
|
+
};
|
|
418
|
+
};
|
|
419
|
+
type: "object";
|
|
420
|
+
};
|
|
421
|
+
slogan: {
|
|
422
|
+
selector: string;
|
|
423
|
+
type: "string";
|
|
424
|
+
};
|
|
425
|
+
tag: {
|
|
426
|
+
selector: string;
|
|
427
|
+
type: "string";
|
|
428
|
+
};
|
|
429
|
+
world_name: {
|
|
430
|
+
regex: RegExp;
|
|
431
|
+
selector: string;
|
|
432
|
+
type: "string";
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
};
|
|
436
|
+
list: {
|
|
437
|
+
fields: {
|
|
438
|
+
crest: {
|
|
439
|
+
attribute: string;
|
|
440
|
+
selector: string;
|
|
441
|
+
type: "string[]";
|
|
442
|
+
};
|
|
443
|
+
data_center: {
|
|
444
|
+
regex: RegExp;
|
|
445
|
+
selector: string;
|
|
446
|
+
type: "string";
|
|
447
|
+
};
|
|
448
|
+
formed: {
|
|
449
|
+
regex: RegExp;
|
|
450
|
+
selector: string;
|
|
451
|
+
type: "string";
|
|
452
|
+
};
|
|
453
|
+
grand_company: {
|
|
454
|
+
shape: {
|
|
455
|
+
name: {
|
|
456
|
+
regex: RegExp;
|
|
457
|
+
selector: string;
|
|
458
|
+
type: "string";
|
|
459
|
+
};
|
|
460
|
+
};
|
|
461
|
+
type: "object";
|
|
462
|
+
};
|
|
463
|
+
has_estate: {
|
|
464
|
+
selector: string;
|
|
465
|
+
type: "boolean";
|
|
466
|
+
};
|
|
467
|
+
id: {
|
|
468
|
+
attribute: string;
|
|
469
|
+
regex: RegExp;
|
|
470
|
+
selector: string;
|
|
471
|
+
type: "string";
|
|
472
|
+
};
|
|
473
|
+
members: {
|
|
474
|
+
selector: string;
|
|
475
|
+
type: "number";
|
|
476
|
+
};
|
|
477
|
+
name: {
|
|
478
|
+
selector: string;
|
|
479
|
+
type: "string";
|
|
480
|
+
};
|
|
481
|
+
world_name: {
|
|
482
|
+
regex: RegExp;
|
|
483
|
+
selector: string;
|
|
484
|
+
type: "string";
|
|
485
|
+
};
|
|
486
|
+
};
|
|
487
|
+
query: {
|
|
488
|
+
activetime: {
|
|
489
|
+
pattern: RegExp;
|
|
490
|
+
type: "string";
|
|
491
|
+
};
|
|
492
|
+
activities: {
|
|
493
|
+
pattern: RegExp;
|
|
494
|
+
type: "string";
|
|
495
|
+
};
|
|
496
|
+
cf_public: {
|
|
497
|
+
type: "boolean";
|
|
498
|
+
};
|
|
499
|
+
character_count: {
|
|
500
|
+
pattern: RegExp;
|
|
501
|
+
type: "string";
|
|
502
|
+
};
|
|
503
|
+
gcid: {
|
|
504
|
+
pattern: RegExp;
|
|
505
|
+
type: "string";
|
|
506
|
+
};
|
|
507
|
+
house: {
|
|
508
|
+
pattern: RegExp;
|
|
509
|
+
type: "string";
|
|
510
|
+
};
|
|
511
|
+
join: {
|
|
512
|
+
type: "boolean";
|
|
513
|
+
};
|
|
514
|
+
order: {
|
|
515
|
+
pattern: RegExp;
|
|
516
|
+
type: "string";
|
|
517
|
+
};
|
|
518
|
+
q: {
|
|
519
|
+
required: true;
|
|
520
|
+
type: "string";
|
|
521
|
+
};
|
|
522
|
+
roles: {
|
|
523
|
+
pattern: RegExp;
|
|
524
|
+
type: "string";
|
|
525
|
+
};
|
|
526
|
+
worldname: {
|
|
527
|
+
pattern: RegExp;
|
|
528
|
+
type: "string";
|
|
529
|
+
};
|
|
530
|
+
};
|
|
531
|
+
};
|
|
532
|
+
path: string;
|
|
533
|
+
};
|
|
534
|
+
declare const linkshell: {
|
|
535
|
+
item: {
|
|
536
|
+
fields: {
|
|
537
|
+
data_center: {
|
|
538
|
+
regex: RegExp;
|
|
539
|
+
selector: string;
|
|
540
|
+
type: "string";
|
|
541
|
+
};
|
|
542
|
+
members: {
|
|
543
|
+
regex: RegExp;
|
|
544
|
+
selector: string;
|
|
545
|
+
type: "number";
|
|
546
|
+
};
|
|
547
|
+
name: {
|
|
548
|
+
regex: RegExp;
|
|
549
|
+
selector: string;
|
|
550
|
+
type: "string";
|
|
551
|
+
};
|
|
552
|
+
world_name: {
|
|
553
|
+
regex: RegExp;
|
|
554
|
+
selector: string;
|
|
555
|
+
type: "string";
|
|
556
|
+
};
|
|
557
|
+
};
|
|
558
|
+
};
|
|
559
|
+
list: {
|
|
560
|
+
fields: {
|
|
561
|
+
data_center: {
|
|
562
|
+
regex: RegExp;
|
|
563
|
+
selector: string;
|
|
564
|
+
type: "string";
|
|
565
|
+
};
|
|
566
|
+
id: {
|
|
567
|
+
attribute: string;
|
|
568
|
+
regex: RegExp;
|
|
569
|
+
selector: string;
|
|
570
|
+
type: "string";
|
|
571
|
+
};
|
|
572
|
+
members: {
|
|
573
|
+
regex: RegExp;
|
|
574
|
+
selector: string;
|
|
575
|
+
type: "number";
|
|
576
|
+
};
|
|
577
|
+
name: {
|
|
578
|
+
selector: string;
|
|
579
|
+
type: "string";
|
|
580
|
+
};
|
|
581
|
+
world_name: {
|
|
582
|
+
regex: RegExp;
|
|
583
|
+
selector: string;
|
|
584
|
+
type: "string";
|
|
585
|
+
};
|
|
586
|
+
};
|
|
587
|
+
query: {
|
|
588
|
+
cf_public: {
|
|
589
|
+
type: "boolean";
|
|
590
|
+
};
|
|
591
|
+
character_count: {
|
|
592
|
+
pattern: RegExp;
|
|
593
|
+
type: "string";
|
|
594
|
+
};
|
|
595
|
+
order: {
|
|
596
|
+
pattern: RegExp;
|
|
597
|
+
type: "string";
|
|
598
|
+
};
|
|
599
|
+
q: {
|
|
600
|
+
required: true;
|
|
601
|
+
type: "string";
|
|
602
|
+
};
|
|
603
|
+
worldname: {
|
|
604
|
+
pattern: RegExp;
|
|
605
|
+
type: "string";
|
|
606
|
+
};
|
|
607
|
+
};
|
|
608
|
+
};
|
|
609
|
+
path: string;
|
|
610
|
+
};
|
|
611
|
+
declare const pvpteam: {
|
|
612
|
+
item: {
|
|
613
|
+
fields: {
|
|
614
|
+
crest: {
|
|
615
|
+
attribute: string;
|
|
616
|
+
selector: string;
|
|
617
|
+
type: "string[]";
|
|
618
|
+
};
|
|
619
|
+
data_center: {
|
|
620
|
+
selector: string;
|
|
621
|
+
type: "string";
|
|
622
|
+
};
|
|
623
|
+
formed: {
|
|
624
|
+
regex: RegExp;
|
|
625
|
+
selector: string;
|
|
626
|
+
type: "string";
|
|
627
|
+
};
|
|
628
|
+
name: {
|
|
629
|
+
selector: string;
|
|
630
|
+
type: "string";
|
|
631
|
+
};
|
|
632
|
+
};
|
|
633
|
+
};
|
|
634
|
+
list: {
|
|
635
|
+
fields: {
|
|
636
|
+
crest: {
|
|
637
|
+
attribute: string;
|
|
638
|
+
selector: string;
|
|
639
|
+
type: "string[]";
|
|
640
|
+
};
|
|
641
|
+
data_center: {
|
|
642
|
+
selector: string;
|
|
643
|
+
type: "string";
|
|
644
|
+
};
|
|
645
|
+
id: {
|
|
646
|
+
attribute: string;
|
|
647
|
+
regex: RegExp;
|
|
648
|
+
selector: string;
|
|
649
|
+
type: "string";
|
|
650
|
+
};
|
|
651
|
+
name: {
|
|
652
|
+
selector: string;
|
|
653
|
+
type: "string";
|
|
654
|
+
};
|
|
655
|
+
};
|
|
656
|
+
query: {
|
|
657
|
+
cf_public: {
|
|
658
|
+
type: "boolean";
|
|
659
|
+
};
|
|
660
|
+
dcname: {
|
|
661
|
+
pattern: RegExp;
|
|
662
|
+
type: "string";
|
|
663
|
+
};
|
|
664
|
+
order: {
|
|
665
|
+
pattern: RegExp;
|
|
666
|
+
type: "string";
|
|
667
|
+
};
|
|
668
|
+
q: {
|
|
669
|
+
required: true;
|
|
670
|
+
type: "string";
|
|
671
|
+
};
|
|
672
|
+
};
|
|
673
|
+
};
|
|
674
|
+
path: string;
|
|
675
|
+
};
|
|
676
|
+
type Character = typeof character;
|
|
677
|
+
type CWLS = typeof cwls;
|
|
678
|
+
type Freecompany = typeof freecompany;
|
|
679
|
+
type Linkshell = typeof linkshell;
|
|
680
|
+
type PvpTeam = typeof pvpteam;
|
|
681
|
+
|
|
682
|
+
type NumberResolvable = string | number;
|
|
683
|
+
type EndpointOptions = {
|
|
684
|
+
headers?: Record<string, string>;
|
|
685
|
+
locale?: string;
|
|
686
|
+
};
|
|
687
|
+
declare class Endpoint<R extends Registry> {
|
|
688
|
+
protected readonly registry: R;
|
|
689
|
+
protected readonly options?: EndpointOptions | undefined;
|
|
690
|
+
/**
|
|
691
|
+
* A generic Lodestone endpoint used to search and get items from Lodestone.
|
|
692
|
+
* @param {T} registry The provided endpoint registry containing field selectors to obtain
|
|
693
|
+
* @param {EndpointOptions<R>} [options] Default method options to use when fetching from Lodestone.
|
|
694
|
+
* @since 0.1.0
|
|
695
|
+
*/
|
|
696
|
+
constructor(registry: R, options?: EndpointOptions | undefined);
|
|
697
|
+
private check;
|
|
698
|
+
private req;
|
|
699
|
+
private fetchDocument;
|
|
700
|
+
private fetchColumn;
|
|
701
|
+
private isMissing;
|
|
702
|
+
private isValid;
|
|
703
|
+
private validate;
|
|
704
|
+
private getRawValue;
|
|
705
|
+
private applyRegex;
|
|
706
|
+
private coerce;
|
|
707
|
+
private extract;
|
|
708
|
+
/**
|
|
709
|
+
* @param {InferQuery<R>} query The raw query parameters used by the Lodestone search.
|
|
710
|
+
* @param {EndpointOptions} [options] Optional method overrides.
|
|
711
|
+
* @returns {Promise<InferList<R>[] | null>}
|
|
712
|
+
* @since 0.1.0
|
|
713
|
+
*/
|
|
714
|
+
find(query: InferQuery<R>, options?: EndpointOptions): Promise<InferList<R>[] | null>;
|
|
715
|
+
/**
|
|
716
|
+
* @param {NumberResolvable} id The unique identifier for the Lodestone item.
|
|
717
|
+
* @param {EndpointOptions & { columns?: Array<keyof InferColumns<R>> }} [options] Optional method overrides.
|
|
718
|
+
* @returns {Promise<(InferItem<R> & Partial<InferColumns<R>) | null>}
|
|
719
|
+
* @since 0.1.0
|
|
720
|
+
*/
|
|
721
|
+
get(id: NumberResolvable, options?: EndpointOptions & {
|
|
722
|
+
columns?: Array<keyof InferColumns<R>>;
|
|
723
|
+
}): Promise<(InferItem<R> & Partial<InferColumns<R>>) | null>;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
declare class Lodestone {
|
|
727
|
+
readonly character: Endpoint<Character>;
|
|
728
|
+
readonly cwls: Endpoint<CWLS>;
|
|
729
|
+
readonly freecompany: Endpoint<Freecompany>;
|
|
730
|
+
readonly linkshell: Endpoint<Linkshell>;
|
|
731
|
+
readonly pvpteam: Endpoint<PvpTeam>;
|
|
732
|
+
/**
|
|
733
|
+
* @param {EndpointOptions} [options] Default method options to use when fetching from Lodestone.
|
|
734
|
+
* @example
|
|
735
|
+
* ```ts
|
|
736
|
+
* const lodestone = new Lodestone();
|
|
737
|
+
*
|
|
738
|
+
* const char = await lodestone.character.get(12345678);
|
|
739
|
+
* // { id: '12345678', ... }
|
|
740
|
+
* ```
|
|
741
|
+
* @since 0.1.0
|
|
742
|
+
*/
|
|
743
|
+
constructor(options?: EndpointOptions);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export { Lodestone, Lodestone as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,t=(t,r,n)=>((t,r,n)=>r in t?e(t,r,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[r]=n)(t,"symbol"!=typeof r?r+"":r,n);import{parseHTML as r}from"linkedom/worker";var n=class extends Error{constructor(e){super(e),Error.captureStackTrace(this),this.name="LodestoneError"}},s=class{constructor(e,r){this.registry=e,this.options=r,t(this,"isMissing",e=>null==e),t(this,"isValid",(e,t)=>{switch(t){case"number":return"number"==typeof e||!Number.isNaN(Number(e));case"boolean":return"boolean"==typeof e;case"date":return e instanceof Date||!Number.isNaN(Date.parse(String(e)));case"url":try{return new URL(String(e)),!0}catch{return!1}default:return"string"==typeof e}}),t(this,"validate",e=>{const t=this.registry.list.query;for(const r of Object.keys(e))if(!(r in t))throw new n(`Unknown query parameter "${r}".`);for(const[r,s]of Object.entries(t)){const t=e[r];if(s.required&&this.isMissing(t))throw new n(`Missing required query parameter "${r}".`);if(!this.isMissing(t)){if(!this.isValid(t,s.type))throw new n(`Query parameter "${r}" must be type ${s.type}.`);if(s.pattern&&!s.pattern.test(String(t)))throw new n(`Query parameter "${r}" does not match required pattern.`)}}})}check(e){const{status:t}=e;if(200!==t){if(302===t)throw new n("Lodestone redirected the request.");if(503===t)throw new n("Lodestone is undergoing maintenance.");if(t>=500)throw new n(`Lodestone server error (${t}).`);if(t>=400)throw new n(`Request failed with status ${t}.`)}}async req(e,t){const{headers:r={},locale:n="na"}=t??this.options,s=Object.fromEntries(Object.entries(r).map(([e,t])=>[e.toLowerCase(),String(t)])),a="curl/0.1.0 (+https://github.com/miichom/lodestone)";return s["user-agent"]=s["user-agent"]?`${a} ${s["user-agent"]}`:a,fetch(`https://${n}.finalfantasyxiv.com/lodestone/${this.registry.path}/${e.replace(/^\/+/,"")}`,{headers:s,method:"GET",redirect:"follow"})}async fetchDocument(e,t){const n=await this.req(e,t);if(!n.ok||404===n.status)return null;this.check(n);const{document:s}=r(await n.text());return!s||s.querySelector(".parts__zero")?null:s}async fetchColumn(e,t,r){const n=this.registry.item.columns?.[t];if(!n)return;const s=await this.fetchDocument(`${e.toString()}/${t}`,r);if(!s)return;if(!("shape"in n))return this.extract(s,{value:n}).value;if("object"===n.type)return this.extract(s,n.shape);const a=n.shape.root.selector;return[...s.querySelectorAll(a)].map(e=>this.extract(e,n.shape))}getRawValue(e,t){return t.attribute?e.getAttribute(t.attribute)??"":e.textContent.trim()??""}applyRegex(e,t){if(!t.regex)return e;const r=e.match(t.regex);if(!r)return"";if(r.groups){const[e]=Object.keys(r.groups);return r.groups[e]??""}return r[1]??""}coerce(e,t){switch(t){case"boolean":return"1"===e||"true"===e;case"number":return Number(e);case"date":return new Date(e);case"url":return new URL(e);default:return e}}extract(e,t){const r={};for(const[n,s]of Object.entries(t)){if("shape"in s){if("object"===s.type)r[n]=this.extract(e,s.shape);else{const t=[...e.querySelectorAll(s.shape.root.selector)];r[n]=t.map(e=>this.extract(e,s.shape))}continue}const t=s.type.endsWith("[]"),a=t?s.type.slice(0,-2):s.type;if(t){const t=[...e.querySelectorAll(s.selector)];r[n]=t.map(e=>{const t=this.getRawValue(e,s),r=this.applyRegex(t,s);return this.coerce(r,a)});continue}const i=e.querySelector(s.selector);if(!i){r[n]=void 0;continue}const c=this.getRawValue(i,s),o=this.applyRegex(c,s);r[n]=this.coerce(o,a)}return r}async find(e,t={}){this.validate(e);const r=new URLSearchParams(Object.fromEntries(Object.entries(e).filter(([,e])=>null!=e).map(([e,t])=>[e,"boolean"==typeof t?t?"1":"0":String(t)]))).toString(),n=await this.fetchDocument(`?${r}`,Object.assign(this.options??{},t));if(!n)return null;return[...n.querySelectorAll("div.entry")].map(e=>this.extract(e,this.registry.list.fields)).filter(e=>null!==e.id||void 0!==e.id)}async get(e,t={}){const{columns:r,...n}=Object.assign(this.options??{},t),s=await this.fetchDocument(e.toString(),n);if(!s)return null;const a=this.extract(s,this.registry.item.fields);if(r&&this.registry.item.columns)for(const t of r){const r=await this.fetchColumn(e,String(t),n);void 0!==r&&(a[t]=r)}return a}},a={item:{columns:{achievement:{shape:{score:{selector:".achievement__point",type:"number"},total:{regex:/^(?<name>\d+)/,selector:".parts__total",type:"number"}},type:"object"},faceaccessory:{selector:".faceaccessory__sort__total > span:nth-child(1)",type:"number"},minion:{selector:".minion__sort__total > span:nth-child(1)",type:"number"},mount:{selector:".minion__sort__total > span:nth-child(1)",type:"number"}},fields:{avatar:{attribute:"src",selector:".frame__chara__face > img:nth-child(1)",type:"string"},bio:{selector:".character__selfintroduction",type:"string"},data_center:{regex:/\[(?<datacenter>\w+)]/,selector:".frame__chara__world",type:"string"},free_company:{shape:{crest:{attribute:"src",selector:"div.character__freecompany__crest > div > img",type:"string[]"},id:{attribute:"href",regex:/lodestone\/freecompany\/(?<id>\d+)\//,selector:".character__freecompany__name > h4:nth-child(2) > a:nth-child(1)",type:"string"},name:{selector:".character__freecompany__name > h4:nth-child(2) > a:nth-child(1)",type:"string"}},type:"object"},grand_company:{shape:{name:{regex:/^(?<name>[^/]+)/,selector:"div.character-block:nth-child(4) > div:nth-child(2) > p:nth-child(2)",type:"string"},rank:{regex:/\/\s*(?<rank>.+)$/,selector:"div.character-block:nth-child(4) > div:nth-child(2) > p:nth-child(2)",type:"string"}},type:"object"},id:{attribute:"href",regex:/lodestone\/character\/(?<id>\d+)\//,selector:".frame__chara__link",type:"string"},name:{selector:"div.frame__chara__box:nth-child(2) > .frame__chara__name",type:"string"},portrait:{attribute:"src",selector:".js__image_popup > img:nth-child(1)",type:"string"},pvp_team:{shape:{crest:{attribute:"src",selector:".character__pvpteam__crest__image > img",type:"string[]"},id:{attribute:"href",regex:/lodestone\/pvpteam\/(?<id>[\da-z]+)\//,selector:".character__pvpteam__name > h4 > a",type:"string"},name:{selector:".character__pvpteam__name > h4 > a",type:"string"}},type:"object"},title:{selector:".frame__chara__title",type:"string"},world_name:{regex:/^(?<world>\w+)/,selector:".frame__chara__world",type:"string"}}},list:{fields:{avatar:{attribute:"src",selector:".entry__chara__face > img",type:"string"},data_center:{regex:/\[(?<datacenter>\w+)]/,selector:".entry__world",type:"string"},grand_company:{shape:{name:{attribute:"data-tooltip",regex:/^(?<name>[^/]+)/,selector:".entry__chara_info > .js__tooltip",type:"string"},rank:{attribute:"data-tooltip",regex:/\/\s*(?<rank>.+)$/,selector:".entry__chara_info > .js__tooltip",type:"string"}},type:"object"},id:{attribute:"href",regex:/lodestone\/character\/(?<id>\d+)\//,selector:".entry__link",type:"string"},name:{selector:".entry__name",type:"string"},world_name:{regex:/^(?<world>\w+)/,selector:".entry__world",type:"string"}},query:{blog_lang:{pattern:/^(?:ja|en|de|fr)$/,type:"string"},classjob:{pattern:/^(?:\d+|_job_(?:TANK|HEALER|MELEE|RANGED|CASTER|GATHERER|CRAFTER))$/,type:"string"},gcid:{pattern:/^[1-3]$/,type:"string"},order:{pattern:/^[18]?$/,type:"string"},q:{required:!0,type:"string"},race_tribe:{pattern:/^(?:race_\d+|tribe_\d+)$/,type:"string"},worldname:{pattern:/^(?:_dc_[A-Za-z]+|_region_[1-4]|[A-Za-z]+)$/,type:"string"}}},path:"character"},i={item:{fields:{data_center:{selector:".heading__cwls__dcname",type:"string"},formed:{regex:/ldst_strftime\((\d+),/,selector:".heading__cwls__formed > script",type:"string"},members:{regex:/(?<total>\d+)/,selector:"div.cf-member-list > .parts__total",type:"number"},name:{regex:/\s*(?<name>.+)/,selector:".heading__linkshell__name",type:"string"}}},list:{fields:{data_center:{selector:".entry__world",type:"string"},id:{attribute:"href",regex:/lodestone\/crossworld_linkshell\/(?<id>.+)\//,selector:".entry__link--line",type:"string"},members:{selector:".entry__linkshell__member > div > span",type:"string"},name:{selector:".entry__name",type:"string"}},query:{cf_public:{type:"boolean"},character_count:{pattern:/^(?:\d+-\d+|\d+-)$/,type:"string"},dcname:{pattern:/^(?:_dc_[A-Za-z]+|_region_[1-4])$/,type:"string"},order:{pattern:/^[16]?$/,type:"string"},q:{required:!0,type:"string"}}},path:"crossworld_linkshell"},c={item:{fields:{crest:{attribute:"src",selector:"div.ldst__window:nth-child(1) > div:nth-child(2) > a:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > img",type:"string[]"},data_center:{regex:/\[(?<datacenter>\w+)]/,selector:"p.entry__freecompany__gc:nth-child(2)",type:"string"},estate:{shape:{greeting:{selector:".freecompany__estate__greeting",type:"string"},name:{selector:".freecompany__estate__name",type:"string"},plot:{selector:".freecompany__estate__text",type:"string"}},type:"object"},formed:{regex:/ldst_strftime\((\d+),/,selector:"p.freecompany__text:nth-of-type(5) > script",type:"string"},grand_company:{shape:{name:{regex:/^(?<name>[^/]+)/,selector:"p.entry__freecompany__gc:nth-child(1)",type:"string"},rank:{regex:/\/\s*(?<rank>.+)$/,selector:"p.entry__freecompany__gc:nth-child(1)",type:"string"}},type:"object"},id:{regex:/lodestone\/freecompany\/(?<id>\d+)\//,selector:"a.entry__freecompany",type:"string"},members:{selector:"p.freecompany__text:nth-of-type(6)",type:"number"},name:{selector:"p.entry__freecompany__name",type:"string"},rank:{selector:"p.freecompany__text:nth-of-type(7)",type:"number"},rankings:{shape:{monthly:{regex:/Monthly Rank:(?<rank>\d+)/,selector:".character__ranking__data tr:nth-child(2) > th:nth-child(1)",type:"number"},weekly:{regex:/Weekly Rank:(?<rank>\d+)/,selector:".character__ranking__data tr:nth-child(1) > th:nth-child(1)",type:"number"}},type:"object"},slogan:{selector:".freecompany__text__message",type:"string"},tag:{selector:".freecompany__text.freecompany__text__tag",type:"string"},world_name:{regex:/^(?<world>\w+)/,selector:"p.entry__freecompany__gc:nth-child(2)",type:"string"}}},list:{fields:{crest:{attribute:"src",selector:".entry__freecompany__crest__image > img",type:"string[]"},data_center:{regex:/\[(?<datacenter>\w+)]/,selector:".entry__world:nth-child(3)",type:"string"},formed:{regex:/ldst_strftime\((\d+),/,selector:".entry__freecompany__fc-day > script",type:"string"},grand_company:{shape:{name:{regex:/^(?<name>[^/]+)/,selector:"p.entry__freecompany__gc:nth-child(1)",type:"string"}},type:"object"},has_estate:{selector:".entry__freecompany__fc-housing",type:"boolean"},id:{attribute:"href",regex:/lodestone\/freecompany\/(?<id>\d+)\//,selector:".entry__block",type:"string"},members:{selector:".entry__freecompany__fc-member",type:"number"},name:{selector:".entry__name",type:"string"},world_name:{regex:/^(?<world>\w+)/,selector:".entry__world:nth-child(3)",type:"string"}},query:{activetime:{pattern:/^[1-3]$/,type:"string"},activities:{pattern:/^(?:-1|[0-8])$/,type:"string"},cf_public:{type:"boolean"},character_count:{pattern:/^(?:\d+-\d+|\d+-)$/,type:"string"},gcid:{pattern:/^[1-3]$/,type:"string"},house:{pattern:/^[0-2]$/,type:"string"},join:{type:"boolean"},order:{pattern:/^[16]?$/,type:"string"},q:{required:!0,type:"string"},roles:{pattern:/^(?:-1|1[6-9]|20)$/,type:"string"},worldname:{pattern:/^(?:_dc_[A-Za-z]+|_region_[1-4]|[A-Za-z]+)$/,type:"string"}}},path:"freecompany"},o={item:{fields:{data_center:{regex:/\[(?<datacenter>\w+)]/,selector:".entry__world",type:"string"},members:{regex:/(?<total>\d+)/,selector:"div.cf-member-list > .parts__total",type:"number"},name:{regex:/\s*(?<name>.+)/,selector:".heading__linkshell__name",type:"string"},world_name:{regex:/^(?<world>\w+)/,selector:".entry__world",type:"string"}}},list:{fields:{data_center:{regex:/\[(?<datacenter>\w+)]/,selector:".entry__world",type:"string"},id:{attribute:"href",regex:/lodestone\/linkshell\/(?<id>.+)\//,selector:".entry__block",type:"string"},members:{regex:/(?<total>\d+)/,selector:".entry__linkshell__member > div > span",type:"number"},name:{selector:".entry__name",type:"string"},world_name:{regex:/^(?<world>\w+)/,selector:".entry__world",type:"string"}},query:{cf_public:{type:"boolean"},character_count:{pattern:/^(?:\d+-\d+|\d+-)$/,type:"string"},order:{pattern:/^[16]?$/,type:"string"},q:{required:!0,type:"string"},worldname:{pattern:/^(?:_region_[1-4]|[A-Za-z]+)$/,type:"string"}}},path:"linkshell"},l={item:{fields:{crest:{attribute:"src",selector:".entry__pvpteam__crest__image > img",type:"string[]"},data_center:{selector:".entry__pvpteam__name--dc",type:"string"},formed:{regex:/ldst_strftime\((\d+),/,selector:".entry__pvpteam__data--formed > script",type:"string"},name:{selector:".entry__pvpteam__name--team",type:"string"}}},list:{fields:{crest:{attribute:"src",selector:".entry__pvpteam__search__crest__image > img",type:"string[]"},data_center:{selector:".entry__world",type:"string"},id:{attribute:"href",regex:/lodestone\/pvpteam\/(?<id>.+)\//,selector:".entry__link",type:"string"},name:{selector:".entry__name",type:"string"}},query:{cf_public:{type:"boolean"},dcname:{pattern:/^(?:_dc_[A-Za-z]+|_region_[1-4])$/,type:"string"},order:{pattern:/^[16]?$/,type:"string"},q:{required:!0,type:"string"}}},path:"pvpteam"},_=class{constructor(e={}){t(this,"character"),t(this,"cwls"),t(this,"freecompany"),t(this,"linkshell"),t(this,"pvpteam"),this.character=new s(a,e),this.cwls=new s(i,e),this.freecompany=new s(c,e),this.linkshell=new s(o,e),this.pvpteam=new s(l,e)}},p=_;export{_ as Lodestone,p as default};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miichom/lodestone",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Fast, worker-safe Lodestone parsing ✨",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"lodestone",
|
|
7
|
+
"ffxiv",
|
|
8
|
+
"final fantasy xiv",
|
|
9
|
+
"ffxiv data",
|
|
10
|
+
"scraper",
|
|
11
|
+
"dom parser",
|
|
12
|
+
"html parser",
|
|
13
|
+
"fast"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/miichom/lodestone#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/miichom/lodestone/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/miichom/lodestone.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "miichom <hello@cammy.xyz> (https://cammy.xyz/)",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "vitest",
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"format": "prettier --write .",
|
|
32
|
+
"lint": "eslint --ext .ts",
|
|
33
|
+
"prepublishOnly": "pnpm run build"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"linkedom": "^0.18.12"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@eslint/js": "^9.39.2",
|
|
40
|
+
"@tsconfig/node20": "^20.1.8",
|
|
41
|
+
"@types/node": "^25.0.9",
|
|
42
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
43
|
+
"eslint": "^9.39.2",
|
|
44
|
+
"eslint-config-prettier": "^10.1.8",
|
|
45
|
+
"eslint-plugin-sort": "^4.0.0",
|
|
46
|
+
"eslint-plugin-unicorn": "^62.0.0",
|
|
47
|
+
"globals": "^17.0.0",
|
|
48
|
+
"jiti": "^2.6.1",
|
|
49
|
+
"prettier": "^3.8.0",
|
|
50
|
+
"terser": "^5.46.0",
|
|
51
|
+
"tsup": "^8.5.1",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"typescript-eslint": "^8.53.0",
|
|
54
|
+
"vitest": "^4.0.17"
|
|
55
|
+
},
|
|
56
|
+
"files": [
|
|
57
|
+
"dist"
|
|
58
|
+
],
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=20.0.0"
|
|
61
|
+
},
|
|
62
|
+
"packageManager": "pnpm@10.28.0"
|
|
63
|
+
}
|