@invinite-org/invinite-data-cli 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +341 -374
- package/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/agents/invinite-data.md +59 -28
- package/claude-plugin/commands/analyze-earnings-call.md +15 -14
- package/claude-plugin/commands/analyze-filing.md +8 -7
- package/claude-plugin/commands/compare-companies.md +5 -5
- package/claude-plugin/commands/financial-research.md +9 -9
- package/claude-plugin/commands/ownership-review.md +6 -6
- package/claude-plugin/skills/invinite-data-cli/SKILL.md +466 -570
- package/dist/bin/invd.js +11 -2
- package/dist/bin/invd.js.map +1 -1
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +48 -29
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client/api-client.d.ts +24 -24
- package/dist/src/client/api-client.js +52 -70
- package/dist/src/client/api-client.js.map +1 -1
- package/dist/src/client/pagination.d.ts +12 -11
- package/dist/src/client/pagination.js +47 -56
- package/dist/src/client/pagination.js.map +1 -1
- package/dist/src/commands/agent-setup.d.ts +21 -0
- package/dist/src/commands/agent-setup.js +112 -0
- package/dist/src/commands/agent-setup.js.map +1 -0
- package/dist/src/commands/api.d.ts +27 -0
- package/dist/src/commands/api.js +180 -0
- package/dist/src/commands/api.js.map +1 -0
- package/dist/src/commands/auth.d.ts +37 -0
- package/dist/src/commands/auth.js +336 -0
- package/dist/src/commands/auth.js.map +1 -0
- package/dist/src/commands/completion.d.ts +7 -0
- package/dist/src/commands/completion.js +115 -0
- package/dist/src/commands/completion.js.map +1 -0
- package/dist/src/commands/config.d.ts +4 -0
- package/dist/src/commands/config.js +85 -48
- package/dist/src/commands/config.js.map +1 -1
- package/dist/src/commands/plugin.d.ts +8 -0
- package/dist/src/commands/plugin.js +8 -10
- package/dist/src/commands/plugin.js.map +1 -1
- package/dist/src/config/auth.d.ts +17 -1
- package/dist/src/config/auth.js +50 -36
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/config-manager.d.ts +56 -4
- package/dist/src/config/config-manager.js +173 -29
- package/dist/src/config/config-manager.js.map +1 -1
- package/dist/src/config/toml.d.ts +7 -0
- package/dist/src/config/toml.js +79 -0
- package/dist/src/config/toml.js.map +1 -0
- package/dist/src/examples.d.ts +22 -0
- package/dist/src/examples.js +56 -0
- package/dist/src/examples.js.map +1 -0
- package/dist/src/generated/cli-manifest.json +5573 -5140
- package/dist/src/hand-written-commands.d.ts +11 -41
- package/dist/src/hand-written-commands.js +7 -17
- package/dist/src/hand-written-commands.js.map +1 -1
- package/dist/src/output/jq.d.ts +15 -0
- package/dist/src/output/jq.js +234 -0
- package/dist/src/output/jq.js.map +1 -0
- package/dist/src/output/options.d.ts +29 -0
- package/dist/src/output/options.js +72 -0
- package/dist/src/output/options.js.map +1 -0
- package/dist/src/output/render.d.ts +54 -0
- package/dist/src/output/render.js +160 -0
- package/dist/src/output/render.js.map +1 -0
- package/dist/src/shared/errors.d.ts +59 -15
- package/dist/src/shared/errors.js +126 -38
- package/dist/src/shared/errors.js.map +1 -1
- package/dist/src/shared/prompt.d.ts +10 -0
- package/dist/src/shared/prompt.js +24 -0
- package/dist/src/shared/prompt.js.map +1 -0
- package/dist/src/shared/spinner.js +2 -1
- package/dist/src/shared/spinner.js.map +1 -1
- package/dist/src/spec-commands.d.ts +14 -6
- package/dist/src/spec-commands.js +454 -174
- package/dist/src/spec-commands.js.map +1 -1
- package/dist/src/spec-flags.d.ts +11 -6
- package/dist/src/spec-flags.js +14 -7
- package/dist/src/spec-flags.js.map +1 -1
- package/dist/src/spec-manifest.d.ts +416 -116
- package/dist/src/spec-manifest.js +44 -13
- package/dist/src/spec-manifest.js.map +1 -1
- package/dist/src/update-notifier.d.ts +9 -0
- package/dist/src/update-notifier.js +63 -0
- package/dist/src/update-notifier.js.map +1 -0
- package/package.json +2 -3
- package/dist/src/client/earnings-live.d.ts +0 -194
- package/dist/src/client/earnings-live.js +0 -300
- package/dist/src/client/earnings-live.js.map +0 -1
- package/dist/src/client/news-live.d.ts +0 -72
- package/dist/src/client/news-live.js +0 -120
- package/dist/src/client/news-live.js.map +0 -1
- package/dist/src/client/pdf.d.ts +0 -15
- package/dist/src/client/pdf.js +0 -15
- package/dist/src/client/pdf.js.map +0 -1
- package/dist/src/client/websocket-client.d.ts +0 -19
- package/dist/src/client/websocket-client.js +0 -174
- package/dist/src/client/websocket-client.js.map +0 -1
- package/dist/src/commands/earnings.d.ts +0 -16
- package/dist/src/commands/earnings.js +0 -464
- package/dist/src/commands/earnings.js.map +0 -1
- package/dist/src/commands/login.d.ts +0 -15
- package/dist/src/commands/login.js +0 -249
- package/dist/src/commands/login.js.map +0 -1
- package/dist/src/commands/news.d.ts +0 -2
- package/dist/src/commands/news.js +0 -69
- package/dist/src/commands/news.js.map +0 -1
- package/dist/src/commands/sec.d.ts +0 -47
- package/dist/src/commands/sec.js +0 -191
- package/dist/src/commands/sec.js.map +0 -1
- package/dist/src/commands/ws.d.ts +0 -33
- package/dist/src/commands/ws.js +0 -116
- package/dist/src/commands/ws.js.map +0 -1
- package/dist/src/formatters/json-formatter.d.ts +0 -1
- package/dist/src/formatters/json-formatter.js +0 -4
- package/dist/src/formatters/json-formatter.js.map +0 -1
- package/dist/src/formatters/table-formatter.d.ts +0 -4
- package/dist/src/formatters/table-formatter.js +0 -73
- package/dist/src/formatters/table-formatter.js.map +0 -1
- package/dist/src/formatters/time.d.ts +0 -2
- package/dist/src/formatters/time.js +0 -14
- package/dist/src/formatters/time.js.map +0 -1
- package/dist/src/shared/output.d.ts +0 -3
- package/dist/src/shared/output.js +0 -13
- package/dist/src/shared/output.js.map +0 -1
- package/dist/src/shared/readiness.d.ts +0 -4
- package/dist/src/shared/readiness.js +0 -60
- package/dist/src/shared/readiness.js.map +0 -1
- package/dist/src/shared/types.d.ts +0 -11
- package/dist/src/shared/types.js +0 -2
- package/dist/src/shared/types.js.map +0 -1
package/dist/bin/invd.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { CommanderError } from 'commander';
|
|
2
3
|
import { createProgram } from '../src/cli.js';
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
import { exitCodeOfCommanderError } from '../src/shared/errors.js';
|
|
5
|
+
try {
|
|
6
|
+
await createProgram().parseAsync();
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
// `exitOverride` turns commander's own exits into errors: help / version keep their code, a usage error is 2.
|
|
10
|
+
if (error instanceof CommanderError)
|
|
11
|
+
process.exit(exitCodeOfCommanderError(error));
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
5
14
|
//# sourceMappingURL=invd.js.map
|
package/dist/bin/invd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invd.js","sourceRoot":"","sources":["../../bin/invd.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"invd.js","sourceRoot":"","sources":["../../bin/invd.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,IAAI,CAAC;IACH,MAAM,aAAa,EAAE,CAAC,UAAU,EAAE,CAAC;AACrC,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,8GAA8G;IAC9G,IAAI,KAAK,YAAY,cAAc;QAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAC;IACnF,MAAM,KAAK,CAAC;AACd,CAAC"}
|
package/dist/src/cli.d.ts
CHANGED
|
@@ -7,4 +7,6 @@ export declare function packageVersion(): string;
|
|
|
7
7
|
* `dist/src/generated/` in the build (`scripts/copy-manifest.mjs` puts it there — `tsc` copies no JSON).
|
|
8
8
|
*/
|
|
9
9
|
export declare function loadManifest(): Manifest;
|
|
10
|
+
/** The eight commands `invd --help` lists first. */
|
|
11
|
+
export declare const TOP_COMMANDS: ReadonlyArray<[string, string]>;
|
|
10
12
|
export declare function createProgram(): Command;
|
package/dist/src/cli.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import { setJsonMode } from './shared/spinner.js';
|
|
4
3
|
import { formatCreditUsage, takeCreditUsage } from './client/api-client.js';
|
|
4
|
+
import { registerAgentSetupCommand } from './commands/agent-setup.js';
|
|
5
|
+
import { registerApiCommand } from './commands/api.js';
|
|
6
|
+
import { registerAuthCommands } from './commands/auth.js';
|
|
7
|
+
import { registerCompletionCommand } from './commands/completion.js';
|
|
5
8
|
import { registerConfigCommands } from './commands/config.js';
|
|
6
|
-
import { registerEarningsCommands } from './commands/earnings.js';
|
|
7
|
-
import { registerLoginCommands } from './commands/login.js';
|
|
8
|
-
import { registerNewsCommands } from './commands/news.js';
|
|
9
9
|
import { registerPluginCommands } from './commands/plugin.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { DASHBOARD_URL } from './config/config-manager.js';
|
|
11
|
+
import { applyColorEnv } from './output/render.js';
|
|
12
12
|
import { registerSpecCommands } from './spec-commands.js';
|
|
13
13
|
import { parseManifest } from './spec-manifest.js';
|
|
14
|
+
import { runUpdateNotifier } from './update-notifier.js';
|
|
14
15
|
const PACKAGE_NAME = '@invinite-org/invinite-data-cli';
|
|
15
16
|
/** `package.json` sits one level up under tsx (`src/cli.ts`), two in the build (`dist/src/cli.js`). */
|
|
16
17
|
export function packageVersion() {
|
|
@@ -36,39 +37,57 @@ export function loadManifest() {
|
|
|
36
37
|
manifest ??= parseManifest(createRequire(import.meta.url)('./generated/cli-manifest.json'));
|
|
37
38
|
return manifest;
|
|
38
39
|
}
|
|
40
|
+
/** The eight commands `invd --help` lists first. */
|
|
41
|
+
export const TOP_COMMANDS = [
|
|
42
|
+
['invd financials AAPL --period quarterly --limit 4', 'statements (standardized / as-reported; --view detailed)'],
|
|
43
|
+
['invd companies get AAPL', 'company profile'],
|
|
44
|
+
['invd filings AAPL --form 10-K', 'SEC filings'],
|
|
45
|
+
['invd events AAPL', 'earnings calls (calendar and history)'],
|
|
46
|
+
['invd events transcript AAPL 2026Q3', 'a call transcript by fiscal period'],
|
|
47
|
+
['invd holdings AAPL', '13F holders (or --filer <cik>)'],
|
|
48
|
+
['invd insider-trades AAPL', 'Form 4 trades'],
|
|
49
|
+
['invd search filings "supply chain risk" --company AAPL', 'full-text search (also transcripts, news)'],
|
|
50
|
+
];
|
|
51
|
+
function topHelp() {
|
|
52
|
+
const width = Math.max(...TOP_COMMANDS.map(([command]) => command.length));
|
|
53
|
+
return [
|
|
54
|
+
'Most used:',
|
|
55
|
+
...TOP_COMMANDS.map(([command, what]) => ` ${command.padEnd(width)} ${what}`),
|
|
56
|
+
'',
|
|
57
|
+
'Output: table on a terminal, tab-separated when piped; --json [fields], --output json|jsonl|csv, --jq, --all.',
|
|
58
|
+
`Docs: ${DASHBOARD_URL}/docs/cli · keys and usage: ${DASHBOARD_URL}`,
|
|
59
|
+
'',
|
|
60
|
+
].join('\n');
|
|
61
|
+
}
|
|
62
|
+
const EXIT_HELP = '\nExit codes: 0 ok · 1 failure · 2 usage · 3 partial · 4 auth required · 5 integrity · 6 credits / plan\n';
|
|
39
63
|
export function createProgram() {
|
|
40
64
|
const program = new Command();
|
|
65
|
+
applyColorEnv();
|
|
41
66
|
program
|
|
42
67
|
.name('invd')
|
|
43
|
-
.description('CLI for the Invinite financial data API')
|
|
44
|
-
.version(packageVersion())
|
|
45
|
-
|
|
46
|
-
.
|
|
47
|
-
.option('--show-cost', 'Print the credits a command cost to stderr (or set INVD_SHOW_COST=1)')
|
|
48
|
-
// Inherited by every group, so rostered and generated commands list alphabetically together.
|
|
68
|
+
.description('CLI for the Invinite financial data API (v2)')
|
|
69
|
+
.version(packageVersion(), '-V, --version')
|
|
70
|
+
// Root options only before the subcommand: an operation's own `--version` (a transcript version) is its own.
|
|
71
|
+
.enablePositionalOptions()
|
|
49
72
|
.configureHelp({ sortSubcommands: true })
|
|
50
|
-
.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.hook('postAction', (thisCommand) => {
|
|
73
|
+
.showSuggestionAfterError()
|
|
74
|
+
// Inherited by every command registered below: commander's usage errors become exit code 2 (bin/invd.ts).
|
|
75
|
+
.exitOverride()
|
|
76
|
+
.addHelpText('beforeAll', (context) => (context.command === program ? topHelp() : ''))
|
|
77
|
+
.addHelpText('after', EXIT_HELP)
|
|
78
|
+
.hook('postAction', async (_thisCommand, actionCommand) => {
|
|
57
79
|
// Always drained, so a long-lived program never carries one command's cost into the next.
|
|
58
80
|
const usage = takeCreditUsage();
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
81
|
+
if (usage && (actionCommand.opts().showCost || process.env.INVD_SHOW_COST === '1'))
|
|
82
|
+
console.error(formatCreditUsage(usage));
|
|
83
|
+
await runUpdateNotifier(packageVersion());
|
|
62
84
|
});
|
|
63
|
-
|
|
64
|
-
// `earnings` / `news` / `sec` action attaches to the group its hand-written commands created.
|
|
65
|
-
registerLoginCommands(program);
|
|
85
|
+
registerAuthCommands(program);
|
|
66
86
|
registerConfigCommands(program);
|
|
67
87
|
registerPluginCommands(program);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
registerSecCommands(program);
|
|
88
|
+
registerAgentSetupCommand(program);
|
|
89
|
+
registerApiCommand(program);
|
|
90
|
+
registerCompletionCommand(program, () => [...new Set(loadManifest().operations.map(operation => operation.path))].sort());
|
|
72
91
|
registerSpecCommands(program, loadManifest());
|
|
73
92
|
return program;
|
|
74
93
|
}
|
package/dist/src/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
+
export type QueryValue = string | string[] | undefined;
|
|
2
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
1
3
|
export interface RequestOptions {
|
|
4
|
+
/** A path under the base URL (`/v2/filings`); a query string on it is kept. */
|
|
2
5
|
path: string;
|
|
3
|
-
|
|
4
|
-
|
|
6
|
+
method?: HttpMethod;
|
|
7
|
+
/** An array value is sent as a repeated parameter (`a=1&a=2`); `undefined` / `''` are dropped. */
|
|
8
|
+
query?: Record<string, QueryValue>;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
/** Sent as JSON. */
|
|
11
|
+
body?: unknown;
|
|
5
12
|
silent?: boolean;
|
|
6
13
|
/** `false` for a public operation: no API key is resolved or sent (default `true`). */
|
|
7
14
|
auth?: boolean;
|
|
8
15
|
}
|
|
16
|
+
export interface ApiResponse<T = unknown> {
|
|
17
|
+
status: number;
|
|
18
|
+
headers: Headers;
|
|
19
|
+
/** The parsed JSON body; `undefined` on a 204 / empty body. */
|
|
20
|
+
body: T;
|
|
21
|
+
}
|
|
22
|
+
/** The request URL: base URL + path (+ its own query) + `query`. */
|
|
23
|
+
export declare function buildUrl(path: string, query?: RequestOptions['query']): string;
|
|
9
24
|
export interface CreditUsage {
|
|
10
25
|
/** `X-Credits-Charged`, summed over every request of this invocation (`--all` pages included). */
|
|
11
26
|
charged: number;
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* response carried the header (routes that skip the quota decision, e.g. `/v1/account/usage`).
|
|
16
|
-
*/
|
|
17
|
-
includedRemaining: number | null;
|
|
27
|
+
/** The last `X-Credits-Remaining` (included credits left in the period); `null` when no response carried it. */
|
|
28
|
+
remaining: number | null;
|
|
29
|
+
requests: number;
|
|
18
30
|
}
|
|
19
31
|
/** What this invocation's requests cost so far, then reset; `null` when no response carried a credit header. */
|
|
20
32
|
export declare function takeCreditUsage(): CreditUsage | null;
|
|
21
33
|
export declare function formatCreditUsage(usage: CreditUsage): string;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*/
|
|
27
|
-
export declare function apiDownload(opts: RequestOptions & {
|
|
28
|
-
accept: string;
|
|
29
|
-
signal?: AbortSignal;
|
|
30
|
-
}): Promise<Response>;
|
|
31
|
-
export interface ApiRequestOptions extends RequestOptions {
|
|
32
|
-
method: 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
33
|
-
/** Sent as JSON. */
|
|
34
|
-
body?: unknown;
|
|
35
|
-
}
|
|
36
|
-
/** A write with a JSON body; same auth / `ApiError` handling as `apiGet`. A 204 / empty body → `undefined`. */
|
|
37
|
-
export declare function apiRequest<T = unknown>(opts: ApiRequestOptions): Promise<T>;
|
|
34
|
+
/** One request. A non-2xx answer throws `ApiError`; the body is parsed as JSON when there is one. */
|
|
35
|
+
export declare function apiCall<T = unknown>(opts: RequestOptions): Promise<ApiResponse<T>>;
|
|
36
|
+
/** A GET's body. */
|
|
37
|
+
export declare function apiGet<T = unknown>(opts: Omit<RequestOptions, 'method' | 'body'>): Promise<T>;
|
|
@@ -2,42 +2,46 @@ import { getApiKey } from '../config/auth.js';
|
|
|
2
2
|
import { getBaseUrl } from '../config/config-manager.js';
|
|
3
3
|
import { ApiError, parseRetryAfter } from '../shared/errors.js';
|
|
4
4
|
import { createSpinner } from '../shared/spinner.js';
|
|
5
|
-
|
|
5
|
+
/** The request URL: base URL + path (+ its own query) + `query`. */
|
|
6
|
+
export function buildUrl(path, query) {
|
|
6
7
|
const base = getBaseUrl().replace(/\/+$/, '');
|
|
7
|
-
const url = new URL(`${base}${path}`);
|
|
8
|
-
|
|
9
|
-
for (const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
url.searchParams.append(key, item);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
8
|
+
const url = new URL(`${base}${path.startsWith('/') ? path : `/${path}`}`);
|
|
9
|
+
for (const [key, value] of Object.entries(query ?? {})) {
|
|
10
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
11
|
+
if (item !== undefined && item !== '')
|
|
12
|
+
url.searchParams.append(key, item);
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
return url.toString();
|
|
18
16
|
}
|
|
19
|
-
const
|
|
17
|
+
const text = (body, key) => {
|
|
20
18
|
if (typeof body !== 'object' || body === null)
|
|
21
19
|
return undefined;
|
|
22
20
|
const value = Reflect.get(body, key);
|
|
23
21
|
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
24
22
|
};
|
|
23
|
+
/** A refused answer → `ApiError`: problem+json (`title`, `detail`, `code`), else the 3.x `{error, message}`. */
|
|
25
24
|
async function apiErrorFrom(response) {
|
|
26
|
-
const
|
|
27
|
-
|
|
25
|
+
const raw = await response.text().catch(() => '');
|
|
26
|
+
let body = null;
|
|
27
|
+
try {
|
|
28
|
+
body = raw ? JSON.parse(raw) : null;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
body = raw || null;
|
|
32
|
+
}
|
|
33
|
+
return new ApiError(response.status, text(body, 'title') ?? text(body, 'error') ?? (response.statusText || 'Unknown Error'), text(body, 'detail') ?? text(body, 'message') ?? `Request failed with status ${response.status}`, parseRetryAfter(response.headers?.get('Retry-After')), text(body, 'code'), body ?? undefined);
|
|
28
34
|
}
|
|
29
35
|
let creditUsage = null;
|
|
30
36
|
function recordCredits(response) {
|
|
31
37
|
const charged = response.headers?.get('X-Credits-Charged') ?? null;
|
|
32
|
-
const remaining = response.headers?.get('X-Credits-
|
|
38
|
+
const remaining = response.headers?.get('X-Credits-Remaining') ?? null;
|
|
33
39
|
if (charged === null && remaining === null)
|
|
34
40
|
return;
|
|
35
|
-
const chargedCredits = Number(charged) || 0;
|
|
36
41
|
creditUsage = {
|
|
37
|
-
charged: (creditUsage?.charged ?? 0) +
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
: Math.max(0, (Number(remaining) || 0) - chargedCredits),
|
|
42
|
+
charged: (creditUsage?.charged ?? 0) + (Number(charged) || 0),
|
|
43
|
+
remaining: remaining === null ? creditUsage?.remaining ?? null : Number(remaining) || 0,
|
|
44
|
+
requests: (creditUsage?.requests ?? 0) + 1,
|
|
41
45
|
};
|
|
42
46
|
}
|
|
43
47
|
/** What this invocation's requests cost so far, then reset; `null` when no response carried a credit header. */
|
|
@@ -47,66 +51,44 @@ export function takeCreditUsage() {
|
|
|
47
51
|
return usage;
|
|
48
52
|
}
|
|
49
53
|
export function formatCreditUsage(usage) {
|
|
50
|
-
return `credits: ${usage.charged} ·
|
|
51
|
-
}
|
|
52
|
-
async function request(opts, accept, init = {}) {
|
|
53
|
-
const url = buildUrl(opts.path, opts.params);
|
|
54
|
-
const headers = { 'Accept': accept };
|
|
55
|
-
if (opts.auth !== false)
|
|
56
|
-
headers['Authorization'] = `Bearer ${await getApiKey()}`;
|
|
57
|
-
if (init.body !== undefined)
|
|
58
|
-
headers['Content-Type'] = 'application/json';
|
|
59
|
-
const response = await fetch(url, {
|
|
60
|
-
headers,
|
|
61
|
-
...(init.method ? { method: init.method } : {}),
|
|
62
|
-
...(init.body !== undefined ? { body: JSON.stringify(init.body) } : {}),
|
|
63
|
-
});
|
|
64
|
-
recordCredits(response);
|
|
65
|
-
if (!response.ok) {
|
|
66
|
-
throw await apiErrorFrom(response);
|
|
67
|
-
}
|
|
68
|
-
return response;
|
|
54
|
+
return `credits: ${usage.charged} · remaining: ${usage.remaining ?? '—'}`;
|
|
69
55
|
}
|
|
70
|
-
|
|
71
|
-
|
|
56
|
+
/** One request. A non-2xx answer throws `ApiError`; the body is parsed as JSON when there is one. */
|
|
57
|
+
export async function apiCall(opts) {
|
|
58
|
+
const spinner = opts.silent ? null : createSpinner(opts.method && opts.method !== 'GET' ? 'Sending request...' : 'Fetching data...');
|
|
72
59
|
try {
|
|
73
|
-
const
|
|
74
|
-
const
|
|
60
|
+
const url = buildUrl(opts.path, opts.query);
|
|
61
|
+
const headers = { Accept: 'application/json', ...opts.headers };
|
|
62
|
+
if (opts.auth !== false)
|
|
63
|
+
headers.Authorization = `Bearer ${await getApiKey()}`;
|
|
64
|
+
if (opts.body !== undefined)
|
|
65
|
+
headers['Content-Type'] = 'application/json';
|
|
66
|
+
const response = await fetch(url, {
|
|
67
|
+
method: opts.method ?? 'GET',
|
|
68
|
+
headers,
|
|
69
|
+
...(opts.body !== undefined ? { body: JSON.stringify(opts.body) } : {}),
|
|
70
|
+
});
|
|
71
|
+
recordCredits(response);
|
|
72
|
+
if (!response.ok)
|
|
73
|
+
throw await apiErrorFrom(response);
|
|
74
|
+
const raw = response.status === 204 ? '' : await response.text();
|
|
75
|
+
let body;
|
|
76
|
+
try {
|
|
77
|
+
body = raw ? JSON.parse(raw) : undefined;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
body = raw;
|
|
81
|
+
}
|
|
75
82
|
spinner?.succeed('Done');
|
|
76
|
-
return body;
|
|
83
|
+
return { status: response.status, headers: response.headers ?? new Headers(), body: body };
|
|
77
84
|
}
|
|
78
85
|
catch (error) {
|
|
79
86
|
spinner?.fail('Failed');
|
|
80
87
|
throw error;
|
|
81
88
|
}
|
|
82
89
|
}
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
*/
|
|
87
|
-
export async function apiDownload(opts) {
|
|
88
|
-
const url = buildUrl(opts.path, opts.params);
|
|
89
|
-
const headers = { 'Accept': opts.accept };
|
|
90
|
-
if (opts.auth !== false)
|
|
91
|
-
headers['Authorization'] = `Bearer ${await getApiKey()}`;
|
|
92
|
-
const response = await fetch(url, { headers, ...(opts.signal ? { signal: opts.signal } : {}) });
|
|
93
|
-
recordCredits(response);
|
|
94
|
-
if (!response.ok)
|
|
95
|
-
throw await apiErrorFrom(response);
|
|
96
|
-
return response;
|
|
97
|
-
}
|
|
98
|
-
/** A write with a JSON body; same auth / `ApiError` handling as `apiGet`. A 204 / empty body → `undefined`. */
|
|
99
|
-
export async function apiRequest(opts) {
|
|
100
|
-
const spinner = opts.silent ? null : createSpinner('Sending request...');
|
|
101
|
-
try {
|
|
102
|
-
const response = await request(opts, 'application/json', { method: opts.method, body: opts.body });
|
|
103
|
-
const text = response.status === 204 ? '' : await response.text();
|
|
104
|
-
spinner?.succeed('Done');
|
|
105
|
-
return (text ? JSON.parse(text) : undefined);
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
spinner?.fail('Failed');
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
90
|
+
/** A GET's body. */
|
|
91
|
+
export async function apiGet(opts) {
|
|
92
|
+
return (await apiCall({ ...opts, method: 'GET' })).body;
|
|
111
93
|
}
|
|
112
94
|
//# sourceMappingURL=api-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/client/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/client/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA0BrD,oEAAoE;AACpE,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,KAA+B;IACpE,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IAC1E,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QACvD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE;gBAAE,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,GAAW,EAAsB,EAAE;IAC9D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAChE,MAAM,KAAK,GAAY,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC9C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC,CAAC;AAEF,gHAAgH;AAChH,KAAK,UAAU,YAAY,CAAC,QAAkB;IAC5C,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAClD,IAAI,IAAI,GAAY,IAAI,CAAC;IACzB,IAAI,CAAC;QACH,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,QAAQ,CACjB,QAAQ,CAAC,MAAM,EACf,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,eAAe,CAAC,EACtF,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,8BAA8B,QAAQ,CAAC,MAAM,EAAE,EAChG,eAAe,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC,EACrD,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAClB,IAAI,IAAI,SAAS,CAClB,CAAC;AACJ,CAAC;AAUD,IAAI,WAAW,GAAuB,IAAI,CAAC;AAE3C,SAAS,aAAa,CAAC,QAAkB;IACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,mBAAmB,CAAC,IAAI,IAAI,CAAC;IACnE,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,qBAAqB,CAAC,IAAI,IAAI,CAAC;IACvE,IAAI,OAAO,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO;IACnD,WAAW,GAAG;QACZ,OAAO,EAAE,CAAC,WAAW,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QACvF,QAAQ,EAAE,CAAC,WAAW,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,eAAe;IAC7B,MAAM,KAAK,GAAG,WAAW,CAAC;IAC1B,WAAW,GAAG,IAAI,CAAC;IACnB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAkB;IAClD,OAAO,YAAY,KAAK,CAAC,OAAO,iBAAiB,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,CAAC;AAC5E,CAAC;AAED,qGAAqG;AACrG,MAAM,CAAC,KAAK,UAAU,OAAO,CAAc,IAAoB;IAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;IACrI,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,OAAO,GAA2B,EAAE,MAAM,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACxF,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,CAAC,aAAa,GAAG,UAAU,MAAM,SAAS,EAAE,EAAE,CAAC;QAC/E,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC1E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;YAC5B,OAAO;YACP,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxE,CAAC,CAAC;QACH,aAAa,CAAC,QAAQ,CAAC,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACjE,IAAI,IAAa,CAAC;QAClB,IAAI,CAAC;YACH,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,GAAG,GAAG,CAAC;QACb,CAAC;QACD,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,IAAI,OAAO,EAAE,EAAE,IAAI,EAAE,IAAS,EAAE,CAAC;IAClG,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,oBAAoB;AACpB,MAAM,CAAC,KAAK,UAAU,MAAM,CAAc,IAA6C;IACrF,OAAO,CAAC,MAAM,OAAO,CAAI,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { type ApiResponse, type RequestOptions } from './api-client.js';
|
|
1
2
|
import { ApiError } from '../shared/errors.js';
|
|
2
|
-
type NextUrlExtractor<T> = (data: T) => string | undefined;
|
|
3
3
|
/**
|
|
4
4
|
* How `--all` waits out a refused page instead of dropping the pages it already holds: a 429 (the API's admission
|
|
5
5
|
* limit) or 503 is retried up to `retries` times, each after the answer's `Retry-After`, else `baseDelayMs * 2^n`,
|
|
@@ -12,15 +12,16 @@ export declare const PAGE_RETRY: {
|
|
|
12
12
|
};
|
|
13
13
|
/** The wait before retry number `retry` (0-based) of a refused page. */
|
|
14
14
|
export declare function pageRetryDelayMs(error: ApiError, retry: number): number;
|
|
15
|
-
|
|
15
|
+
/** The `rel="next"` target of a `Link` header, if any. */
|
|
16
|
+
export declare function linkNext(header: string | null | undefined): string | undefined;
|
|
17
|
+
/** A v2 list page's `next_cursor` (only while `has_more` is not false). */
|
|
18
|
+
export declare function nextCursorOf(body: unknown): string | undefined;
|
|
19
|
+
/** The rows of a v2 list page (`data[]`). */
|
|
20
|
+
export declare const rowsOf: (body: unknown) => unknown[];
|
|
21
|
+
/** The request of the page after `page`: `next_cursor` on the same request, else the `Link rel="next"` URL. */
|
|
22
|
+
export declare function nextRequest(request: RequestOptions, page: ApiResponse): RequestOptions | undefined;
|
|
16
23
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
24
|
+
* Every page from `first` on, following `next_cursor` (or `Link rel="next"`) until `has_more` is false. A failure
|
|
25
|
+
* after the first page is thrown as is; the caller knows what it already wrote (`PartialResultError`).
|
|
19
26
|
*/
|
|
20
|
-
export declare function
|
|
21
|
-
/**
|
|
22
|
-
* `--all` over a multi-company response (`companies[].next_url`): follows `companies[0].next_url` and concatenates
|
|
23
|
-
* each company's `itemsPaths` arrays by index, carrying each company's own `next_url` forward.
|
|
24
|
-
*/
|
|
25
|
-
export declare function fetchAllCompanyPages(initialData: unknown, itemsPaths: readonly string[]): Promise<unknown>;
|
|
26
|
-
export {};
|
|
27
|
+
export declare function walkPages(request: RequestOptions, first: ApiResponse): AsyncGenerator<ApiResponse>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { apiCall } from './api-client.js';
|
|
2
2
|
import { ApiError } from '../shared/errors.js';
|
|
3
3
|
import { createSpinner } from '../shared/spinner.js';
|
|
4
|
-
import { pick } from '../formatters/flatten.js';
|
|
5
4
|
/**
|
|
6
5
|
* How `--all` waits out a refused page instead of dropping the pages it already holds: a 429 (the API's admission
|
|
7
6
|
* limit) or 503 is retried up to `retries` times, each after the answer's `Retry-After`, else `baseDelayMs * 2^n`,
|
|
@@ -14,15 +13,43 @@ export function pageRetryDelayMs(error, retry) {
|
|
|
14
13
|
return Math.min(PAGE_RETRY.maxDelayMs, error.retryAfterMs ?? PAGE_RETRY.baseDelayMs * 2 ** retry);
|
|
15
14
|
}
|
|
16
15
|
const sleep = (ms) => new Promise(done => setTimeout(done, ms));
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
17
|
+
/** The `rel="next"` target of a `Link` header, if any. */
|
|
18
|
+
export function linkNext(header) {
|
|
19
|
+
if (!header)
|
|
20
|
+
return undefined;
|
|
21
|
+
for (const part of header.split(',')) {
|
|
22
|
+
const match = /<([^>]+)>\s*;(.*)$/.exec(part.trim());
|
|
23
|
+
if (match?.[1] && /\brel\s*=\s*"?next"?/i.test(match[2] ?? ''))
|
|
24
|
+
return match[1];
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
/** A v2 list page's `next_cursor` (only while `has_more` is not false). */
|
|
29
|
+
export function nextCursorOf(body) {
|
|
30
|
+
if (!isRecord(body) || body.has_more === false)
|
|
31
|
+
return undefined;
|
|
32
|
+
return typeof body.next_cursor === 'string' && body.next_cursor !== '' ? body.next_cursor : undefined;
|
|
33
|
+
}
|
|
34
|
+
/** The rows of a v2 list page (`data[]`). */
|
|
35
|
+
export const rowsOf = (body) => (isRecord(body) && Array.isArray(body.data) ? body.data : []);
|
|
36
|
+
/** The request of the page after `page`: `next_cursor` on the same request, else the `Link rel="next"` URL. */
|
|
37
|
+
export function nextRequest(request, page) {
|
|
38
|
+
const cursor = nextCursorOf(page.body);
|
|
39
|
+
if (cursor)
|
|
40
|
+
return { ...request, query: { ...request.query, cursor } };
|
|
41
|
+
const link = linkNext(page.headers.get('Link'));
|
|
42
|
+
if (!link || (isRecord(page.body) && page.body.has_more === false))
|
|
43
|
+
return undefined;
|
|
44
|
+
const url = new URL(link, 'http://placeholder');
|
|
45
|
+
const query = {};
|
|
46
|
+
url.searchParams.forEach((value, key) => { (query[key] ??= []).push(value); });
|
|
47
|
+
return { ...request, path: url.pathname, query };
|
|
48
|
+
}
|
|
49
|
+
async function fetchPage(request, onWait) {
|
|
23
50
|
for (let retry = 0;; retry += 1) {
|
|
24
51
|
try {
|
|
25
|
-
return await
|
|
52
|
+
return await apiCall({ ...request, silent: true });
|
|
26
53
|
}
|
|
27
54
|
catch (error) {
|
|
28
55
|
if (!isRetryable(error) || retry >= PAGE_RETRY.retries)
|
|
@@ -33,65 +60,29 @@ async function fetchPage(nextUrl, onWait) {
|
|
|
33
60
|
}
|
|
34
61
|
}
|
|
35
62
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Every page from `first` on, following `next_cursor` (or `Link rel="next"`) until `has_more` is false. A failure
|
|
65
|
+
* after the first page is thrown as is; the caller knows what it already wrote (`PartialResultError`).
|
|
66
|
+
*/
|
|
67
|
+
export async function* walkPages(request, first) {
|
|
68
|
+
yield first;
|
|
69
|
+
let next = nextRequest(request, first);
|
|
39
70
|
let page = 1;
|
|
40
|
-
while (
|
|
71
|
+
while (next) {
|
|
41
72
|
page += 1;
|
|
42
73
|
const spinner = createSpinner(`Fetching page ${page}...`);
|
|
43
74
|
try {
|
|
44
|
-
const
|
|
75
|
+
const current = await fetchPage(next, (seconds, retry) => {
|
|
45
76
|
spinner.text = `Page ${page} refused (rate limit) — retry ${retry}/${PAGE_RETRY.retries} in ${seconds}s...`;
|
|
46
77
|
});
|
|
47
|
-
result = mergeResults(result, pageData);
|
|
48
|
-
nextUrl = extractNextUrl(pageData);
|
|
49
78
|
spinner.succeed(`Page ${page} fetched`);
|
|
79
|
+
yield current;
|
|
80
|
+
next = nextRequest(next, current);
|
|
50
81
|
}
|
|
51
82
|
catch (error) {
|
|
52
83
|
spinner.fail(`Page ${page} failed`);
|
|
53
84
|
throw error;
|
|
54
85
|
}
|
|
55
86
|
}
|
|
56
|
-
return result;
|
|
57
|
-
}
|
|
58
|
-
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
59
|
-
const arrayAt = (value, path) => {
|
|
60
|
-
const found = pick(value, path);
|
|
61
|
-
return Array.isArray(found) ? found : [];
|
|
62
|
-
};
|
|
63
|
-
/** A copy of `target` with `value` at the dotted `path` (missing objects on the way are created). */
|
|
64
|
-
function withValueAt(target, path, value) {
|
|
65
|
-
const [head, ...rest] = path.split('.');
|
|
66
|
-
if (rest.length === 0)
|
|
67
|
-
return { ...target, [head]: value };
|
|
68
|
-
const child = target[head];
|
|
69
|
-
return { ...target, [head]: withValueAt(isRecord(child) ? child : {}, rest.join('.'), value) };
|
|
70
|
-
}
|
|
71
|
-
const nextUrlAt = (value, path) => {
|
|
72
|
-
const found = pick(value, path);
|
|
73
|
-
return typeof found === 'string' && found !== '' ? found : undefined;
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* `--all` over a response that pages with a top-level `next_url`: every `itemsPaths` array is concatenated across
|
|
77
|
-
* pages (a path a page does not carry adds nothing); the other fields come from the last page.
|
|
78
|
-
*/
|
|
79
|
-
export async function fetchAllTopPages(initialData, itemsPaths) {
|
|
80
|
-
return fetchAllPages(initialData, data => nextUrlAt(data, 'next_url'), (accumulated, page) => itemsPaths.reduce((merged, path) => withValueAt(merged, path, [...arrayAt(accumulated, path), ...arrayAt(page, path)]), isRecord(page) ? page : {}));
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* `--all` over a multi-company response (`companies[].next_url`): follows `companies[0].next_url` and concatenates
|
|
84
|
-
* each company's `itemsPaths` arrays by index, carrying each company's own `next_url` forward.
|
|
85
|
-
*/
|
|
86
|
-
export async function fetchAllCompanyPages(initialData, itemsPaths) {
|
|
87
|
-
return fetchAllPages(initialData, data => nextUrlAt(data, 'companies.0.next_url'), (accumulated, page) => {
|
|
88
|
-
const base = isRecord(accumulated) ? accumulated : {};
|
|
89
|
-
const companies = arrayAt(accumulated, 'companies').map((company, index) => {
|
|
90
|
-
const pageCompany = pick(page, `companies.${index}`);
|
|
91
|
-
const merged = itemsPaths.reduce((acc, path) => withValueAt(acc, path, [...arrayAt(company, path), ...arrayAt(pageCompany, path)]), isRecord(company) ? company : {});
|
|
92
|
-
return { ...merged, next_url: pick(pageCompany, 'next_url') };
|
|
93
|
-
});
|
|
94
|
-
return { ...base, companies };
|
|
95
|
-
});
|
|
96
87
|
}
|
|
97
88
|
//# sourceMappingURL=pagination.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../../src/client/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../../src/client/pagination.ts"],"names":[],"mappings":"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"}
|