@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.
Files changed (121) hide show
  1. package/AGENTS.md +31 -0
  2. package/README.md +26 -9
  3. package/api.json +6861 -3083
  4. package/api.md +508 -277
  5. package/dist/api-identity.d.ts +40 -0
  6. package/dist/api-identity.d.ts.map +1 -0
  7. package/dist/api-identity.js +128 -0
  8. package/dist/auth-profiles.d.ts +30 -0
  9. package/dist/auth-profiles.d.ts.map +1 -0
  10. package/dist/auth-profiles.js +138 -0
  11. package/dist/cli-agent.d.ts +9 -1
  12. package/dist/cli-agent.d.ts.map +1 -1
  13. package/dist/cli-agent.js +26 -9
  14. package/dist/cli.js +600 -582
  15. package/dist/console-login-check.d.ts +21 -0
  16. package/dist/console-login-check.d.ts.map +1 -0
  17. package/dist/console-login-check.js +107 -0
  18. package/dist/console-login-contract.d.ts +45 -0
  19. package/dist/console-login-contract.d.ts.map +1 -0
  20. package/dist/console-login-contract.js +40 -0
  21. package/dist/core/http.d.ts +21 -92
  22. package/dist/core/http.d.ts.map +1 -1
  23. package/dist/core/http.js +143 -221
  24. package/dist/core/pagination.d.ts.map +1 -1
  25. package/dist/core/pagination.js +6 -34
  26. package/dist/credential-storage.d.ts +24 -0
  27. package/dist/credential-storage.d.ts.map +1 -0
  28. package/dist/credential-storage.js +207 -0
  29. package/dist/dates.d.ts +0 -2
  30. package/dist/dates.d.ts.map +1 -1
  31. package/dist/dates.js +0 -1
  32. package/dist/docs.d.ts +36 -0
  33. package/dist/docs.d.ts.map +1 -0
  34. package/dist/docs.js +258 -0
  35. package/dist/errors.d.ts +42 -34
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/errors.js +30 -20
  38. package/dist/index.d.ts +27 -12
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +40 -14
  41. package/dist/named-credentials.d.ts +21 -0
  42. package/dist/named-credentials.d.ts.map +1 -0
  43. package/dist/named-credentials.js +86 -0
  44. package/dist/oauth-login.d.ts +39 -0
  45. package/dist/oauth-login.d.ts.map +1 -0
  46. package/dist/oauth-login.js +171 -0
  47. package/dist/oauth-request.d.ts +21 -0
  48. package/dist/oauth-request.d.ts.map +1 -0
  49. package/dist/oauth-request.js +119 -0
  50. package/dist/oauth-session.d.ts +106 -0
  51. package/dist/oauth-session.d.ts.map +1 -0
  52. package/dist/oauth-session.js +244 -0
  53. package/dist/ops.d.ts +18 -0
  54. package/dist/ops.d.ts.map +1 -1
  55. package/dist/ops.js +31 -17
  56. package/dist/polling-login.d.ts +57 -0
  57. package/dist/polling-login.d.ts.map +1 -0
  58. package/dist/polling-login.js +204 -0
  59. package/dist/resources/account.d.ts +4 -4
  60. package/dist/resources/account.d.ts.map +1 -1
  61. package/dist/resources/account.js +1 -0
  62. package/dist/resources/api-keys.d.ts +13 -8
  63. package/dist/resources/api-keys.d.ts.map +1 -1
  64. package/dist/resources/api-keys.js +5 -1
  65. package/dist/resources/definition-revisions.d.ts +58 -0
  66. package/dist/resources/definition-revisions.d.ts.map +1 -0
  67. package/dist/resources/definition-revisions.js +114 -0
  68. package/dist/resources/definitions.d.ts +35 -0
  69. package/dist/resources/definitions.d.ts.map +1 -0
  70. package/dist/resources/definitions.js +60 -0
  71. package/dist/resources/generate.d.ts +18 -7
  72. package/dist/resources/generate.d.ts.map +1 -1
  73. package/dist/resources/generate.js +13 -5
  74. package/dist/resources/generations.d.ts +6 -6
  75. package/dist/resources/generations.d.ts.map +1 -1
  76. package/dist/resources/generations.js +3 -1
  77. package/dist/resources/projects.d.ts +111 -35
  78. package/dist/resources/projects.d.ts.map +1 -1
  79. package/dist/resources/projects.js +125 -15
  80. package/dist/resources/targets.d.ts +97 -0
  81. package/dist/resources/targets.d.ts.map +1 -0
  82. package/dist/resources/targets.js +197 -0
  83. package/dist/schemas.d.ts.map +1 -1
  84. package/dist/schemas.js +135 -62
  85. package/dist/types.d.ts +2072 -267
  86. package/dist/types.d.ts.map +1 -1
  87. package/dist/types.js +20 -3
  88. package/package.json +2 -1
  89. package/src/api-identity.ts +98 -0
  90. package/src/auth-profiles.ts +114 -0
  91. package/src/cli-agent.ts +28 -11
  92. package/src/cli.ts +528 -560
  93. package/src/console-login-check.ts +88 -0
  94. package/src/console-login-contract.ts +65 -0
  95. package/src/core/http.ts +156 -305
  96. package/src/core/pagination.ts +6 -30
  97. package/src/credential-storage.ts +183 -0
  98. package/src/dates.ts +0 -1
  99. package/src/docs.ts +239 -0
  100. package/src/errors.ts +52 -41
  101. package/src/index.ts +49 -14
  102. package/src/named-credentials.ts +74 -0
  103. package/src/oauth-login.ts +184 -0
  104. package/src/oauth-request.ts +90 -0
  105. package/src/oauth-session.ts +258 -0
  106. package/src/ops.ts +56 -17
  107. package/src/polling-login.ts +165 -0
  108. package/src/resources/account.ts +6 -3
  109. package/src/resources/api-keys.ts +27 -7
  110. package/src/resources/definition-revisions.ts +207 -0
  111. package/src/resources/definitions.ts +122 -0
  112. package/src/resources/generate.ts +29 -6
  113. package/src/resources/generations.ts +9 -4
  114. package/src/resources/projects.ts +274 -41
  115. package/src/resources/targets.ts +378 -0
  116. package/src/schemas.ts +135 -62
  117. package/src/types.ts +2273 -322
  118. package/dist/resources/spec-revisions.d.ts +0 -47
  119. package/dist/resources/spec-revisions.d.ts.map +0 -1
  120. package/dist/resources/spec-revisions.js +0 -90
  121. package/src/resources/spec-revisions.ts +0 -150
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE5I;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBAChD,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBAClD,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBAC/C,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBACrD,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBACzD,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBACjD,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;gBACpD,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBACrD,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBAC9C,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBACpD,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;gBAC9C,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;CAGrD"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,KAAK,EAAc,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7D,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE3I;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBACpD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBACtD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBACnD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBAClD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBACzD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBAC7D,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBACrD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;gBACxD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBACzD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBACxD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBAClD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;gBACpD,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY;CAGzD"}
package/dist/errors.js CHANGED
@@ -1,14 +1,14 @@
1
1
  // typeship — typed error classes.
