@miichom/lodestone 0.1.0 → 0.1.1
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/README.md +133 -133
- package/dist/index.d.ts +38 -23
- package/dist/index.js +1 -1
- package/package.json +61 -63
package/README.md
CHANGED
|
@@ -1,133 +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).
|
|
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
CHANGED
|
@@ -58,11 +58,22 @@ type InferSelectors<T extends Selectors> = {
|
|
|
58
58
|
type InferSelector<T> = T extends {
|
|
59
59
|
type: infer U;
|
|
60
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
|
|
62
|
-
type
|
|
63
|
-
|
|
61
|
+
type InferFields<R extends Registry> = InferSelectors<R["item"]["fields"]>;
|
|
62
|
+
type InferColumns<R extends Registry> = R["item"] extends {
|
|
63
|
+
columns: Selectors;
|
|
64
|
+
} ? {
|
|
64
65
|
[K in keyof R["item"]["columns"]]: InferSelector<R["item"]["columns"][K]>;
|
|
65
|
-
} :
|
|
66
|
+
} : object;
|
|
67
|
+
type InferSelectedFields<R extends Registry, F extends Array<keyof InferFields<R>>> = {
|
|
68
|
+
[K in F[number]]: InferFields<R>[K];
|
|
69
|
+
};
|
|
70
|
+
type InferSelectedColumns<R extends Registry, C extends Array<keyof InferColumns<R>>> = {
|
|
71
|
+
[K in C[number]]: InferColumns<R>[K];
|
|
72
|
+
};
|
|
73
|
+
type InferItemFields<R extends Registry, F> = F extends undefined ? InferFields<R> : F extends Array<keyof InferFields<R>> ? InferSelectedFields<R, F> : never;
|
|
74
|
+
type InferItemColumns<R extends Registry, C> = C extends undefined ? InferColumns<R> : C extends Array<keyof InferColumns<R>> ? InferSelectedColumns<R, C> : never;
|
|
75
|
+
type InferItem<R extends Registry, F = undefined, C = undefined> = InferItemFields<R, F> & InferItemColumns<R, C>;
|
|
76
|
+
type InferList<R extends Registry> = InferSelectors<R["list"]["fields"]>;
|
|
66
77
|
declare const character: {
|
|
67
78
|
item: {
|
|
68
79
|
columns: {
|
|
@@ -97,7 +108,7 @@ declare const character: {
|
|
|
97
108
|
avatar: {
|
|
98
109
|
attribute: string;
|
|
99
110
|
selector: string;
|
|
100
|
-
type: "
|
|
111
|
+
type: "url";
|
|
101
112
|
};
|
|
102
113
|
bio: {
|
|
103
114
|
selector: string;
|
|
@@ -113,7 +124,7 @@ declare const character: {
|
|
|
113
124
|
crest: {
|
|
114
125
|
attribute: string;
|
|
115
126
|
selector: string;
|
|
116
|
-
type: "
|
|
127
|
+
type: "url[]";
|
|
117
128
|
};
|
|
118
129
|
id: {
|
|
119
130
|
attribute: string;
|
|
@@ -156,14 +167,14 @@ declare const character: {
|
|
|
156
167
|
portrait: {
|
|
157
168
|
attribute: string;
|
|
158
169
|
selector: string;
|
|
159
|
-
type: "
|
|
170
|
+
type: "url";
|
|
160
171
|
};
|
|
161
172
|
pvp_team: {
|
|
162
173
|
shape: {
|
|
163
174
|
crest: {
|
|
164
175
|
attribute: string;
|
|
165
176
|
selector: string;
|
|
166
|
-
type: "
|
|
177
|
+
type: "url[]";
|
|
167
178
|
};
|
|
168
179
|
id: {
|
|
169
180
|
attribute: string;
|
|
@@ -194,7 +205,7 @@ declare const character: {
|
|
|
194
205
|
avatar: {
|
|
195
206
|
attribute: string;
|
|
196
207
|
selector: string;
|
|
197
|
-
type: "
|
|
208
|
+
type: "url";
|
|
198
209
|
};
|
|
199
210
|
data_center: {
|
|
200
211
|
regex: RegExp;
|
|
@@ -277,7 +288,7 @@ declare const cwls: {
|
|
|
277
288
|
formed: {
|
|
278
289
|
regex: RegExp;
|
|
279
290
|
selector: string;
|
|
280
|
-
type: "
|
|
291
|
+
type: "date";
|
|
281
292
|
};
|
|
282
293
|
members: {
|
|
283
294
|
regex: RegExp;
|
|
@@ -342,7 +353,7 @@ declare const freecompany: {
|
|
|
342
353
|
crest: {
|
|
343
354
|
attribute: string;
|
|
344
355
|
selector: string;
|
|
345
|
-
type: "
|
|
356
|
+
type: "url[]";
|
|
346
357
|
};
|
|
347
358
|
data_center: {
|
|
348
359
|
regex: RegExp;
|
|
@@ -369,7 +380,7 @@ declare const freecompany: {
|
|
|
369
380
|
formed: {
|
|
370
381
|
regex: RegExp;
|
|
371
382
|
selector: string;
|
|
372
|
-
type: "
|
|
383
|
+
type: "date";
|
|
373
384
|
};
|
|
374
385
|
grand_company: {
|
|
375
386
|
shape: {
|
|
@@ -438,7 +449,7 @@ declare const freecompany: {
|
|
|
438
449
|
crest: {
|
|
439
450
|
attribute: string;
|
|
440
451
|
selector: string;
|
|
441
|
-
type: "
|
|
452
|
+
type: "url[]";
|
|
442
453
|
};
|
|
443
454
|
data_center: {
|
|
444
455
|
regex: RegExp;
|
|
@@ -448,7 +459,7 @@ declare const freecompany: {
|
|
|
448
459
|
formed: {
|
|
449
460
|
regex: RegExp;
|
|
450
461
|
selector: string;
|
|
451
|
-
type: "
|
|
462
|
+
type: "date";
|
|
452
463
|
};
|
|
453
464
|
grand_company: {
|
|
454
465
|
shape: {
|
|
@@ -614,7 +625,7 @@ declare const pvpteam: {
|
|
|
614
625
|
crest: {
|
|
615
626
|
attribute: string;
|
|
616
627
|
selector: string;
|
|
617
|
-
type: "
|
|
628
|
+
type: "url[]";
|
|
618
629
|
};
|
|
619
630
|
data_center: {
|
|
620
631
|
selector: string;
|
|
@@ -623,7 +634,7 @@ declare const pvpteam: {
|
|
|
623
634
|
formed: {
|
|
624
635
|
regex: RegExp;
|
|
625
636
|
selector: string;
|
|
626
|
-
type: "
|
|
637
|
+
type: "date";
|
|
627
638
|
};
|
|
628
639
|
name: {
|
|
629
640
|
selector: string;
|
|
@@ -636,7 +647,7 @@ declare const pvpteam: {
|
|
|
636
647
|
crest: {
|
|
637
648
|
attribute: string;
|
|
638
649
|
selector: string;
|
|
639
|
-
type: "
|
|
650
|
+
type: "url[]";
|
|
640
651
|
};
|
|
641
652
|
data_center: {
|
|
642
653
|
selector: string;
|
|
@@ -689,7 +700,7 @@ declare class Endpoint<R extends Registry> {
|
|
|
689
700
|
protected readonly options?: EndpointOptions | undefined;
|
|
690
701
|
/**
|
|
691
702
|
* A generic Lodestone endpoint used to search and get items from Lodestone.
|
|
692
|
-
* @param {
|
|
703
|
+
* @param {R} registry The provided endpoint registry containing field selectors to obtain
|
|
693
704
|
* @param {EndpointOptions<R>} [options] Default method options to use when fetching from Lodestone.
|
|
694
705
|
* @since 0.1.0
|
|
695
706
|
*/
|
|
@@ -704,6 +715,7 @@ declare class Endpoint<R extends Registry> {
|
|
|
704
715
|
private getRawValue;
|
|
705
716
|
private applyRegex;
|
|
706
717
|
private coerce;
|
|
718
|
+
private pickSelectors;
|
|
707
719
|
private extract;
|
|
708
720
|
/**
|
|
709
721
|
* @param {InferQuery<R>} query The raw query parameters used by the Lodestone search.
|
|
@@ -711,16 +723,19 @@ declare class Endpoint<R extends Registry> {
|
|
|
711
723
|
* @returns {Promise<InferList<R>[] | null>}
|
|
712
724
|
* @since 0.1.0
|
|
713
725
|
*/
|
|
714
|
-
find(query: InferQuery<R>, options?: EndpointOptions
|
|
726
|
+
find<F extends Array<Extract<keyof InferFields<R>, string>> = []>(query: InferQuery<R>, options?: EndpointOptions & {
|
|
727
|
+
fields?: F;
|
|
728
|
+
}): Promise<InferList<R>[] | null>;
|
|
715
729
|
/**
|
|
716
730
|
* @param {NumberResolvable} id The unique identifier for the Lodestone item.
|
|
717
731
|
* @param {EndpointOptions & { columns?: Array<keyof InferColumns<R>> }} [options] Optional method overrides.
|
|
718
|
-
* @returns {Promise<(InferItem<R>
|
|
732
|
+
* @returns {Promise<(InferItem<R>) | null>}
|
|
719
733
|
* @since 0.1.0
|
|
720
734
|
*/
|
|
721
|
-
get(id: NumberResolvable, options?: EndpointOptions & {
|
|
722
|
-
|
|
723
|
-
|
|
735
|
+
get<F extends Array<Extract<keyof InferFields<R>, string>> | undefined = undefined, C extends Array<Extract<keyof InferColumns<R>, string>> | undefined = undefined>(id: NumberResolvable, options?: EndpointOptions & {
|
|
736
|
+
fields?: F;
|
|
737
|
+
columns?: C;
|
|
738
|
+
}): Promise<InferItem<R, F, C> | null>;
|
|
724
739
|
}
|
|
725
740
|
|
|
726
741
|
declare class Lodestone {
|
package/dist/index.js
CHANGED
|
@@ -1 +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};
|
|
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}}pickSelectors(e,t){const r={};for(const n of t)r[n]=e[n];return r}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)].map(e=>{const t=this.getRawValue(e,s),r=this.applyRegex(t,s);return this.coerce(r,a)});r[n]=t.length>0?t:void 0;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={}){const{fields:r,...n}=t;this.validate(e);const s=new URLSearchParams(Object.fromEntries(Object.entries(e).filter(([,e])=>null!=e).map(([e,t])=>[e,"boolean"==typeof t?t?"1":"0":String(t)]))).toString(),a=await this.fetchDocument(`?${s}`,Object.assign(this.options??{},n));if(!a)return null;const i=r?.length?this.pickSelectors(this.registry.item.fields,r):this.registry.item.fields;return[...a.querySelectorAll("div.entry")].map(e=>this.extract(e,i)).filter(e=>null!==e.id||void 0!==e.id)}async get(e,t={}){const{columns:r,fields:n,...s}=t,a=await this.fetchDocument(e.toString(),Object.assign(this.options??{},s));if(!a)return null;const i=n?.length?this.pickSelectors(this.registry.item.fields,n):this.registry.item.fields,c=this.extract(a,i);if(r&&this.registry.item.columns)for(const t of r){const r=await this.fetchColumn(e,String(t),s);void 0!==r&&(c[t]=r)}return c}},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:"url"},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:"url[]"},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:"url"},pvp_team:{shape:{crest:{attribute:"src",selector:".character__pvpteam__crest__image > img",type:"url[]"},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:"url"},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:"date"},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:"url[]"},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:"date"},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:"url[]"},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:"date"},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:"url[]"},data_center:{selector:".entry__pvpteam__name--dc",type:"string"},formed:{regex:/ldst_strftime\((\d+),/,selector:".entry__pvpteam__data--formed > script",type:"date"},name:{selector:".entry__pvpteam__name--team",type:"string"}}},list:{fields:{crest:{attribute:"src",selector:".entry__pvpteam__search__crest__image > img",type:"url[]"},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
CHANGED
|
@@ -1,63 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@miichom/lodestone",
|
|
3
|
-
"version": "0.1.
|
|
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
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"eslint
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"packageManager": "pnpm@10.28.0"
|
|
63
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@miichom/lodestone",
|
|
3
|
+
"version": "0.1.1",
|
|
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
|
+
"dependencies": {
|
|
29
|
+
"linkedom": "^0.18.12"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@eslint/js": "^9.39.2",
|
|
33
|
+
"@tsconfig/node20": "^20.1.8",
|
|
34
|
+
"@types/node": "^25.0.9",
|
|
35
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
36
|
+
"eslint": "^9.39.2",
|
|
37
|
+
"eslint-config-prettier": "^10.1.8",
|
|
38
|
+
"eslint-plugin-sort": "^4.0.0",
|
|
39
|
+
"eslint-plugin-unicorn": "^62.0.0",
|
|
40
|
+
"globals": "^17.0.0",
|
|
41
|
+
"jiti": "^2.6.1",
|
|
42
|
+
"prettier": "^3.8.0",
|
|
43
|
+
"terser": "^5.46.0",
|
|
44
|
+
"tsup": "^8.5.1",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"typescript-eslint": "^8.53.0",
|
|
47
|
+
"vitest": "^4.0.17"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"dist"
|
|
51
|
+
],
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20.0.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"test": "vitest",
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"format": "prettier --write .",
|
|
59
|
+
"lint": "eslint --ext .ts"
|
|
60
|
+
}
|
|
61
|
+
}
|