njsparser 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +390 -40
- package/api.js +76 -50
- package/bun.lock +2 -48
- package/mod.js +148 -0
- package/package.json +11 -16
- package/parser/flight_data.js +189 -306
- package/parser/manifests.js +37 -37
- package/parser/next_data.js +29 -26
- package/parser/types.js +408 -296
- package/parser/urls.js +86 -56
- package/tests/api.test.js +96 -0
- package/tests/integration.test.js +68 -0
- package/tests/parser/flight_data.test.js +105 -0
- package/tests/parser/manifests.test.js +50 -0
- package/tests/parser/next_data.test.js +53 -0
- package/tests/parser/types.test.js +243 -0
- package/tests/parser/urls.test.js +84 -0
- package/tests/property.test.js +299 -0
- package/tests/setup.js +21 -0
- package/tests/utils.test.js +32 -0
- package/tools.js +263 -185
- package/utils.js +29 -24
- package/_.js +0 -10
- package/_.json +0 -12837
- package/api.test.js +0 -41
- package/index.js +0 -8
- package/package-lock.json +0 -291
- package/parser/flight_data.test.js +0 -59
- package/parser/manifests.test.js +0 -36
- package/parser/next_data.test.js +0 -15
- package/parser/types.test.js +0 -261
- package/parser/urls.test.js +0 -26
- package/test/src/index.js +0 -16
- package/tools.test.js +0 -153
- package/utils.test.js +0 -38
package/README.md
CHANGED
|
@@ -1,75 +1,425 @@
|
|
|
1
|
-
#
|
|
1
|
+
# NJSParser (JS)
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
Ported from the Python library `njsparser`.
|
|
3
|
+
A 99.9% AI-generated (including unit tests & README) JS port of [`novitae/njsparser`](https://github.com/novitae/njsparser) for extracting and parsing Next.js hydration data from HTML content.
|
|
5
4
|
|
|
6
|
-
- Parses flight data (from the
|
|
7
|
-
- Parses next data from
|
|
8
|
-
- Parses build manifests
|
|
9
|
-
- Searches for build id
|
|
5
|
+
- Parses flight data (from the **`self.__next_f.push`** scripts)
|
|
6
|
+
- Parses next data from **`__NEXT_DATA__`** script
|
|
7
|
+
- Parses **build manifests**
|
|
8
|
+
- Searches for **build id**
|
|
9
|
+
- Many other things...
|
|
10
|
+
|
|
11
|
+
This is the JavaScript/TypeScript port of the Python [njsparser](https://github.com/novitae/njsparser) library, designed to run on Bun.
|
|
10
12
|
|
|
11
13
|
## Installation
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
bun
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
bun add njsparser
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install from source:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git clone <repository-url>
|
|
23
|
+
cd njsparser/js
|
|
24
|
+
bun install
|
|
17
25
|
```
|
|
18
26
|
|
|
27
|
+
## DOMParser dependency
|
|
28
|
+
|
|
29
|
+
This library doesn’t bundle an HTML parser. You must pass a `DOMParser` implementation when creating the parser:
|
|
30
|
+
|
|
31
|
+
- In Bun tests we use `deno-dom`’s WASM build.
|
|
32
|
+
- In a browser you can pass the native `DOMParser`.
|
|
33
|
+
|
|
19
34
|
## Usage
|
|
20
35
|
|
|
36
|
+
### Basic Setup (Bun)
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import { DOMParser } from 'deno-dom/deno-dom-wasm.ts';
|
|
40
|
+
import { NJSParser } from 'njsparser';
|
|
41
|
+
|
|
42
|
+
const parser = NJSParser({ DOMParser });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Basic Setup (Browser)
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
import { NJSParser } from 'njsparser';
|
|
49
|
+
|
|
50
|
+
const parser = NJSParser({ DOMParser: window.DOMParser });
|
|
51
|
+
```
|
|
52
|
+
|
|
21
53
|
### Parsing Flight Data
|
|
22
54
|
|
|
55
|
+
Flight data is contained in `self.__next_f.push()` scripts within NextJS pages.
|
|
56
|
+
|
|
23
57
|
```javascript
|
|
24
|
-
import {
|
|
58
|
+
import { DOMParser } from 'deno-dom/deno-dom-wasm.ts';
|
|
59
|
+
import { NJSParser } from 'njsparser';
|
|
25
60
|
|
|
26
|
-
const
|
|
61
|
+
const parser = NJSParser({ DOMParser });
|
|
27
62
|
|
|
28
|
-
|
|
29
|
-
const
|
|
63
|
+
const response = await fetch('https://nextjs.org');
|
|
64
|
+
const html = await response.text();
|
|
30
65
|
|
|
31
|
-
|
|
32
|
-
import { Data, RSCPayload } from 'njsparser';
|
|
66
|
+
const flightData = parser.parser.getFlightData(html);
|
|
33
67
|
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
// Use BeautifulFD for easier searching
|
|
69
|
+
const fd = new parser.BeautifulFD(html);
|
|
36
70
|
|
|
37
|
-
|
|
38
|
-
data.
|
|
39
|
-
|
|
40
|
-
|
|
71
|
+
// Find specific element types
|
|
72
|
+
for (const data of fd.find_iter([parser.types.Data])) {
|
|
73
|
+
if (data.content && typeof data.content === 'object' && 'user' in data.content) {
|
|
74
|
+
console.log(data.content.user);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
41
78
|
```
|
|
42
79
|
|
|
43
80
|
### Parsing `__NEXT_DATA__`
|
|
44
81
|
|
|
45
82
|
```javascript
|
|
46
|
-
|
|
83
|
+
const parser = NJSParser({ DOMParser });
|
|
84
|
+
|
|
85
|
+
const html = await fetch('https://example.com').then(r => r.text());
|
|
86
|
+
const nextData = parser.parser.getNextData(html);
|
|
47
87
|
|
|
48
|
-
|
|
49
|
-
console.log(
|
|
88
|
+
if (nextData) {
|
|
89
|
+
console.log('Build ID:', nextData.buildId);
|
|
90
|
+
console.log('Page Props:', nextData.props);
|
|
91
|
+
}
|
|
50
92
|
```
|
|
51
93
|
|
|
52
|
-
###
|
|
94
|
+
### Finding Build ID
|
|
53
95
|
|
|
54
96
|
```javascript
|
|
55
|
-
|
|
97
|
+
const parser = NJSParser({ DOMParser });
|
|
56
98
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
99
|
+
const html = await fetch('https://example.com').then(r => r.text());
|
|
100
|
+
const buildId = parser.findBuildId(html);
|
|
101
|
+
|
|
102
|
+
console.log('Build ID:', buildId);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Parsing Build Manifests
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
const parser = NJSParser({ DOMParser });
|
|
109
|
+
|
|
110
|
+
const manifestScript = await fetch('https://example.com/_next/static/BUILD_ID/_buildManifest.js')
|
|
111
|
+
.then(r => r.text());
|
|
112
|
+
|
|
113
|
+
const manifest = parser.parser.parseBuildManifest(manifestScript);
|
|
114
|
+
console.log('Manifest:', manifest);
|
|
61
115
|
```
|
|
62
116
|
|
|
63
117
|
## API Reference
|
|
64
118
|
|
|
65
|
-
|
|
119
|
+
### Factory Function
|
|
120
|
+
|
|
121
|
+
#### `NJSParser({ DOMParser })`
|
|
122
|
+
|
|
123
|
+
Creates a new parser instance.
|
|
124
|
+
|
|
125
|
+
**Parameters:**
|
|
126
|
+
- `DOMParser` (required): a DOMParser class/constructor to parse HTML
|
|
127
|
+
|
|
128
|
+
**Returns:** Parser instance with all methods and classes
|
|
129
|
+
|
|
130
|
+
### Parser Methods
|
|
131
|
+
|
|
132
|
+
#### `parser.hasFlightData(html)`
|
|
133
|
+
|
|
134
|
+
Check if HTML contains flight data.
|
|
135
|
+
|
|
136
|
+
**Parameters:**
|
|
137
|
+
- `html` (string): HTML content
|
|
138
|
+
|
|
139
|
+
**Returns:** `boolean`
|
|
140
|
+
|
|
141
|
+
#### `parser.getFlightData(html)`
|
|
142
|
+
|
|
143
|
+
Extract and parse flight data from HTML.
|
|
144
|
+
|
|
145
|
+
**Parameters:**
|
|
146
|
+
- `html` (string): HTML content
|
|
147
|
+
|
|
148
|
+
**Returns:** `Object | null` - Parsed flight data or null
|
|
149
|
+
|
|
150
|
+
#### `parser.hasNextData(html)`
|
|
151
|
+
|
|
152
|
+
Check if HTML contains `__NEXT_DATA__` script.
|
|
153
|
+
|
|
154
|
+
**Parameters:**
|
|
155
|
+
- `html` (string): HTML content
|
|
156
|
+
|
|
157
|
+
**Returns:** `boolean`
|
|
158
|
+
|
|
159
|
+
#### `parser.getNextData(html)`
|
|
160
|
+
|
|
161
|
+
Extract and parse `__NEXT_DATA__` script.
|
|
162
|
+
|
|
163
|
+
**Parameters:**
|
|
164
|
+
- `html` (string): HTML content
|
|
165
|
+
|
|
166
|
+
**Returns:** `Object | null` - Parsed JSON data or null
|
|
167
|
+
|
|
168
|
+
#### `parser.parseBuildManifest(script)`
|
|
169
|
+
|
|
170
|
+
Parse build manifest script.
|
|
171
|
+
|
|
172
|
+
**Parameters:**
|
|
173
|
+
- `script` (string): Build manifest script content
|
|
174
|
+
|
|
175
|
+
**Returns:** `Object` - Parsed manifest
|
|
176
|
+
|
|
177
|
+
#### `parser.getBuildManifestPath(buildId, basePath)`
|
|
178
|
+
|
|
179
|
+
Generate build manifest path.
|
|
180
|
+
|
|
181
|
+
**Parameters:**
|
|
182
|
+
- `buildId` (string): Build ID
|
|
183
|
+
- `basePath` (string, optional): Base path
|
|
184
|
+
|
|
185
|
+
**Returns:** `string` - Build manifest path
|
|
186
|
+
|
|
187
|
+
#### `parser.getNextStaticUrls(html)`
|
|
188
|
+
|
|
189
|
+
Find all NextJS static URLs in HTML.
|
|
190
|
+
|
|
191
|
+
**Parameters:**
|
|
192
|
+
- `html` (string): HTML content
|
|
193
|
+
|
|
194
|
+
**Returns:** `Array<string> | null` - Array of URLs or null
|
|
195
|
+
|
|
196
|
+
#### `parser.getBasePath(htmlOrUrls, removeDomain)`
|
|
197
|
+
|
|
198
|
+
Extract base path from URLs or HTML.
|
|
199
|
+
|
|
200
|
+
**Parameters:**
|
|
201
|
+
- `htmlOrUrls` (string | Array<string>): HTML content or array of URLs
|
|
202
|
+
- `removeDomain` (boolean, optional): Remove domain from absolute URLs
|
|
203
|
+
|
|
204
|
+
**Returns:** `string | null` - Base path or null
|
|
205
|
+
|
|
206
|
+
### High-Level Tools
|
|
207
|
+
|
|
208
|
+
#### `hasNextJS(html)`
|
|
209
|
+
|
|
210
|
+
Check if page has any NextJS data.
|
|
211
|
+
|
|
212
|
+
**Parameters:**
|
|
213
|
+
- `html` (string): HTML content
|
|
214
|
+
|
|
215
|
+
**Returns:** `boolean`
|
|
216
|
+
|
|
217
|
+
#### `findBuildId(html)`
|
|
218
|
+
|
|
219
|
+
Find build ID from page (searches static URLs, next data, and flight data).
|
|
220
|
+
|
|
221
|
+
**Parameters:**
|
|
222
|
+
- `html` (string): HTML content
|
|
223
|
+
|
|
224
|
+
**Returns:** `string | null` - Build ID or null
|
|
225
|
+
|
|
226
|
+
#### `findInFlightData(flightData, classFilters, callback, recursive)`
|
|
227
|
+
|
|
228
|
+
Find first matching element in flight data.
|
|
229
|
+
|
|
230
|
+
**Parameters:**
|
|
231
|
+
- `flightData` (Object): Flight data dictionary
|
|
232
|
+
- `classFilters` (Array, optional): Array of Element classes to filter by
|
|
233
|
+
- `callback` (Function, optional): Callback function for filtering
|
|
234
|
+
- `recursive` (boolean, optional): Search recursively (default: true)
|
|
235
|
+
|
|
236
|
+
**Returns:** `Element | null`
|
|
237
|
+
|
|
238
|
+
#### `findallInFlightData(flightData, classFilters, callback, recursive)`
|
|
239
|
+
|
|
240
|
+
Find all matching elements in flight data.
|
|
241
|
+
|
|
242
|
+
**Parameters:**
|
|
243
|
+
- `flightData` (Object): Flight data dictionary
|
|
244
|
+
- `classFilters` (Array, optional): Array of Element classes to filter by
|
|
245
|
+
- `callback` (Function, optional): Callback function for filtering
|
|
246
|
+
- `recursive` (boolean, optional): Search recursively (default: true)
|
|
247
|
+
|
|
248
|
+
**Returns:** `Array<Element>`
|
|
249
|
+
|
|
250
|
+
#### `finditerInFlightData(flightData, classFilters, callback, recursive)`
|
|
251
|
+
|
|
252
|
+
Iterator for finding elements in flight data.
|
|
253
|
+
|
|
254
|
+
**Parameters:**
|
|
255
|
+
- `flightData` (Object): Flight data dictionary
|
|
256
|
+
- `classFilters` (Array, optional): Array of Element classes to filter by
|
|
257
|
+
- `callback` (Function, optional): Callback function for filtering
|
|
258
|
+
- `recursive` (boolean, optional): Search recursively (default: true)
|
|
259
|
+
|
|
260
|
+
**Returns:** `Generator<Element>`
|
|
261
|
+
|
|
262
|
+
### BeautifulFD Class
|
|
263
|
+
|
|
264
|
+
Simplified interface for working with flight data.
|
|
265
|
+
|
|
266
|
+
#### `new BeautifulFD(htmlOrFlightData)`
|
|
267
|
+
|
|
268
|
+
Create BeautifulFD instance.
|
|
269
|
+
|
|
270
|
+
**Parameters:**
|
|
271
|
+
- `htmlOrFlightData` (string | Object): HTML content or parsed flight data
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
#### `find(classFilters, callback, recursive)`
|
|
275
|
+
|
|
276
|
+
Find first matching element.
|
|
277
|
+
|
|
278
|
+
**Parameters:**
|
|
279
|
+
- `classFilters` (Array, optional): Array of Element classes
|
|
280
|
+
- `callback` (Function, optional): Callback function for filtering
|
|
281
|
+
- `recursive` (boolean, optional): Search recursively (default: true)
|
|
282
|
+
|
|
283
|
+
**Returns:** `Element | null`
|
|
284
|
+
|
|
285
|
+
#### `find_all(classFilters, callback, recursive)`
|
|
286
|
+
|
|
287
|
+
Find all matching elements.
|
|
288
|
+
|
|
289
|
+
**Parameters:**
|
|
290
|
+
- `classFilters` (Array, optional): Array of Element classes
|
|
291
|
+
- `callback` (Function, optional): Callback function for filtering
|
|
292
|
+
- `recursive` (boolean, optional): Search recursively (default: true)
|
|
293
|
+
|
|
294
|
+
**Returns:** `Array<Element>`
|
|
295
|
+
|
|
296
|
+
#### `find_iter(classFilters, callback, recursive)`
|
|
297
|
+
|
|
298
|
+
Iterator for finding elements.
|
|
299
|
+
|
|
300
|
+
**Parameters:**
|
|
301
|
+
- `classFilters` (Array, optional): Array of Element classes
|
|
302
|
+
- `callback` (Function, optional): Callback function for filtering
|
|
303
|
+
- `recursive` (boolean, optional): Search recursively (default: true)
|
|
304
|
+
|
|
305
|
+
**Returns:** `Generator<Element>`
|
|
306
|
+
|
|
307
|
+
#### `as_list()`
|
|
308
|
+
|
|
309
|
+
Get flight data as array.
|
|
310
|
+
|
|
311
|
+
**Returns:** `Array<Element>`
|
|
312
|
+
|
|
313
|
+
#### `static from_list(list, viaEnumerate)`
|
|
314
|
+
|
|
315
|
+
Create BeautifulFD from array.
|
|
316
|
+
|
|
317
|
+
**Parameters:**
|
|
318
|
+
- `list` (Array): Array of Elements
|
|
319
|
+
- `viaEnumerate` (boolean, optional): Use array indices as element indices
|
|
320
|
+
|
|
321
|
+
**Returns:** `BeautifulFD`
|
|
322
|
+
|
|
323
|
+
### Element Types
|
|
324
|
+
|
|
325
|
+
All flight data elements extend the base `Element` class:
|
|
326
|
+
|
|
327
|
+
- `Element` - Base class
|
|
328
|
+
- `HintPreload` - Preload hints (class "HL")
|
|
329
|
+
- `Module` - Module imports (class "I")
|
|
330
|
+
- `Text` - Text content (class "T")
|
|
331
|
+
- `Data` - Structured data
|
|
332
|
+
- `EmptyData` - Null/empty data
|
|
333
|
+
- `SpecialData` - Special markers (strings starting with "$")
|
|
334
|
+
- `HTMLElement` - HTML elements
|
|
335
|
+
- `DataContainer` - Container of multiple elements
|
|
336
|
+
- `DataParent` - Parent element with children
|
|
337
|
+
- `URLQuery` - URL query parameters
|
|
338
|
+
- `RSCPayload` - React Server Components payload
|
|
339
|
+
- `Error` - Error elements (class "E")
|
|
340
|
+
|
|
341
|
+
You can access types via `parser.types`:
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
const parser = NJSParser({ DOMParser });
|
|
345
|
+
const fd = new parser.BeautifulFD(html);
|
|
346
|
+
|
|
347
|
+
const textElements = fd.find_all([parser.types.Text]);
|
|
348
|
+
const modules = fd.find_all([parser.types.Module]);
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### API Utilities
|
|
352
|
+
|
|
353
|
+
#### `api.getApiPath(buildId, basePath, path)`
|
|
354
|
+
|
|
355
|
+
Generate API path for a page.
|
|
356
|
+
|
|
357
|
+
**Parameters:**
|
|
358
|
+
- `buildId` (string): Build ID
|
|
359
|
+
- `basePath` (string, optional): Base path
|
|
360
|
+
- `path` (string, optional): Page path
|
|
361
|
+
|
|
362
|
+
**Returns:** `string | null` - API path or null for excluded paths
|
|
363
|
+
|
|
364
|
+
#### `api.getIndexApiPath(buildId, basePath)`
|
|
365
|
+
|
|
366
|
+
Generate index API path.
|
|
367
|
+
|
|
368
|
+
**Parameters:**
|
|
369
|
+
- `buildId` (string): Build ID
|
|
370
|
+
- `basePath` (string, optional): Base path
|
|
371
|
+
|
|
372
|
+
**Returns:** `string`
|
|
373
|
+
|
|
374
|
+
#### `api.isApiExposedFromResponse(statusCode, contentType, text)`
|
|
375
|
+
|
|
376
|
+
Check if API is exposed from response.
|
|
377
|
+
|
|
378
|
+
**Parameters:**
|
|
379
|
+
- `statusCode` (number): HTTP status code
|
|
380
|
+
- `contentType` (string): Content-Type header
|
|
381
|
+
- `text` (string): Response text
|
|
382
|
+
|
|
383
|
+
**Returns:** `boolean`
|
|
384
|
+
|
|
385
|
+
#### `api.listApiPaths(sortedPages, buildId, basePath, isApiExposed)`
|
|
386
|
+
|
|
387
|
+
List API paths from build manifest.
|
|
388
|
+
|
|
389
|
+
**Parameters:**
|
|
390
|
+
- `sortedPages` (Array<string>): Sorted pages from build manifest
|
|
391
|
+
- `buildId` (string): Build ID
|
|
392
|
+
- `basePath` (string): Base path
|
|
393
|
+
- `isApiExposed` (boolean, optional): Is API exposed
|
|
394
|
+
|
|
395
|
+
**Returns:** `Array<string>`
|
|
396
|
+
|
|
397
|
+
## Differences from Python Version
|
|
398
|
+
|
|
399
|
+
1. **DOMParser Parameter**: The JavaScript version requires a DOMParser instance to be provided, making it compatible with different environments (Bun, browser, Node.js with jsdom, etc.)
|
|
400
|
+
|
|
401
|
+
2. **No CLI**: This port focuses on the library functionality only. No CLI is included.
|
|
402
|
+
|
|
403
|
+
3. **Factory Pattern**: Uses a factory function to inject dependencies rather than global imports.
|
|
404
|
+
|
|
405
|
+
4. **Async/Await**: JavaScript version is designed to work with async/await for fetching HTML.
|
|
406
|
+
|
|
407
|
+
5. **Native JSON**: Uses native `JSON.parse` and `JSON.stringify` instead of orjson.
|
|
408
|
+
|
|
409
|
+
6. **Native eval**: Uses JavaScript `eval` for build manifest parsing instead of pythonmonkey.
|
|
410
|
+
|
|
411
|
+
## Testing
|
|
412
|
+
|
|
413
|
+
Run tests with Bun:
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
bun test
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## License
|
|
420
|
+
|
|
421
|
+
MIT
|
|
66
422
|
|
|
67
|
-
|
|
68
|
-
- `get_flight_data(html)`
|
|
69
|
-
- `get_next_data(html)`
|
|
70
|
-
- `find_build_id(html)`
|
|
71
|
-
- `has_nextjs(html)`
|
|
72
|
-
- `get_next_static_urls(html)`
|
|
73
|
-
- `get_base_path(html)`
|
|
423
|
+
## Credits
|
|
74
424
|
|
|
75
|
-
|
|
425
|
+
This is a JavaScript port of the Python [njsparser](https://github.com/novitae/njsparser) library by novitae.
|
package/api.js
CHANGED
|
@@ -1,57 +1,83 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* API path generation utilities
|
|
3
|
+
*/
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
const _excluded_paths = ["/404", "/_app", "/_error", "/sitemap.xml", "/_middleware"];
|
|
5
|
+
import { join } from './utils.js';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
const _index_json = 'index.json';
|
|
8
|
+
const _excluded_paths = ['/404', '/_app', '/_error', '/sitemap.xml', '/_middleware'];
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Get API path for a page
|
|
12
|
+
* @param {string} buildId - Build ID
|
|
13
|
+
* @param {string} basePath - Base path (optional)
|
|
14
|
+
* @param {string} path - Page path (optional)
|
|
15
|
+
* @returns {string|null} API path or null
|
|
16
|
+
*/
|
|
17
|
+
export function getApiPath(buildId, basePath = '', path = null) {
|
|
18
|
+
if (path === null) {
|
|
19
|
+
path = _index_json;
|
|
20
|
+
} else if (_excluded_paths.includes(path)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!path.endsWith('.json')) {
|
|
25
|
+
path += '.json';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (path.endsWith('/.json')) {
|
|
29
|
+
path = _index_json;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return join(basePath, '/_next/data/', buildId, path);
|
|
33
|
+
}
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
+
/**
|
|
36
|
+
* Get index API path
|
|
37
|
+
* @param {string} buildId - Build ID
|
|
38
|
+
* @param {string} basePath - Base path (optional)
|
|
39
|
+
* @returns {string} Index API path
|
|
40
|
+
*/
|
|
41
|
+
export function getIndexApiPath(buildId, basePath = '') {
|
|
42
|
+
return getApiPath(buildId, basePath, _index_json);
|
|
43
|
+
}
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Check if API is exposed from response
|
|
47
|
+
* @param {number} statusCode - HTTP status code
|
|
48
|
+
* @param {string} contentType - Content-Type header
|
|
49
|
+
* @param {string} text - Response text
|
|
50
|
+
* @returns {boolean} True if API is exposed
|
|
51
|
+
*/
|
|
52
|
+
export function isApiExposedFromResponse(statusCode, contentType, text) {
|
|
53
|
+
if (statusCode === 200) {
|
|
54
|
+
return true;
|
|
55
|
+
} else if (contentType !== null && contentType.startsWith('application/json')) {
|
|
56
|
+
return true;
|
|
57
|
+
} else {
|
|
58
|
+
return text === '{"notFound":true}';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
45
61
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
/**
|
|
63
|
+
* List API paths from build manifest
|
|
64
|
+
* @param {Array<string>} sortedPages - Sorted pages from build manifest
|
|
65
|
+
* @param {string} buildId - Build ID
|
|
66
|
+
* @param {string} basePath - Base path
|
|
67
|
+
* @param {boolean} isApiExposed - Is API exposed (optional)
|
|
68
|
+
* @returns {Array<string>} Array of API paths
|
|
69
|
+
*/
|
|
70
|
+
export function listApiPaths(sortedPages, buildId, basePath, isApiExposed = true) {
|
|
71
|
+
const result = [];
|
|
72
|
+
|
|
73
|
+
if (isApiExposed !== false) {
|
|
74
|
+
for (const path of sortedPages) {
|
|
75
|
+
const apiPath = getApiPath(buildId, basePath, path);
|
|
76
|
+
if (apiPath !== null) {
|
|
77
|
+
result.push(apiPath);
|
|
78
|
+
}
|
|
55
79
|
}
|
|
56
|
-
|
|
57
|
-
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result;
|
|
83
|
+
}
|
package/bun.lock
CHANGED
|
@@ -4,65 +4,19 @@
|
|
|
4
4
|
"workspaces": {
|
|
5
5
|
"": {
|
|
6
6
|
"name": "njsparser",
|
|
7
|
-
"dependencies": {
|
|
8
|
-
"cheerio": "^1.0.0",
|
|
9
|
-
},
|
|
10
7
|
"devDependencies": {
|
|
11
8
|
"bun-types": "latest",
|
|
9
|
+
"deno-dom": "git+https://github.com/b-fuze/deno-dom.git",
|
|
12
10
|
},
|
|
13
11
|
},
|
|
14
12
|
},
|
|
15
13
|
"packages": {
|
|
16
14
|
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
|
|
17
15
|
|
|
18
|
-
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
|
19
|
-
|
|
20
16
|
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
|
21
17
|
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
|
|
25
|
-
|
|
26
|
-
"css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
|
|
27
|
-
|
|
28
|
-
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
|
29
|
-
|
|
30
|
-
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
|
31
|
-
|
|
32
|
-
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
|
33
|
-
|
|
34
|
-
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
|
35
|
-
|
|
36
|
-
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
|
37
|
-
|
|
38
|
-
"encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="],
|
|
39
|
-
|
|
40
|
-
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
|
41
|
-
|
|
42
|
-
"htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="],
|
|
43
|
-
|
|
44
|
-
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
|
45
|
-
|
|
46
|
-
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
|
47
|
-
|
|
48
|
-
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
|
49
|
-
|
|
50
|
-
"parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
|
|
51
|
-
|
|
52
|
-
"parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="],
|
|
53
|
-
|
|
54
|
-
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
|
55
|
-
|
|
56
|
-
"undici": ["undici@7.22.0", "", {}, "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg=="],
|
|
18
|
+
"deno-dom": ["deno-dom@github:b-fuze/deno-dom#92ff960", {}, "b-fuze-deno-dom-92ff960"],
|
|
57
19
|
|
|
58
20
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
59
|
-
|
|
60
|
-
"whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
|
|
61
|
-
|
|
62
|
-
"whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
|
|
63
|
-
|
|
64
|
-
"htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
|
65
|
-
|
|
66
|
-
"parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
|
67
21
|
}
|
|
68
22
|
}
|