2
2
  // Generated by typeship — https://typeship.dev — do not edit by hand.
3
3
  import { ApiError } from "./core/http.js";
4
- export { ApiError, GraphQLRequestError, TransportError, UnexpectedApiError, ValidationError, unwrap } from "./core/http.js";
4
+ export { ApiError, ResponseParseError, TransportError, UnexpectedApiError, ValidationError, unwrap } from "./core/http.js";
5
5
  /**
6
- * The request body, specification source, output selection, or package name is invalid.
6
+ * The request body, Definition source, target selection, or package name is invalid.
7
7
  * Raised for HTTP 400 responses.
8
8
  */
9
9
  export class BadRequestError extends ApiError {
10
10
  constructor(body, response) {
11
- super("The request body, specification source, output selection, or package name is invalid.", 400, body, response);
11
+ super("The request body, Definition source, target selection, or package name is invalid.", 400, body, response);
12
12
  }
13
13
  }
14
14
  /**
@@ -29,6 +29,15 @@ export class ForbiddenError extends ApiError {
29
29
  super("The credentials are valid but cannot act on the requested organization.", 403, body, response);
30
30
  }
31
31
  }
32
+ /**
33
+ * The key identifies changed intent.
34
+ * Raised for HTTP 409 responses.
35
+ */
36
+ export class ConflictError extends ApiError {
37
+ constructor(body, response) {
38
+ super("The key identifies changed intent.", 409, body, response);
39
+ }
40
+ }
32
41
  /**
33
42
  * Spec exceeds the 10MB limit.
34
43
  * Raised for HTTP 413 responses.
@@ -39,21 +48,22 @@ export class PayloadTooLargeError extends ApiError {
39
48
  }
40
49
  }
41
50
  /**
42
- * The spec could not be understood.
51
+ * The Definition could not be resolved or understood.
43
52
  * Raised for HTTP 422 responses.
44
53
  */
45
54
  export class UnprocessableEntityError extends ApiError {
46
55
  constructor(body, response) {
47
- super("The spec could not be understood.", 422, body, response);
56
+ super("The Definition could not be resolved or understood.", 422, body, response);
48
57
  }
49
58
  }
50
59
  /**
51
- * Too many requests. Wait for Retry-After before retrying.
60
+ * Too many requests, or an identical write is still in progress. Wait for Retry-After before
61
+ * retrying.
52
62
  * Raised for HTTP 429 responses.
53
63
  */
54
64
  export class RateLimitedError extends ApiError {
55
65
  constructor(body, response) {
56
- super("Too many requests. Wait for Retry-After before retrying.", 429, body, response);
66
+ super("Too many requests, or an identical write is still in progress. Wait for Retry-After before retrying.", 429, body, response);
57
67
  }
58
68
  }
