@typeship-ax/cli 0.6.0 → 0.9.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/AGENTS.md +31 -0
- package/README.md +26 -9
- package/api.json +6861 -3083
- package/api.md +508 -277
- package/dist/api-identity.d.ts +40 -0
- package/dist/api-identity.d.ts.map +1 -0
- package/dist/api-identity.js +128 -0
- package/dist/auth-profiles.d.ts +30 -0
- package/dist/auth-profiles.d.ts.map +1 -0
- package/dist/auth-profiles.js +138 -0
- package/dist/cli-agent.d.ts +9 -1
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +26 -9
- package/dist/cli.js +600 -582
- package/dist/console-login-check.d.ts +21 -0
- package/dist/console-login-check.d.ts.map +1 -0
- package/dist/console-login-check.js +107 -0
- package/dist/console-login-contract.d.ts +45 -0
- package/dist/console-login-contract.d.ts.map +1 -0
- package/dist/console-login-contract.js +40 -0
- package/dist/core/http.d.ts +21 -92
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +143 -221
- package/dist/core/pagination.d.ts.map +1 -1
- package/dist/core/pagination.js +6 -34
- package/dist/credential-storage.d.ts +24 -0
- package/dist/credential-storage.d.ts.map +1 -0
- package/dist/credential-storage.js +207 -0
- package/dist/dates.d.ts +0 -2
- package/dist/dates.d.ts.map +1 -1
- package/dist/dates.js +0 -1
- package/dist/docs.d.ts +36 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/docs.js +258 -0
- package/dist/errors.d.ts +42 -34
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +30 -20
- package/dist/index.d.ts +27 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -14
- package/dist/named-credentials.d.ts +21 -0
- package/dist/named-credentials.d.ts.map +1 -0
- package/dist/named-credentials.js +86 -0
- package/dist/oauth-login.d.ts +39 -0
- package/dist/oauth-login.d.ts.map +1 -0
- package/dist/oauth-login.js +171 -0
- package/dist/oauth-request.d.ts +21 -0
- package/dist/oauth-request.d.ts.map +1 -0
- package/dist/oauth-request.js +119 -0
- package/dist/oauth-session.d.ts +106 -0
- package/dist/oauth-session.d.ts.map +1 -0
- package/dist/oauth-session.js +244 -0
- package/dist/ops.d.ts +18 -0
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +31 -17
- package/dist/polling-login.d.ts +57 -0
- package/dist/polling-login.d.ts.map +1 -0
- package/dist/polling-login.js +204 -0
- package/dist/resources/account.d.ts +4 -4
- package/dist/resources/account.d.ts.map +1 -1
- package/dist/resources/account.js +1 -0
- package/dist/resources/api-keys.d.ts +13 -8
- package/dist/resources/api-keys.d.ts.map +1 -1
- package/dist/resources/api-keys.js +5 -1
- package/dist/resources/definition-revisions.d.ts +58 -0
- package/dist/resources/definition-revisions.d.ts.map +1 -0
- package/dist/resources/definition-revisions.js +114 -0
- package/dist/resources/definitions.d.ts +35 -0
- package/dist/resources/definitions.d.ts.map +1 -0
- package/dist/resources/definitions.js +60 -0
- package/dist/resources/generate.d.ts +18 -7
- package/dist/resources/generate.d.ts.map +1 -1
- package/dist/resources/generate.js +13 -5
- package/dist/resources/generations.d.ts +6 -6
- package/dist/resources/generations.d.ts.map +1 -1
- package/dist/resources/generations.js +3 -1
- package/dist/resources/projects.d.ts +111 -35
- package/dist/resources/projects.d.ts.map +1 -1
- package/dist/resources/projects.js +125 -15
- package/dist/resources/targets.d.ts +97 -0
- package/dist/resources/targets.d.ts.map +1 -0
- package/dist/resources/targets.js +197 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +135 -62
- package/dist/types.d.ts +2072 -267
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +20 -3
- package/package.json +2 -1
- package/src/api-identity.ts +98 -0
- package/src/auth-profiles.ts +114 -0
- package/src/cli-agent.ts +28 -11
- package/src/cli.ts +528 -560
- package/src/console-login-check.ts +88 -0
- package/src/console-login-contract.ts +65 -0
- package/src/core/http.ts +156 -305
- package/src/core/pagination.ts +6 -30
- package/src/credential-storage.ts +183 -0
- package/src/dates.ts +0 -1
- package/src/docs.ts +239 -0
- package/src/errors.ts +52 -41
- package/src/index.ts +49 -14
- package/src/named-credentials.ts +74 -0
- package/src/oauth-login.ts +184 -0
- package/src/oauth-request.ts +90 -0
- package/src/oauth-session.ts +258 -0
- package/src/ops.ts +56 -17
- package/src/polling-login.ts +165 -0
- package/src/resources/account.ts +6 -3
- package/src/resources/api-keys.ts +27 -7
- package/src/resources/definition-revisions.ts +207 -0
- package/src/resources/definitions.ts +122 -0
- package/src/resources/generate.ts +29 -6
- package/src/resources/generations.ts +9 -4
- package/src/resources/projects.ts +274 -41
- package/src/resources/targets.ts +378 -0
- package/src/schemas.ts +135 -62
- package/src/types.ts +2273 -322
- package/dist/resources/spec-revisions.d.ts +0 -47
- package/dist/resources/spec-revisions.d.ts.map +0 -1
- package/dist/resources/spec-revisions.js +0 -90
- package/src/resources/spec-revisions.ts +0 -150
package/dist/core/http.js
CHANGED
|
@@ -29,43 +29,21 @@ export class UnexpectedApiError extends ApiError {
|
|
|
29
29
|
super("Unexpected HTTP status " + status, status, body, response);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
/** A
|
|
33
|
-
export class
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
/** A successful response declared JSON but carried a body that could not be parsed. */
|
|
33
|
+
export class ResponseParseError extends Error {
|
|
34
|
+
status;
|
|
35
|
+
body;
|
|
36
36
|
response;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
super(
|
|
40
|
-
this.name = "
|
|
41
|
-
this.
|
|
37
|
+
cause;
|
|
38
|
+
constructor(body, response, cause) {
|
|
39
|
+
super("HTTP " + response.status + " response body was not valid JSON");
|
|
40
|
+
this.name = "ResponseParseError";
|
|
41
|
+
this.status = response.status;
|
|
42
|
+
this.body = body;
|
|
42
43
|
this.response = response;
|
|
44
|
+
this.cause = cause;
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
|
-
/** Serialize a selection object to a GraphQL selection set. Strings pass
|
|
46
|
-
* through untouched. */
|
|
47
|
-
export function selectionToString(selection) {
|
|
48
|
-
if (typeof selection === "string")
|
|
49
|
-
return selection;
|
|
50
|
-
const parts = [];
|
|
51
|
-
for (const [key, value] of Object.entries(selection)) {
|
|
52
|
-
if (!value)
|
|
53
|
-
continue;
|
|
54
|
-
if (key === "on" && typeof value === "object") {
|
|
55
|
-
for (const [typeName, sub] of Object.entries(value)) {
|
|
56
|
-
if (sub && typeof sub === "object")
|
|
57
|
-
parts.push("... on " + typeName + " " + selectionToString(sub));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else if (value === true) {
|
|
61
|
-
parts.push(key);
|
|
62
|
-
}
|
|
63
|
-
else if (typeof value === "object") {
|
|
64
|
-
parts.push(key + " " + selectionToString(value));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return "{ " + (parts.length > 0 ? parts.join(" ") : "__typename") + " }";
|
|
68
|
-
}
|
|
69
47
|
/** Request or response data did not match the spec's schema (opt-in via the
|
|
70
48
|
* client's validate option). Never thrown: returned as the error side of
|
|
71
49
|
* ApiResult, like every other failure. */
|
|
@@ -211,7 +189,7 @@ function transportFailureMessage(method, url, cause) {
|
|
|
211
189
|
}
|
|
212
190
|
return method + " " + url + " failed: " + detail;
|
|
213
191
|
}
|
|
214
|
-
/** The request
|
|
192
|
+
/** The request failed before a complete HTTP response arrived (network failure, timeout, abort, or truncated body). */
|
|
215
193
|
export class TransportError extends Error {
|
|
216
194
|
cause;
|
|
217
195
|
constructor(message, cause) {
|
|
@@ -220,6 +198,33 @@ export class TransportError extends Error {
|
|
|
220
198
|
this.cause = cause;
|
|
221
199
|
}
|
|
222
200
|
}
|
|
201
|
+
/** Resolve one complete alternative in spec order. Missing or incompatible
|
|
202
|
+
* combinations contribute no partial credentials. Anonymous operations send
|
|
203
|
+
* no generated credentials; explicit headers/hooks remain application owned. */
|
|
204
|
+
function selectSecurity(requirements, credentials) {
|
|
205
|
+
for (const requirement of requirements) {
|
|
206
|
+
const names = Object.keys(requirement);
|
|
207
|
+
if (!names.length || names.some((name) => !Object.hasOwn(credentials, name)))
|
|
208
|
+
continue;
|
|
209
|
+
const selected = { headers: Object.create(null), query: Object.create(null) };
|
|
210
|
+
const destinations = new Set();
|
|
211
|
+
let conflict = false;
|
|
212
|
+
for (const name of names) {
|
|
213
|
+
for (const location of ["headers", "query"]) {
|
|
214
|
+
for (const [wire, value] of Object.entries(credentials[name][location] ?? {})) {
|
|
215
|
+
const destination = location + ":" + (location === "headers" ? wire.toLowerCase() : wire);
|
|
216
|
+
if (destinations.has(destination))
|
|
217
|
+
conflict = true;
|
|
218
|
+
destinations.add(destination);
|
|
219
|
+
selected[location][wire] = value;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!conflict)
|
|
224
|
+
return selected;
|
|
225
|
+
}
|
|
226
|
+
return {};
|
|
227
|
+
}
|
|
223
228
|
const RETRYABLE_STATUSES = new Set([408, 429, 500, 502, 503, 504]);
|
|
224
229
|
/** Credential-bearing headers fetch itself drops when a redirect crosses to
|
|
225
230
|
* another origin. A spec's own API-key headers join them below. */
|
|
@@ -269,8 +274,13 @@ export class HttpCore {
|
|
|
269
274
|
const policy = { ...this.config.retry, ...req.retry };
|
|
270
275
|
const maxRetries = req.options?.maxRetries ?? policy.maxRetries ?? this.config.maxRetries;
|
|
271
276
|
const timeoutMs = req.options?.timeoutMs ?? this.config.timeoutMs;
|
|
272
|
-
const retryAllowed = req.idempotent === true ||
|
|
273
|
-
|
|
277
|
+
const retryAllowed = req.idempotent === true ||
|
|
278
|
+
req.method === "GET" ||
|
|
279
|
+
req.idempotencyKey !== undefined ||
|
|
280
|
+
policy.retryNonIdempotent === true;
|
|
281
|
+
const retryableStatuses = policy.statuses
|
|
282
|
+
? new Set(policy.statuses)
|
|
283
|
+
: RETRYABLE_STATUSES;
|
|
274
284
|
// One key per logical call, reused on every retry — that's the point
|
|
275
285
|
// of idempotency keys.
|
|
276
286
|
const autoIdempotencyKey = req.idempotencyKey !== undefined &&
|
|
@@ -299,16 +309,19 @@ export class HttpCore {
|
|
|
299
309
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
300
310
|
let response;
|
|
301
311
|
const attemptStarted = Date.now();
|
|
312
|
+
const emitResponseDebug = (value, body) => this.config.debug?.({
|
|
313
|
+
method: req.method,
|
|
314
|
+
path: req.path,
|
|
315
|
+
status: value.status,
|
|
316
|
+
durationMs: Date.now() - attemptStarted,
|
|
317
|
+
attempt: attempt + 1,
|
|
318
|
+
requestId: requestIdFromBody(body) ??
|
|
319
|
+
value.headers.get("request-id") ??
|
|
320
|
+
value.headers.get("x-request-id") ??
|
|
321
|
+
undefined,
|
|
322
|
+
});
|
|
302
323
|
try {
|
|
303
324
|
response = await this.send(req, timeoutMs, attempt, autoIdempotencyKey);
|
|
304
|
-
this.config.debug?.({
|
|
305
|
-
method: req.method,
|
|
306
|
-
path: req.path,
|
|
307
|
-
status: response.status,
|
|
308
|
-
durationMs: Date.now() - attemptStarted,
|
|
309
|
-
attempt: attempt + 1,
|
|
310
|
-
requestId: response.headers.get("x-request-id") ?? response.headers.get("request-id") ?? undefined,
|
|
311
|
-
});
|
|
312
325
|
}
|
|
313
326
|
catch (cause) {
|
|
314
327
|
lastError = cause;
|
|
@@ -328,21 +341,24 @@ export class HttpCore {
|
|
|
328
341
|
return { ok: false, error };
|
|
329
342
|
}
|
|
330
343
|
if (response.ok) {
|
|
331
|
-
if (req.stream) {
|
|
332
|
-
return { ok: true, data: sseEvents(response), response: meta(response) };
|
|
333
|
-
}
|
|
334
344
|
let data;
|
|
335
345
|
try {
|
|
336
346
|
data = (await parseBody(response, req.method));
|
|
337
347
|
}
|
|
338
348
|
catch (cause) {
|
|
339
|
-
const
|
|
349
|
+
const parseError = cause instanceof ResponseParseError ? cause : undefined;
|
|
350
|
+
emitResponseDebug(response, parseError?.body);
|
|
351
|
+
const error = (parseError ?? new TransportError("The response body read failed before completing", cause));
|
|
340
352
|
await this.config.onError?.(error, { method: req.method, path: req.path });
|
|
341
|
-
return { ok: false, error, response: meta(response) };
|
|
353
|
+
return { ok: false, error, response: parseError?.response ?? meta(response) };
|
|
342
354
|
}
|
|
343
|
-
|
|
355
|
+
emitResponseDebug(response, data);
|
|
356
|
+
const responseMeta = meta(response, data);
|
|
357
|
+
let responseData = data;
|
|
358
|
+
let shouldValidateResponse = responseData !== undefined;
|
|
359
|
+
if (opSchemas?.res && this.config.validate.responses && shouldValidateResponse) {
|
|
344
360
|
const violations = [];
|
|
345
|
-
validateAgainstSchema(
|
|
361
|
+
validateAgainstSchema(responseData, opSchemas.res, "response", violations, this.config.schemaDefs);
|
|
346
362
|
if (violations.length > 0) {
|
|
347
363
|
const validationError = new ValidationError("response", violations);
|
|
348
364
|
if (this.config.validate.mode === "warn") {
|
|
@@ -351,28 +367,27 @@ export class HttpCore {
|
|
|
351
367
|
else {
|
|
352
368
|
const error = validationError;
|
|
353
369
|
await this.config.onError?.(error, { method: req.method, path: req.path });
|
|
354
|
-
return { ok: false, error, response:
|
|
370
|
+
return { ok: false, error, response: responseMeta };
|
|
355
371
|
}
|
|
356
372
|
}
|
|
357
373
|
}
|
|
358
|
-
|
|
359
|
-
const payload = data;
|
|
360
|
-
const responseMeta = meta(response);
|
|
361
|
-
if (Array.isArray(payload?.errors) && payload.errors.length > 0) {
|
|
362
|
-
const gqlError = new GraphQLRequestError(payload.errors, responseMeta);
|
|
363
|
-
await this.config.onError?.(gqlError, { method: req.method, path: req.path });
|
|
364
|
-
return { ok: false, error: gqlError, response: responseMeta };
|
|
365
|
-
}
|
|
366
|
-
return { ok: true, data: payload?.data?.[req.graphqlField], response: responseMeta };
|
|
367
|
-
}
|
|
368
|
-
return { ok: true, data, response: meta(response) };
|
|
374
|
+
return { ok: true, data, response: responseMeta };
|
|
369
375
|
}
|
|
370
376
|
// 429 is safe to retry regardless of idempotency; other retryable
|
|
371
377
|
// statuses only when the verb is idempotent.
|
|
372
378
|
const retryableStatus = retryableStatuses.has(response.status) &&
|
|
373
379
|
(retryAllowed || response.status === 429);
|
|
374
380
|
if (attempt < maxRetries && retryableStatus) {
|
|
375
|
-
|
|
381
|
+
const delay = retryAfterMs(response) ?? backoff(attempt, policy);
|
|
382
|
+
let retryBody;
|
|
383
|
+
try {
|
|
384
|
+
retryBody = await parseBody(response, req.method);
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
retryBody = undefined;
|
|
388
|
+
}
|
|
389
|
+
emitResponseDebug(response, retryBody);
|
|
390
|
+
await sleep(delay);
|
|
376
391
|
continue;
|
|
377
392
|
}
|
|
378
393
|
let body;
|
|
@@ -382,7 +397,8 @@ export class HttpCore {
|
|
|
382
397
|
catch {
|
|
383
398
|
body = undefined; // error responses keep their status even if the body read aborts
|
|
384
399
|
}
|
|
385
|
-
|
|
400
|
+
emitResponseDebug(response, body);
|
|
401
|
+
const responseMeta = meta(response, body);
|
|
386
402
|
const Ctor = req.errors?.[String(response.status)] ??
|
|
387
403
|
req.errors?.[String(Math.floor(response.status / 100)) + "XX"] ??
|
|
388
404
|
req.errors?.["default"];
|
|
@@ -398,8 +414,9 @@ export class HttpCore {
|
|
|
398
414
|
return { ok: false, error };
|
|
399
415
|
}
|
|
400
416
|
async send(req, timeoutMs, attempt, autoIdempotencyKey) {
|
|
417
|
+
const selected = req.security && this.config.credentials ? selectSecurity(req.security, this.config.credentials) : {};
|
|
401
418
|
const headers = {};
|
|
402
|
-
for (const [k, v] of Object.entries(this.config.headers)) {
|
|
419
|
+
for (const [k, v] of Object.entries({ ...this.config.headers, ...selected.headers })) {
|
|
403
420
|
headers[k] = await resolveAuthValue(v);
|
|
404
421
|
}
|
|
405
422
|
if (req.idempotencyKey && autoIdempotencyKey) {
|
|
@@ -416,31 +433,19 @@ export class HttpCore {
|
|
|
416
433
|
}
|
|
417
434
|
const context = {
|
|
418
435
|
method: req.method,
|
|
419
|
-
url: await this.buildUrl(req),
|
|
436
|
+
url: await this.buildUrl(req, selected.query),
|
|
420
437
|
headers,
|
|
421
438
|
attempt,
|
|
422
439
|
};
|
|
423
440
|
await this.config.onRequest?.(context);
|
|
424
|
-
// Streaming responses are exempt from the attempt timeout once headers
|
|
425
|
-
// arrive (an event stream may stay open far longer than timeoutMs);
|
|
426
|
-
// everything else keeps the timeout armed through the body read, so a
|
|
427
|
-
// stalled body aborts instead of hanging.
|
|
428
441
|
const signals = [];
|
|
429
|
-
|
|
430
|
-
if (req.stream) {
|
|
431
|
-
const headersTimeout = new AbortController();
|
|
432
|
-
const timer = setTimeout(() => headersTimeout.abort(new DOMException("Timed out waiting for response headers", "TimeoutError")), timeoutMs);
|
|
433
|
-
timer.unref?.();
|
|
434
|
-
clearStreamTimeout = () => clearTimeout(timer);
|
|
435
|
-
signals.push(headersTimeout.signal);
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
442
|
+
{
|
|
438
443
|
signals.push(AbortSignal.timeout(timeoutMs));
|
|
439
444
|
}
|
|
440
445
|
if (req.options?.signal)
|
|
441
446
|
signals.push(req.options.signal);
|
|
442
447
|
try {
|
|
443
|
-
const signal =
|
|
448
|
+
const signal = composeSignals(signals);
|
|
444
449
|
const response = this.manualRedirects
|
|
445
450
|
? await this.followRedirects(context, body, signal)
|
|
446
451
|
: await this.config.fetch(context.url, {
|
|
@@ -453,7 +458,6 @@ export class HttpCore {
|
|
|
453
458
|
return response;
|
|
454
459
|
}
|
|
455
460
|
finally {
|
|
456
|
-
clearStreamTimeout?.();
|
|
457
461
|
}
|
|
458
462
|
}
|
|
459
463
|
/**
|
|
@@ -514,7 +518,7 @@ export class HttpCore {
|
|
|
514
518
|
origin = target.origin;
|
|
515
519
|
}
|
|
516
520
|
}
|
|
517
|
-
async buildUrl(req) {
|
|
521
|
+
async buildUrl(req, authQuery) {
|
|
518
522
|
const base = this.config.baseUrl.replace(/\/+$/, "");
|
|
519
523
|
const url = new URL(base + req.path);
|
|
520
524
|
for (const [k, v] of Object.entries(req.query ?? {})) {
|
|
@@ -529,7 +533,7 @@ export class HttpCore {
|
|
|
529
533
|
appendDeep(url.searchParams, k, v);
|
|
530
534
|
}
|
|
531
535
|
}
|
|
532
|
-
for (const [k, v] of Object.entries(this.config.query)) {
|
|
536
|
+
for (const [k, v] of Object.entries({ ...this.config.query, ...authQuery })) {
|
|
533
537
|
url.searchParams.append(k, await resolveAuthValue(v));
|
|
534
538
|
}
|
|
535
539
|
return url.toString();
|
|
@@ -538,64 +542,6 @@ export class HttpCore {
|
|
|
538
542
|
async function resolveAuthValue(value) {
|
|
539
543
|
return typeof value === "function" ? await value() : value;
|
|
540
544
|
}
|
|
541
|
-
/** Parse a text/event-stream body into SseEvents, lazily. */
|
|
542
|
-
async function* sseEvents(response) {
|
|
543
|
-
if (!response.body)
|
|
544
|
-
return;
|
|
545
|
-
const reader = response.body.getReader();
|
|
546
|
-
const decoder = new TextDecoder();
|
|
547
|
-
let buffer = "";
|
|
548
|
-
let dataLines = [];
|
|
549
|
-
let eventName;
|
|
550
|
-
let eventId;
|
|
551
|
-
const flush = () => {
|
|
552
|
-
if (dataLines.length === 0)
|
|
553
|
-
return undefined;
|
|
554
|
-
const event = { data: dataLines.join("\n") };
|
|
555
|
-
if (eventName !== undefined)
|
|
556
|
-
event.event = eventName;
|
|
557
|
-
if (eventId !== undefined)
|
|
558
|
-
event.id = eventId;
|
|
559
|
-
dataLines = [];
|
|
560
|
-
eventName = undefined;
|
|
561
|
-
return event;
|
|
562
|
-
};
|
|
563
|
-
try {
|
|
564
|
-
while (true) {
|
|
565
|
-
const { done, value } = await reader.read();
|
|
566
|
-
if (done)
|
|
567
|
-
break;
|
|
568
|
-
buffer += decoder.decode(value, { stream: true });
|
|
569
|
-
let newline;
|
|
570
|
-
while ((newline = buffer.indexOf("\n")) !== -1) {
|
|
571
|
-
const hasCr = newline > 0 && buffer[newline - 1] === "\r";
|
|
572
|
-
const line = buffer.slice(0, hasCr ? newline - 1 : newline);
|
|
573
|
-
buffer = buffer.slice(newline + 1);
|
|
574
|
-
if (line === "") {
|
|
575
|
-
const event = flush();
|
|
576
|
-
if (event)
|
|
577
|
-
yield event;
|
|
578
|
-
}
|
|
579
|
-
else if (line.startsWith("data:")) {
|
|
580
|
-
dataLines.push(line.slice(5).replace(/^ /, ""));
|
|
581
|
-
}
|
|
582
|
-
else if (line.startsWith("event:")) {
|
|
583
|
-
eventName = line.slice(6).replace(/^ /, "");
|
|
584
|
-
}
|
|
585
|
-
else if (line.startsWith("id:")) {
|
|
586
|
-
eventId = line.slice(3).replace(/^ /, "");
|
|
587
|
-
}
|
|
588
|
-
// comments (":") and "retry:" are intentionally ignored
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
const last = flush();
|
|
592
|
-
if (last)
|
|
593
|
-
yield last;
|
|
594
|
-
}
|
|
595
|
-
finally {
|
|
596
|
-
reader.releaseLock();
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
545
|
/** Default rendering for debug events (the boolean debug:true sink). */
|
|
600
546
|
export function formatDebugEvent(name, event) {
|
|
601
547
|
return name + " " + event.method + " " + event.path +
|
|
@@ -612,54 +558,6 @@ export function bearerAuth(token) {
|
|
|
612
558
|
}
|
|
613
559
|
return "Bearer " + token;
|
|
614
560
|
}
|
|
615
|
-
/**
|
|
616
|
-
* OAuth2 client-credentials token source: fetches from the token URL,
|
|
617
|
-
* caches until expiry (60s early refresh), and shares one in-flight
|
|
618
|
-
* request across concurrent callers. Returned function plugs in as an
|
|
619
|
-
* Authorization AuthValue, resolved before every attempt.
|
|
620
|
-
*/
|
|
621
|
-
export function oauthClientCredentials(config) {
|
|
622
|
-
let token;
|
|
623
|
-
let expiresAt = 0;
|
|
624
|
-
let inflight;
|
|
625
|
-
const fetchImpl = config.fetchImpl ?? fetch;
|
|
626
|
-
async function fetchToken() {
|
|
627
|
-
const params = new URLSearchParams({ grant_type: "client_credentials" });
|
|
628
|
-
if (config.scopes !== undefined && config.scopes.length > 0) {
|
|
629
|
-
params.set("scope", config.scopes.join(" "));
|
|
630
|
-
}
|
|
631
|
-
for (const [key, value] of Object.entries(config.tokenParams ?? {})) {
|
|
632
|
-
params.set(key, value);
|
|
633
|
-
}
|
|
634
|
-
const headers = {
|
|
635
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
636
|
-
Accept: "application/json",
|
|
637
|
-
};
|
|
638
|
-
if (config.authMethod === "basic") {
|
|
639
|
-
headers["Authorization"] = "Basic " + toBase64(config.clientId + ":" + config.clientSecret);
|
|
640
|
-
}
|
|
641
|
-
else {
|
|
642
|
-
params.set("client_id", config.clientId);
|
|
643
|
-
params.set("client_secret", config.clientSecret);
|
|
644
|
-
}
|
|
645
|
-
const response = await fetchImpl(config.tokenUrl, { method: "POST", headers, body: params.toString() });
|
|
646
|
-
const body = (await response.json().catch(() => null));
|
|
647
|
-
if (!response.ok || typeof body?.access_token !== "string") {
|
|
648
|
-
throw new TransportError("OAuth token request failed (HTTP " + response.status + (body?.error ? ": " + body.error : "") + ")", body);
|
|
649
|
-
}
|
|
650
|
-
token = body.access_token;
|
|
651
|
-
expiresAt = body.expires_in !== undefined
|
|
652
|
-
? Date.now() + body.expires_in * 1000 - 60_000
|
|
653
|
-
: Number.MAX_SAFE_INTEGER;
|
|
654
|
-
return token;
|
|
655
|
-
}
|
|
656
|
-
return async () => {
|
|
657
|
-
if (token !== undefined && Date.now() < expiresAt)
|
|
658
|
-
return "Bearer " + token;
|
|
659
|
-
inflight ??= fetchToken().finally(() => { inflight = undefined; });
|
|
660
|
-
return "Bearer " + (await inflight);
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
561
|
/**
|
|
664
562
|
* Bracket-style deep encoding shared by query strings and form bodies:
|
|
665
563
|
* { created: { gte: 5 } } -> created[gte]=5, { items: [{ id: "x" }] } ->
|
|
@@ -686,37 +584,28 @@ function serializeBody(req) {
|
|
|
686
584
|
switch (req.bodyKind ?? "json") {
|
|
687
585
|
case "json":
|
|
688
586
|
return { body: JSON.stringify(req.body), contentType: "application/json" };
|
|
689
|
-
case "form": {
|
|
690
|
-
const params = new URLSearchParams();
|
|
691
|
-
for (const [k, v] of Object.entries(req.body)) {
|
|
692
|
-
appendDeep(params, k, v);
|
|
693
|
-
}
|
|
694
|
-
// URLSearchParams sets its own content type with the charset suffix.
|
|
695
|
-
return { body: params };
|
|
696
|
-
}
|
|
697
|
-
case "multipart": {
|
|
698
|
-
const form = new FormData();
|
|
699
|
-
for (const [k, v] of Object.entries(req.body)) {
|
|
700
|
-
if (v === undefined || v === null)
|
|
701
|
-
continue;
|
|
702
|
-
form.append(k, v instanceof Blob ? v : typeof v === "object" ? JSON.stringify(v) : String(v));
|
|
703
|
-
}
|
|
704
|
-
// Let fetch set the boundary header.
|
|
705
|
-
return { body: form };
|
|
706
|
-
}
|
|
707
|
-
case "text":
|
|
708
|
-
return { body: String(req.body), contentType: "text/plain" };
|
|
709
|
-
case "binary":
|
|
710
|
-
return { body: req.body };
|
|
711
587
|
}
|
|
588
|
+
throw new Error("Unsupported request body kind: " + String(req.bodyKind));
|
|
712
589
|
}
|
|
713
590
|
async function parseBody(response, method) {
|
|
714
591
|
if (method === "HEAD" || response.status === 204 || response.status === 205)
|
|
715
592
|
return undefined;
|
|
716
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
593
|
+
const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
|
|
594
|
+
const mediaType = contentType.split(";", 1)[0].trim();
|
|
595
|
+
const slash = mediaType.indexOf("/");
|
|
596
|
+
const subtype = slash === -1 ? "" : mediaType.slice(slash + 1);
|
|
717
597
|
try {
|
|
718
|
-
if (
|
|
719
|
-
|
|
598
|
+
if (subtype === "json" || subtype.endsWith("+json")) {
|
|
599
|
+
const body = await response.text();
|
|
600
|
+
if (body.length === 0)
|
|
601
|
+
return undefined;
|
|
602
|
+
try {
|
|
603
|
+
return JSON.parse(body);
|
|
604
|
+
}
|
|
605
|
+
catch (cause) {
|
|
606
|
+
throw new ResponseParseError(body, meta(response, body), cause);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
720
609
|
if (contentType.startsWith("text/"))
|
|
721
610
|
return await response.text();
|
|
722
611
|
if (response.body === null)
|
|
@@ -724,22 +613,55 @@ async function parseBody(response, method) {
|
|
|
724
613
|
return await response.blob();
|
|
725
614
|
}
|
|
726
615
|
catch (cause) {
|
|
727
|
-
|
|
728
|
-
// empty body — surface it instead of faking success.
|
|
729
|
-
if (cause instanceof Error && (cause.name === "AbortError" || cause.name === "TimeoutError"))
|
|
616
|
+
if (cause instanceof ResponseParseError)
|
|
730
617
|
throw cause;
|
|
731
|
-
|
|
618
|
+
// A timed-out, aborted, or prematurely terminated body is a transport
|
|
619
|
+
// failure, not an empty body — surface it instead of faking success.
|
|
620
|
+
throw cause;
|
|
732
621
|
}
|
|
733
622
|
}
|
|
734
|
-
function
|
|
623
|
+
function requestIdFromBody(body) {
|
|
624
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
625
|
+
return undefined;
|
|
626
|
+
const value = body.request_id ?? body.requestId;
|
|
627
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
628
|
+
}
|
|
629
|
+
function meta(response, body) {
|
|
735
630
|
return {
|
|
736
631
|
status: response.status,
|
|
737
632
|
headers: response.headers,
|
|
738
|
-
|
|
633
|
+
...(body !== undefined ? { rawBody: body } : {}),
|
|
634
|
+
requestId: requestIdFromBody(body) ??
|
|
739
635
|
response.headers.get("request-id") ??
|
|
636
|
+
response.headers.get("x-request-id") ??
|
|
740
637
|
undefined,
|
|
741
638
|
};
|
|
742
639
|
}
|
|
640
|
+
/** AbortSignal.any reached Node in 18.17. Keep the package's Node 18 floor
|
|
641
|
+
* honest for earlier 18.x releases and for web runtimes without it. */
|
|
642
|
+
function composeSignals(signals) {
|
|
643
|
+
const nativeAny = AbortSignal.any;
|
|
644
|
+
if (nativeAny)
|
|
645
|
+
return nativeAny.call(AbortSignal, signals);
|
|
646
|
+
const controller = new AbortController();
|
|
647
|
+
const listeners = new Map();
|
|
648
|
+
const abortFrom = (signal) => {
|
|
649
|
+
for (const [item, listener] of listeners)
|
|
650
|
+
item.removeEventListener("abort", listener);
|
|
651
|
+
listeners.clear();
|
|
652
|
+
controller.abort(signal.reason);
|
|
653
|
+
};
|
|
654
|
+
for (const signal of signals) {
|
|
655
|
+
if (signal.aborted) {
|
|
656
|
+
abortFrom(signal);
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
const listener = () => abortFrom(signal);
|
|
660
|
+
listeners.set(signal, listener);
|
|
661
|
+
signal.addEventListener("abort", listener, { once: true });
|
|
662
|
+
}
|
|
663
|
+
return controller.signal;
|
|
664
|
+
}
|
|
743
665
|
function retryAfterMs(response) {
|
|
744
666
|
const header = response.headers.get("retry-after");
|
|
745
667
|
if (!header)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/core/pagination.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,QAAQ,EACR,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AAEnB,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,QAAQ,GAAG,kBAAkB,GAAG,MAAM,GAAG,QAAQ,CAAC;IACzD,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,CACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAE1C,qBAAa,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;gBAG9B,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,EAC7B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,YAAY;IASxB,IAAI,KAAK,IAAI,IAAI,EAAE,CAGlB;IAED,WAAW,IAAI,OAAO;IAItB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/core/pagination.ts"],"names":[],"mappings":"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"}
|
package/dist/core/pagination.js
CHANGED
|
@@ -37,30 +37,12 @@ export class Page {
|
|
|
37
37
|
const next = getPath(this.body, config.nextCursorField);
|
|
38
38
|
if (next === undefined || next === null || next === "")
|
|
39
39
|
return null;
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
case "cursorFromLastId": {
|
|
43
|
-
if (items.length === 0)
|
|
44
|
-
return null;
|
|
45
|
-
const last = items[items.length - 1];
|
|
46
|
-
const id = last?.[config.idField ?? "id"];
|
|
47
|
-
if (id === undefined || id === null)
|
|
48
|
-
return null;
|
|
49
|
-
return { ...params, [config.cursorParam]: id };
|
|
50
|
-
}
|
|
51
|
-
case "page": {
|
|
52
|
-
if (!this.looksLikeMore(items))
|
|
40
|
+
if (next === params[config.cursorParam])
|
|
53
41
|
return null;
|
|
54
|
-
|
|
55
|
-
return { ...params, [config.pageParam]: current + 1 };
|
|
56
|
-
}
|
|
57
|
-
case "offset": {
|
|
58
|
-
if (!this.looksLikeMore(items))
|
|
59
|
-
return null;
|
|
60
|
-
const current = Number(params[config.offsetParam] ?? 0);
|
|
61
|
-
return { ...params, [config.offsetParam]: current + items.length };
|
|
42
|
+
return { ...params, [config.cursorParam]: next };
|
|
62
43
|
}
|
|
63
44
|
}
|
|
45
|
+
return null;
|
|
64
46
|
}
|
|
65
47
|
/** Fetch the next page, or null when this is the last one. Throws the typed error on failure. */
|
|
66
48
|
async getNextPage() {
|
|
@@ -116,17 +98,9 @@ export class PagePromise {
|
|
|
116
98
|
}
|
|
117
99
|
}
|
|
118
100
|
export function paginate(core, req, config) {
|
|
119
|
-
const isGraphql =
|
|
101
|
+
const isGraphql = false;
|
|
120
102
|
const fetchPage = async (params) => {
|
|
121
|
-
|
|
122
|
-
? {
|
|
123
|
-
...req,
|
|
124
|
-
body: {
|
|
125
|
-
...req.body,
|
|
126
|
-
variables: { ...(req.body?.variables ?? {}), ...params },
|
|
127
|
-
},
|
|
128
|
-
}
|
|
129
|
-
: { ...req, query: params };
|
|
103
|
+
let nextReq = { ...req, query: params };
|
|
130
104
|
const result = await core.request(nextReq);
|
|
131
105
|
if (!result.ok)
|
|
132
106
|
return result;
|
|
@@ -134,9 +108,7 @@ export function paginate(core, req, config) {
|
|
|
134
108
|
return { ok: true, data: page, response: result.response };
|
|
135
109
|
};
|
|
136
110
|
const initial = {};
|
|
137
|
-
|
|
138
|
-
? (req.body?.variables ?? {})
|
|
139
|
-
: (req.query ?? {});
|
|
111
|
+
let seed = req.query ?? {};
|
|
140
112
|
for (const [k, v] of Object.entries(seed)) {
|
|
141
113
|
if (v !== undefined)
|
|
142
114
|
initial[k] = v;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FileCredentialStore, type CredentialCodec } from "./oauth-session.js";
|
|
2
|
+
export interface NativeCommandResult {
|
|
3
|
+
status: number | null;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
error?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export type NativeCommand = (executable: string, args: string[], input?: string) => NativeCommandResult;
|
|
9
|
+
export interface CredentialKeyStore {
|
|
10
|
+
readonly name: string;
|
|
11
|
+
read(): Buffer | null;
|
|
12
|
+
write(key: Buffer): void;
|
|
13
|
+
/** Used only when explicitly removing this wrapping key, not ordinary logout. */
|
|
14
|
+
remove(): void;
|
|
15
|
+
}
|
|
16
|
+
/** A small wrapping key avoids OS item-size limits; session files are encrypted
|
|
17
|
+
* separately so refresh rotation can retain the atomic file/lock transaction. */
|
|
18
|
+
export declare function nativeCredentialKeyStore(path: string, platform?: NodeJS.Platform, run?: NativeCommand, environmentName?: string): CredentialKeyStore;
|
|
19
|
+
export declare function encryptedCredentialCodec(path: string, keyStore: CredentialKeyStore): CredentialCodec;
|
|
20
|
+
/** OS protection is the default. Plaintext storage is an explicit, separate
|
|
21
|
+
* store for environments where the owner accepts that tradeoff. Never migrate
|
|
22
|
+
* or fall back silently when an OS service is unavailable. */
|
|
23
|
+
export declare function createCredentialStore(directory: string, mode?: string, environmentName?: string): FileCredentialStore;
|
|
24
|
+
//# sourceMappingURL=credential-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-storage.d.ts","sourceRoot":"","sources":["../src/credential-storage.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAA0B,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEvG,MAAM,WAAW,mBAAmB;IAAG,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE;AAC/G,MAAM,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,mBAAmB,CAAC;AACxG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,IAAI,IAAI,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iFAAiF;IACjF,MAAM,IAAI,IAAI,CAAC;CAChB;AAkBD;iFACiF;AACjF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAAE,GAAG,GAAE,aAA6B,EAAE,eAAe,SAAqB,GAAG,kBAAkB,CAsGjM;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,eAAe,CAkCpG;AAED;;8DAE8D;AAC9D,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,MAAa,EAAE,eAAe,SAAqB,GAAG,mBAAmB,CAKvI"}
|