59
69
  /**
@@ -66,30 +76,21 @@ export class ApiResponseError extends ApiError {
66
76
  }
67
77
  }
68
78
  /**
69
- * The plan does not include another project or the requested output configuration.
79
+ * The plan does not include another project or the requested target configuration.
70
80
  * Raised for HTTP 402 responses.
71
81
  */
72
82
  export class PaymentRequiredError extends ApiError {
73
83
  constructor(body, response) {
74
- super("The plan does not include another project or the requested output configuration.", 402, body, response);
84
+ super("The plan does not include another project or the requested target configuration.", 402, body, response);
75
85
  }
76
86
  }
77
87
  /**
78
- * The Idempotency-Key was already used with different request parameters.
79
- * Raised for HTTP 409 responses.
80
- */
81
- export class ConflictError extends ApiError {
82
- constructor(body, response) {
83
- super("The Idempotency-Key was already used with different request parameters.", 409, body, response);
84
- }
85
- }
86
- /**
87
- * The original idempotent request is temporarily unavailable.
88
+ * Project setup failed unexpectedly; the key reservation is released.
88
89
  * Raised for HTTP 500 responses.
89
90
  */
90
91
  export class InternalServerError extends ApiError {
91
92
  constructor(body, response) {
92
- super("The original idempotent request is temporarily unavailable.", 500, body, response);
93
+ super("Project setup failed unexpectedly; the key reservation is released.", 500, body, response);
93
94
  }
94
95
  }
95
96
  /**
@@ -101,3 +102,12 @@ export class NotFoundError extends ApiError {
101
102
  super("No such resource in this account.", 404, body, response);
102
103
  }
103
104
  }
105
+ /**
106
+ * The Project was saved, but an obsolete release pull request could not be retired.
107
+ * Raised for HTTP 502 responses.
108
+ */
109
+ export class BadGatewayError extends ApiError {
110
+ constructor(body, response) {
111
+ super("The Project was saved, but an obsolete release pull request could not be retired.", 502, body, response);
112
+ }
113
+ }
package/dist/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
- import { type DebugEvent, type RequestContext } from "./core/http.js";
1
+ import { type AuthValue, type DebugEvent, type RequestContext } from "./core/http.js";
2
2
  import { GenerateResource } from "./resources/generate.js";
3
3
  import { ProjectsResource } from "./resources/projects.js";
4
+ import { DefinitionsResource } from "./resources/definitions.js";
5
+ import { TargetsResource } from "./resources/targets.js";
4
6
  import { GenerationsResource } from "./resources/generations.js";
5
- import { SpecRevisionsResource } from "./resources/spec-revisions.js";
7
+ import { DefinitionRevisionsResource } from "./resources/definition-revisions.js";
6
8
  import { AccountResource } from "./resources/account.js";
7
9
  import { ApiKeysResource } from "./resources/api-keys.js";
8
10
  /** This package's version, also sent as the `User-Agent`. */
9
- export declare const VERSION = "0.6.0";
11
+ export declare const VERSION = "0.9.1";
10
12
  export interface ClientOptions {
11
13
  /** Override the server URL. Default: `https://typeship.dev/api/v1` */
12
14
  baseUrl?: string;
@@ -15,6 +17,13 @@ export interface ClientOptions {
15
17
  * before every attempt.
16
18
  */
17
19
  bearerToken?: string | (() => string | Promise<string>);
20
+ /**
21
+ * Credentials keyed by the Definition's security scheme names. Only one complete alternative is
22
+ * sent for each operation. Named values take precedence over convenience options.
23
+ */
24
+ credentials?: {
25
+ "apiKey"?: AuthValue;
26
+ };
18
27
  /** Per-attempt timeout in milliseconds. Default: 30000. */
19
28
  timeoutMs?: number;
20
29
  /** Retries after the first attempt (retryable failures only). Default: 2. */
@@ -60,21 +69,25 @@ export interface ClientOptions {
60
69
  };
61
70
  }
62
71
  /**
63
- * typeship — v0.6.0
72
+ * typeship — v0.9.1
64
73
  *
65
- * Generate production SDKs, CLIs, and MCP servers from an OpenAPI or
66
- * GraphQL spec, and keep every selected output current.
74
+ * Resolve an OpenAPI or GraphQL Definition, diagnose it, and keep every
75
+ * selected SDK, CLI, and MCP Target current.
67
76
  *
68
- * Every operation but one requires an API key, created in the console and
69
- * sent as `Authorization: Bearer ak_...`. A browser session is not a
70
- * credential for this API. The exception is POST /generate, which works
71
- * anonymously with the free plan's limits.
77
+ * Every operation but one requires a bearer credential: an organization
78
+ * API key from the console, or an OAuth access token carrying the operation's
79
+ * read, generate, or write capability and the organization selected during
80
+ * consent. OAuth grants cannot switch organizations after consent. A browser
81
+ * session is not a credential for this API. The exception is POST /generate,
82
+ * which works anonymously with the free plan's limits.
72
83
  */
73
84
  export declare class TypeshipClient {
74
85
  readonly generate: GenerateResource;
75
86
  readonly projects: ProjectsResource;
87
+ readonly definitions: DefinitionsResource;
88
+ readonly targets: TargetsResource;
76
89
  readonly generations: GenerationsResource;
77
- readonly specRevisions: SpecRevisionsResource;
90
+ readonly definitionRevisions: DefinitionRevisionsResource;
78
91
  readonly account: AccountResource;
79
92
  readonly apiKeys: ApiKeysResource;
80
93
  constructor(options?: ClientOptions);
@@ -85,8 +98,10 @@ export { formatDebugEvent, type ApiResult, type AuthValue, type DebugEvent, type
85
98
  export { Page, PagePromise } from "./core/pagination.js";
86
99
  export * from "./resources/generate.js";
87
100
  export * from "./resources/projects.js";
101
+ export * from "./resources/definitions.js";
102
+ export * from "./resources/targets.js";
88
103
  export * from "./resources/generations.js";
89
- export * from "./resources/spec-revisions.js";
104
+ export * from "./resources/definition-revisions.js";
90
105
  export * from "./resources/account.js";
91
106
  export * from "./resources/api-keys.js";
92
107
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -4,29 +4,35 @@ import { HttpCore, formatDebugEvent, bearerAuth, } from "./core/http.js";
4
4
  import { DEFS, SCHEMAS } from "./schemas.js";
5
5
  import { GenerateResource } from "./resources/generate.js";
6
6
  import { ProjectsResource } from "./resources/projects.js";
7
+ import { DefinitionsResource } from "./resources/definitions.js";
8
+ import { TargetsResource } from "./resources/targets.js";
7
9
  import { GenerationsResource } from "./resources/generations.js";
8
- import { SpecRevisionsResource } from "./resources/spec-revisions.js";
10
+ import { DefinitionRevisionsResource } from "./resources/definition-revisions.js";
9
11
  import { AccountResource } from "./resources/account.js";
10
12
  import { ApiKeysResource } from "./resources/api-keys.js";
11
13
  /** This package's version, also sent as the `User-Agent`. */
12
- export const VERSION = "0.6.0";
13
- const USER_AGENT = "@typeship-ax/cli/0.6.0 (typeship)";
14
+ export const VERSION = "0.9.1";
15
+ const USER_AGENT = "@typeship-ax/cli/0.9.1 (typeship)";
14
16
  /**
15
- * typeship — v0.6.0
17
+ * typeship — v0.9.1
16
18
  *
17
- * Generate production SDKs, CLIs, and MCP servers from an OpenAPI or
18
- * GraphQL spec, and keep every selected output current.
19
+ * Resolve an OpenAPI or GraphQL Definition, diagnose it, and keep every
20
+ * selected SDK, CLI, and MCP Target current.
19
21
  *
20
- * Every operation but one requires an API key, created in the console and
21
- * sent as `Authorization: Bearer ak_...`. A browser session is not a
22
- * credential for this API. The exception is POST /generate, which works
23
- * anonymously with the free plan's limits.
22
+ * Every operation but one requires a bearer credential: an organization
23
+ * API key from the console, or an OAuth access token carrying the operation's
24
+ * read, generate, or write capability and the organization selected during
25
+ * consent. OAuth grants cannot switch organizations after consent. A browser
26
+ * session is not a credential for this API. The exception is POST /generate,
27
+ * which works anonymously with the free plan's limits.
24
28
  */
25
29
  export class TypeshipClient {
26
30
  generate;
27
31
  projects;
32
+ definitions;
33
+ targets;
28
34
  generations;
29
- specRevisions;
35
+ definitionRevisions;
30
36
  account;
31
37
  apiKeys;
32
38
  constructor(options = {}) {
@@ -41,8 +47,23 @@ export class TypeshipClient {
41
47
  ? (event) => console.error(formatDebugEvent("typeship", event))
42
48
  : undefined;
43
49
  const query = {};
50
+ const authHeaders = {};
51
+ const authQuery = {};
52
+ let bearerCredential;
44
53
  if (options.bearerToken !== undefined)
45
- headers["Authorization"] = bearerAuth(options.bearerToken);
54
+ bearerCredential = bearerAuth(options.bearerToken);
55
+ const allowedCredentials = new Set(["apiKey"]);
56
+ for (const name of Object.keys(options.credentials ?? {})) {
57
+ if (!allowedCredentials.has(name))
58
+ throw new Error("Unknown security scheme: " + name);
59
+ }
60
+ const credentials = Object.create(null);
61
+ {
62
+ const named = options.credentials && Object.hasOwn(options.credentials, "apiKey") ? options.credentials["apiKey"] : undefined;
63
+ const value = named !== undefined ? bearerAuth(named) : bearerCredential;
64
+ if (value !== undefined)
65
+ credentials["apiKey"] = { headers: { ["Authorization"]: value } };
66
+ }
46
67
  const v = options.validate;
47
68
  const validate = v
48
69
  ? {
@@ -55,6 +76,7 @@ export class TypeshipClient {
55
76
  baseUrl: options.baseUrl ?? "https://typeship.dev/api/v1",
56
77
  headers,
57
78
  query,
79
+ credentials,
58
80
  fetch: options.fetch ?? fetch,
59
81
  timeoutMs: options.timeoutMs ?? 30_000,
60
82
  maxRetries: options.maxRetries ?? 2,
@@ -68,8 +90,10 @@ export class TypeshipClient {
68
90
  });
69
91
  this.generate = new GenerateResource(core);
70
92
  this.projects = new ProjectsResource(core);
93
+ this.definitions = new DefinitionsResource(core);
94
+ this.targets = new TargetsResource(core);
71
95
  this.generations = new GenerationsResource(core);
72
- this.specRevisions = new SpecRevisionsResource(core);
96
+ this.definitionRevisions = new DefinitionRevisionsResource(core);
73
97
  this.account = new AccountResource(core);
74
98
  this.apiKeys = new ApiKeysResource(core);
75
99
  }
@@ -80,7 +104,9 @@ export { formatDebugEvent, } from "./core/http.js";
80
104
  export { Page, PagePromise } from "./core/pagination.js";
81
105
  export * from "./resources/generate.js";
82
106
  export * from "./resources/projects.js";
107
+ export * from "./resources/definitions.js";
108
+ export * from "./resources/targets.js";
83
109
  export * from "./resources/generations.js";
84
- export * from "./resources/spec-revisions.js";
110
+ export * from "./resources/definition-revisions.js";
85
111
  export * from "./resources/account.js";
86
112
  export * from "./resources/api-keys.js";
@@ -0,0 +1,21 @@
1
+ /** Runtime credentials keyed by the API's exact security-scheme names. */
2
+ export type NamedCredential = string | {
3
+ username: string;
4
+ password: string;
5
+ };
6
+ export type NamedCredentials = Record<string, NamedCredential>;
7
+ export type CredentialSchemes = Record<string, {
8
+ kind: string;
9
+ options: string[];
10
+ }>;
11
+ export declare function parseNamedCredentials(value: unknown, schemes: CredentialSchemes): NamedCredentials;
12
+ /** Low-to-high source order. Within a source, exact names beat convenience options. */
13
+ export declare function resolveNamedCredentials(schemes: CredentialSchemes, layers: {
14
+ named?: NamedCredentials;
15
+ options?: Record<string, unknown>;
16
+ }[]): NamedCredentials;
17
+ /** Make convenience and named inputs comparable without expanding combinations. */
18
+ export declare function namedCredentialAvailability(schemes: CredentialSchemes, options: Set<string>, named: NamedCredentials): Set<string>;
19
+ /** Bound file/stdin reads before allocating a full credential document. */
20
+ export declare function readNamedCredentialsFile(input: string, schemes: CredentialSchemes): NamedCredentials;
21
+ //# sourceMappingURL=named-credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"named-credentials.d.ts","sourceRoot":"","sources":["../src/named-credentials.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAC9E,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAEpF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAqBlG;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAAE,GAAG,gBAAgB,CAa/J;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAKlI;AAED,2EAA2E;AAC3E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAkBpG"}
@@ -0,0 +1,86 @@
1
+ import { openSync, readSync, closeSync } from "node:fs";
2
+ export function parseNamedCredentials(value, schemes) {
3
+ if (typeof value === "string") {
4
+ if (new TextEncoder().encode(value).byteLength > 1_048_576)
5
+ throw new Error("Named credentials exceed the 1 MiB limit.");
6
+ try {
7
+ value = JSON.parse(value);
8
+ }
9
+ catch {
10
+ throw new Error("Named credentials must be a JSON object keyed by security scheme. Credential contents are not included in errors.");
11
+ }
12
+ }
13
+ if (!value || typeof value !== "object" || Array.isArray(value))
14
+ throw new Error("Named credentials must be a JSON object keyed by security scheme.");
15
+ const result = Object.create(null);
16
+ for (const [name, credential] of Object.entries(value)) {
17
+ if (!Object.hasOwn(schemes, name))
18
+ throw new Error("Named credentials contain an unknown or unsupported security scheme. Check the names listed by login --help.");
19
+ if (schemes[name].kind === "basic") {
20
+ if (!credential || typeof credential !== "object" || Array.isArray(credential) || Object.keys(credential).some((key) => key !== "username" && key !== "password") ||
21
+ typeof credential.username !== "string" || typeof credential.password !== "string" || !credential.username || !credential.password || credential.username.includes(":")) {
22
+ throw new Error("A named Basic credential requires a nonempty username and password; the username cannot contain a colon.");
23
+ }
24
+ result[name] = { username: credential.username, password: credential.password };
25
+ }
26
+ else {
27
+ if (typeof credential !== "string" || !credential || /[\x00-\x1f\x7f]/.test(credential) || (schemes[name].kind === "bearer" && credential.includes(" ")))
28
+ throw new Error("A named token or API key must be a nonempty string without control characters; bearer tokens cannot contain spaces.");
29
+ result[name] = credential;
30
+ }
31
+ }
32
+ return result;
33
+ }
34
+ /** Low-to-high source order. Within a source, exact names beat convenience options. */
35
+ export function resolveNamedCredentials(schemes, layers) {
36
+ const resolved = Object.create(null);
37
+ for (const layer of layers) {
38
+ for (const [name, scheme] of Object.entries(schemes)) {
39
+ for (const option of scheme.options) {
40
+ if (option === "clientCredentials")
41
+ continue;
42
+ const value = layer.options?.[option];
43
+ if (value !== undefined)
44
+ resolved[name] = value;
45
+ }
46
+ }
47
+ if (layer.named)
48
+ Object.assign(resolved, parseNamedCredentials(layer.named, schemes));
49
+ }
50
+ return resolved;
51
+ }
52
+ /** Make convenience and named inputs comparable without expanding combinations. */
53
+ export function namedCredentialAvailability(schemes, options, named) {
54
+ for (const [name, scheme] of Object.entries(schemes)) {
55
+ if (Object.hasOwn(named, name) || scheme.options.some((option) => options.has(option)))
56
+ options.add("credentials." + name);
57
+ }
58
+ return options;
59
+ }
60
+ /** Bound file/stdin reads before allocating a full credential document. */
61
+ export function readNamedCredentialsFile(input, schemes) {
62
+ let fd;
63
+ const chunks = [];
64
+ let total = 0;
65
+ try {
66
+ fd = input === "-" ? 0 : openSync(input.slice(1), "r");
67
+ while (true) {
68
+ const buffer = Buffer.alloc(Math.min(32_768, 1_048_577 - total));
69
+ const size = readSync(fd, buffer, 0, buffer.length, null);
70
+ if (!size)
71
+ break;
72
+ total += size;
73
+ if (total > 1_048_576)
74
+ throw new Error("size");
75
+ chunks.push(buffer.subarray(0, size));
76
+ }
77
+ }
78
+ catch {
79
+ throw new Error("Cannot read named credentials. Supply a readable JSON file or stdin input of at most 1 MiB.");
80
+ }
81
+ finally {
82
+ if (fd !== undefined && input !== "-")
83
+ closeSync(fd);
84
+ }
85
+ return parseNamedCredentials(Buffer.concat(chunks).toString("utf8"), schemes);
86
+ }
@@ -0,0 +1,39 @@
1
+ export interface OAuthLoginConfig {
2
+ issuer: string;
3
+ clientId: string;
4
+ discoveryUrl?: string;
5
+ authorizationUrl?: string;
6
+ tokenUrl?: string;
7
+ /** Register http://127.0.0.1/callback; an omitted port uses a random port. */
8
+ redirectUri?: string;
9
+ scopes?: string[];
10
+ audience?: string;
11
+ resource?: string;
12
+ /** Explicit runtime selection. The CLI verifies this ID through the API. */
13
+ organization?: {
14
+ parameter: "organization" | "organization_id";
15
+ id: string;
16
+ };
17
+ }
18
+ export interface OAuthLoginSession {
19
+ accessToken: string;
20
+ refreshToken?: string;
21
+ expiresAt?: number;
22
+ issuer: string;
23
+ clientId: string;
24
+ tokenUrl: string;
25
+ /** The actual native callback used for this exchange, including its port. */
26
+ redirectUri: string;
27
+ revocationUrl?: string;
28
+ scopes?: string[];
29
+ }
30
+ export interface OAuthLoginInteraction {
31
+ /** Open or display the URL. Never send the verifier or returned tokens. */
32
+ authorize(url: string): void | Promise<void>;
33
+ signal?: AbortSignal;
34
+ timeoutMs?: number;
35
+ }
36
+ /** Authorization Code + S256 PKCE for a public native client. The listener
37
+ * binds only a loopback IP, verifies state/issuer, and closes on every path. */
38
+ export declare function oauthBrowserLogin(config: OAuthLoginConfig, interaction: OAuthLoginInteraction): Promise<OAuthLoginSession>;
39
+ //# sourceMappingURL=oauth-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-login.d.ts","sourceRoot":"","sources":["../src/oauth-login.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,YAAY,CAAC,EAAE;QAAE,SAAS,EAAE,cAAc,GAAG,iBAAiB,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9E;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgDD;gFACgF;AAChF,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgGhI"}
@@ -0,0 +1,171 @@
1
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { createServer } from "node:http";
3
+ import { oauthJsonRequest, OAuthResponseError } from "./oauth-request.js";
4
+ function endpoint(value) {
5
+ const url = new URL(value);
6
+ const loopback = ["127.0.0.1", "[::1]", "localhost"].includes(url.hostname);
7
+ if ((url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) || url.username || url.password || url.hash) {
8
+ throw new Error("OAuth endpoints require HTTPS without credentials or fragments; loopback HTTP is allowed for development.");
9
+ }
10
+ return url;
11
+ }
12
+ async function metadata(config, signal) {
13
+ const issuer = endpoint(config.issuer);
14
+ if (issuer.search)
15
+ throw new Error("The OAuth issuer must not contain a query.");
16
+ const path = issuer.pathname.replace(/\/$/, "");
17
+ const urls = config.discoveryUrl ? [config.discoveryUrl] : [
18
+ issuer.origin + "/.well-known/oauth-authorization-server" + path,
19
+ issuer.origin + path + "/.well-known/openid-configuration",
20
+ ];
21
+ for (const url of urls) {
22
+ let response;
23
+ try {
24
+ response = await oauthJsonRequest(endpoint(url), { headers: { Accept: "application/json" }, signal }, 10_000);
25
+ }
26
+ catch (error) {
27
+ if (!signal.aborted && error instanceof OAuthResponseError && error.code === "request_failed")
28
+ continue;
29
+ throw error;
30
+ }
31
+ if (response.status !== 200)
32
+ continue;
33
+ const data = response.data;
34
+ if (data.issuer !== config.issuer)
35
+ throw new Error("OAuth discovery returned a different issuer. Check the issuer and discovery URL.");
36
+ if (Array.isArray(data.code_challenge_methods_supported) && !data.code_challenge_methods_supported.includes("S256")) {
37
+ throw new Error("The provider does not advertise S256 PKCE support.");
38
+ }
39
+ const authorizationUrl = config.authorizationUrl ?? data.authorization_endpoint;
40
+ const tokenUrl = config.tokenUrl ?? data.token_endpoint;
41
+ if (typeof authorizationUrl !== "string" || typeof tokenUrl !== "string")
42
+ throw new Error("Browser login requires authorization and token endpoints.");
43
+ return {
44
+ authorizationUrl: endpoint(authorizationUrl), tokenUrl: endpoint(tokenUrl).href,
45
+ ...(typeof data.revocation_endpoint === "string" ? { revocationUrl: endpoint(data.revocation_endpoint).href } : {}),
46
+ };
47
+ }
48
+ // Some custom providers publish no metadata. Their explicit endpoints are
49
+ // owner configuration, not endpoints accepted from an unverified document.
50
+ if (config.authorizationUrl && config.tokenUrl)
51
+ return {
52
+ authorizationUrl: endpoint(config.authorizationUrl), tokenUrl: endpoint(config.tokenUrl).href,
53
+ };
54
+ throw new Error("Could not discover OAuth endpoints. Check the issuer or provide explicit authorization and token URLs.");
55
+ }
56
+ /** Authorization Code + S256 PKCE for a public native client. The listener
57
+ * binds only a loopback IP, verifies state/issuer, and closes on every path. */
58
+ export async function oauthBrowserLogin(config, interaction) {
59
+ if (!config.clientId.trim())
60
+ throw new Error("Browser login requires a public client ID.");
61
+ if (config.organization && (!["organization", "organization_id"].includes(config.organization.parameter) || typeof config.organization.id !== "string" || !config.organization.id || config.organization.id.length > 512 || /\s|[\u0000-\u001F\u007F]/.test(config.organization.id)))
62
+ throw new Error("Organization selection requires a supported parameter and a nonempty organization ID without whitespace.");
63
+ const signal = AbortSignal.any([interaction.signal ?? new AbortController().signal, AbortSignal.timeout(interaction.timeoutMs ?? 600_000)]);
64
+ signal.throwIfAborted();
65
+ const endpoints = await metadata(config, signal);
66
+ const redirect = new URL(config.redirectUri ?? "http://127.0.0.1/callback");
67
+ if (redirect.protocol !== "http:" || !["127.0.0.1", "[::1]"].includes(redirect.hostname) || redirect.username || redirect.password || redirect.search || redirect.hash) {
68
+ throw new Error("The OAuth redirect must be an HTTP loopback IP URL without credentials, query, or fragment.");
69
+ }
70
+ const verifier = randomBytes(32).toString("base64url");
71
+ const state = randomBytes(32).toString("base64url");
72
+ const challenge = createHash("sha256").update(verifier).digest("base64url");
73
+ let resolveCode;
74
+ let rejectCode;
75
+ const codeResult = new Promise((resolve, reject) => { resolveCode = resolve; rejectCode = reject; });
76
+ // Observe rejection even if opening the browser or starting the server fails.
77
+ void codeResult.catch(() => undefined);
78
+ let accepted = false;
79
+ const server = createServer((request, response) => {
80
+ response.setHeader("Content-Type", "text/plain; charset=utf-8");
81
+ response.setHeader("Cache-Control", "no-store");
82
+ response.setHeader("Referrer-Policy", "no-referrer");
83
+ response.setHeader("Content-Security-Policy", "default-src 'none'");
84
+ let incoming;
85
+ try {
86
+ incoming = new URL(request.url ?? "/", redirect.origin);
87
+ }
88
+ catch {
89
+ response.writeHead(400).end("Invalid callback URL.");
90
+ return;
91
+ }
92
+ if (accepted || request.method !== "GET" || request.headers.host !== redirect.host || incoming.pathname !== redirect.pathname) {
93
+ response.writeHead(404).end("Not found.");
94
+ return;
95
+ }
96
+ const returnedState = Buffer.from(incoming.searchParams.get("state") ?? "");
97
+ const expectedState = Buffer.from(state);
98
+ if (incoming.searchParams.getAll("state").length !== 1 || returnedState.length !== expectedState.length || !timingSafeEqual(returnedState, expectedState)) {
99
+ response.writeHead(400).end("Invalid login state. Return to the terminal and try again.");
100
+ return;
101
+ }
102
+ const returnedIssuer = incoming.searchParams.get("iss");
103
+ if (returnedIssuer !== null && (incoming.searchParams.getAll("iss").length !== 1 || returnedIssuer !== config.issuer)) {
104
+ response.writeHead(400).end("The login issuer did not match.");
105
+ return;
106
+ }
107
+ accepted = true;
108
+ if (incoming.searchParams.has("error")) {
109
+ response.writeHead(400).end("Login was not approved. Return to the terminal.", () => rejectCode(new Error("OAuth authorization was denied or cancelled. Run login again to retry.")));
110
+ return;
111
+ }
112
+ const code = incoming.searchParams.get("code");
113
+ if (!code || incoming.searchParams.getAll("code").length !== 1) {
114
+ response.writeHead(400).end("The provider returned no authorization code.", () => rejectCode(new Error("OAuth callback did not contain one authorization code.")));
115
+ return;
116
+ }
117
+ response.end("Authorization received. Return to the terminal to finish signing in.", () => resolveCode(code));
118
+ });
119
+ server.headersTimeout = 10_000;
120
+ server.requestTimeout = 10_000;
121
+ const aborted = () => rejectCode(new Error("Browser login timed out or was cancelled. Run login again to retry."));
122
+ signal.addEventListener("abort", aborted, { once: true });
123
+ try {
124
+ await new Promise((resolve, reject) => {
125
+ server.once("error", reject);
126
+ server.listen(Number(redirect.port) || 0, redirect.hostname === "[::1]" ? "::1" : "127.0.0.1", () => { server.off("error", reject); resolve(); });
127
+ });
128
+ const address = server.address();
129
+ if (!address || typeof address === "string")
130
+ throw new Error("Could not start the OAuth callback listener.");
131
+ redirect.port = String(address.port);
132
+ signal.throwIfAborted();
133
+ const authorization = new URL(endpoints.authorizationUrl);
134
+ if (config.organization) {
135
+ const other = config.organization.parameter === "organization" ? "organization_id" : "organization";
136
+ if (authorization.searchParams.has(other))
137
+ throw new Error("The authorization URL contains a conflicting organization parameter. Update the provider configuration.");
138
+ authorization.searchParams.set(config.organization.parameter, config.organization.id);
139
+ }
140
+ for (const [key, value] of Object.entries({ response_type: "code", client_id: config.clientId, redirect_uri: redirect.href, state, code_challenge: challenge, code_challenge_method: "S256", ...(config.scopes?.length ? { scope: config.scopes.join(" ") } : {}), ...(config.audience ? { audience: config.audience } : {}), ...(config.resource ? { resource: config.resource } : {}) }))
141
+ authorization.searchParams.set(key, value);
142
+ await interaction.authorize(authorization.href);
143
+ const code = await codeResult;
144
+ signal.throwIfAborted();
145
+ const response = await oauthJsonRequest(endpoints.tokenUrl, {
146
+ method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" }, redirect: "error",
147
+ body: new URLSearchParams({ grant_type: "authorization_code", client_id: config.clientId, redirect_uri: redirect.href, code, code_verifier: verifier, ...(config.audience ? { audience: config.audience } : {}), ...(config.resource ? { resource: config.resource } : {}) }),
148
+ signal,
149
+ });
150
+ const data = response.data;
151
+ if (response.status !== 200 || !data || typeof data.access_token !== "string" || !data.access_token || typeof data.token_type !== "string" || data.token_type.toLowerCase() !== "bearer") {
152
+ throw new Error(`OAuth token exchange failed (HTTP ${response.status}). Check the public client registration and run login again.`);
153
+ }
154
+ if (data.expires_in !== undefined && (typeof data.expires_in !== "number" || !Number.isFinite(data.expires_in) || data.expires_in <= 0))
155
+ throw new Error("OAuth provider returned an invalid token lifetime.");
156
+ return {
157
+ accessToken: data.access_token,
158
+ ...(typeof data.refresh_token === "string" && data.refresh_token ? { refreshToken: data.refresh_token } : {}),
159
+ ...(typeof data.expires_in === "number" ? { expiresAt: Date.now() + data.expires_in * 1000 } : {}),
160
+ issuer: config.issuer, clientId: config.clientId, tokenUrl: endpoints.tokenUrl, redirectUri: redirect.href,
161
+ ...(endpoints.revocationUrl ? { revocationUrl: endpoints.revocationUrl } : {}),
162
+ ...(typeof data.scope === "string" ? { scopes: data.scope.split(/\s+/).filter(Boolean) } : {}),
163
+ };
164
+ }
165
+ finally {
166
+ signal.removeEventListener("abort", aborted);
167
+ server.closeAllConnections();
168
+ if (server.listening)
169
+ await new Promise((resolve) => server.close(() => resolve()));
170
+ }
171
+ }