toolcraft 0.0.5 → 0.0.7

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 (149) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.js +77 -59
  4. package/node_modules/@poe-code/agent-defs/dist/agents/claude-code.d.ts +2 -0
  5. package/node_modules/@poe-code/agent-defs/dist/agents/claude-code.js +15 -0
  6. package/node_modules/@poe-code/agent-defs/dist/agents/claude-desktop.d.ts +2 -0
  7. package/node_modules/@poe-code/agent-defs/dist/agents/claude-desktop.js +13 -0
  8. package/node_modules/@poe-code/agent-defs/dist/agents/codex.d.ts +2 -0
  9. package/node_modules/@poe-code/agent-defs/dist/agents/codex.js +14 -0
  10. package/node_modules/@poe-code/agent-defs/dist/agents/goose.d.ts +2 -0
  11. package/node_modules/@poe-code/agent-defs/dist/agents/goose.js +14 -0
  12. package/node_modules/@poe-code/agent-defs/dist/agents/index.d.ts +7 -0
  13. package/node_modules/@poe-code/agent-defs/dist/agents/index.js +7 -0
  14. package/node_modules/@poe-code/agent-defs/dist/agents/kimi.d.ts +2 -0
  15. package/node_modules/@poe-code/agent-defs/dist/agents/kimi.js +15 -0
  16. package/node_modules/@poe-code/agent-defs/dist/agents/opencode.d.ts +2 -0
  17. package/node_modules/@poe-code/agent-defs/dist/agents/opencode.js +14 -0
  18. package/node_modules/@poe-code/agent-defs/dist/agents/poe-agent.d.ts +2 -0
  19. package/node_modules/@poe-code/agent-defs/dist/agents/poe-agent.js +13 -0
  20. package/node_modules/@poe-code/agent-defs/dist/index.d.ts +5 -0
  21. package/node_modules/@poe-code/agent-defs/dist/index.js +3 -0
  22. package/node_modules/@poe-code/agent-defs/dist/registry.d.ts +3 -0
  23. package/node_modules/@poe-code/agent-defs/dist/registry.js +26 -0
  24. package/node_modules/@poe-code/agent-defs/dist/specifier.d.ts +7 -0
  25. package/node_modules/@poe-code/agent-defs/dist/specifier.js +27 -0
  26. package/node_modules/@poe-code/agent-defs/dist/types.d.ts +16 -0
  27. package/node_modules/@poe-code/agent-defs/dist/types.js +1 -0
  28. package/node_modules/@poe-code/agent-defs/package.json +20 -0
  29. package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.d.ts +5 -0
  30. package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +552 -0
  31. package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.d.ts +17 -0
  32. package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +58 -0
  33. package/node_modules/@poe-code/config-mutations/dist/execution/run-mutations.d.ts +7 -0
  34. package/node_modules/@poe-code/config-mutations/dist/execution/run-mutations.js +46 -0
  35. package/node_modules/@poe-code/config-mutations/dist/formats/index.d.ts +13 -0
  36. package/node_modules/@poe-code/config-mutations/dist/formats/index.js +49 -0
  37. package/node_modules/@poe-code/config-mutations/dist/formats/json.d.ts +31 -0
  38. package/node_modules/@poe-code/config-mutations/dist/formats/json.js +140 -0
  39. package/node_modules/@poe-code/config-mutations/dist/formats/toml.d.ts +2 -0
  40. package/node_modules/@poe-code/config-mutations/dist/formats/toml.js +72 -0
  41. package/node_modules/@poe-code/config-mutations/dist/formats/yaml.d.ts +2 -0
  42. package/node_modules/@poe-code/config-mutations/dist/formats/yaml.js +73 -0
  43. package/node_modules/@poe-code/config-mutations/dist/fs-utils.d.ts +18 -0
  44. package/node_modules/@poe-code/config-mutations/dist/fs-utils.js +45 -0
  45. package/node_modules/@poe-code/config-mutations/dist/index.d.ts +8 -0
  46. package/node_modules/@poe-code/config-mutations/dist/index.js +8 -0
  47. package/node_modules/@poe-code/config-mutations/dist/mutations/config-mutation.d.ts +47 -0
  48. package/node_modules/@poe-code/config-mutations/dist/mutations/config-mutation.js +34 -0
  49. package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.d.ts +52 -0
  50. package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.js +46 -0
  51. package/node_modules/@poe-code/config-mutations/dist/mutations/template-mutation.d.ts +40 -0
  52. package/node_modules/@poe-code/config-mutations/dist/mutations/template-mutation.js +32 -0
  53. package/node_modules/@poe-code/config-mutations/dist/template/render.d.ts +7 -0
  54. package/node_modules/@poe-code/config-mutations/dist/template/render.js +28 -0
  55. package/node_modules/@poe-code/config-mutations/dist/testing/format-utils.d.ts +7 -0
  56. package/node_modules/@poe-code/config-mutations/dist/testing/format-utils.js +21 -0
  57. package/node_modules/@poe-code/config-mutations/dist/testing/index.d.ts +3 -0
  58. package/node_modules/@poe-code/config-mutations/dist/testing/index.js +2 -0
  59. package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.d.ts +25 -0
  60. package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +170 -0
  61. package/node_modules/@poe-code/config-mutations/dist/types.d.ts +156 -0
  62. package/node_modules/@poe-code/config-mutations/dist/types.js +6 -0
  63. package/node_modules/@poe-code/config-mutations/package.json +33 -0
  64. package/node_modules/@poe-code/file-lock/README.md +52 -0
  65. package/node_modules/@poe-code/file-lock/dist/index.d.ts +1 -0
  66. package/node_modules/@poe-code/file-lock/dist/index.js +1 -0
  67. package/node_modules/@poe-code/file-lock/dist/lock.d.ts +27 -0
  68. package/node_modules/@poe-code/file-lock/dist/lock.js +203 -0
  69. package/node_modules/@poe-code/file-lock/package.json +23 -0
  70. package/node_modules/auth-store/README.md +47 -0
  71. package/node_modules/auth-store/dist/create-secret-store.d.ts +2 -0
  72. package/node_modules/auth-store/dist/create-secret-store.js +35 -0
  73. package/node_modules/auth-store/dist/encrypted-file-store.d.ts +39 -0
  74. package/node_modules/auth-store/dist/encrypted-file-store.js +156 -0
  75. package/node_modules/auth-store/dist/index.d.ts +7 -0
  76. package/node_modules/auth-store/dist/index.js +4 -0
  77. package/node_modules/auth-store/dist/keychain-store.d.ts +22 -0
  78. package/node_modules/auth-store/dist/keychain-store.js +111 -0
  79. package/node_modules/auth-store/dist/provider-store.d.ts +10 -0
  80. package/node_modules/auth-store/dist/provider-store.js +28 -0
  81. package/node_modules/auth-store/dist/types.d.ts +20 -0
  82. package/node_modules/auth-store/dist/types.js +1 -0
  83. package/node_modules/auth-store/package.json +25 -0
  84. package/node_modules/mcp-oauth/README.md +31 -0
  85. package/node_modules/mcp-oauth/dist/client/auth-store-session-store.d.ts +14 -0
  86. package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +97 -0
  87. package/node_modules/mcp-oauth/dist/client/authorization-state.d.ts +8 -0
  88. package/node_modules/mcp-oauth/dist/client/authorization-state.js +34 -0
  89. package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.d.ts +3 -0
  90. package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +491 -0
  91. package/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +20 -0
  92. package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +169 -0
  93. package/node_modules/mcp-oauth/dist/client/pkce.d.ts +2 -0
  94. package/node_modules/mcp-oauth/dist/client/pkce.js +7 -0
  95. package/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +40 -0
  96. package/node_modules/mcp-oauth/dist/client/token-endpoint.js +143 -0
  97. package/node_modules/mcp-oauth/dist/client/types.d.ts +113 -0
  98. package/node_modules/mcp-oauth/dist/client/types.js +1 -0
  99. package/node_modules/mcp-oauth/dist/index.d.ts +10 -0
  100. package/node_modules/mcp-oauth/dist/index.js +7 -0
  101. package/node_modules/mcp-oauth/dist/resource-indicator.d.ts +1 -0
  102. package/node_modules/mcp-oauth/dist/resource-indicator.js +11 -0
  103. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.d.ts +27 -0
  104. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +259 -0
  105. package/node_modules/mcp-oauth/dist/types.compile-check.d.ts +1 -0
  106. package/node_modules/mcp-oauth/dist/types.compile-check.js +22 -0
  107. package/node_modules/mcp-oauth/package.json +31 -0
  108. package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +4 -0
  109. package/node_modules/tiny-mcp-client/dist/index.d.ts +2 -0
  110. package/node_modules/tiny-mcp-client/dist/index.js +1 -0
  111. package/node_modules/tiny-mcp-client/dist/internal.d.ts +547 -0
  112. package/node_modules/tiny-mcp-client/dist/internal.js +2404 -0
  113. package/node_modules/tiny-mcp-client/dist/jsonrpc-types.compile-check.d.ts +1 -0
  114. package/node_modules/tiny-mcp-client/dist/jsonrpc-types.compile-check.js +37 -0
  115. package/node_modules/tiny-mcp-client/dist/mcp-lifecycle-types.compile-check.d.ts +1 -0
  116. package/node_modules/tiny-mcp-client/dist/mcp-lifecycle-types.compile-check.js +50 -0
  117. package/node_modules/tiny-mcp-client/dist/mcp-prompt-types.compile-check.d.ts +1 -0
  118. package/node_modules/tiny-mcp-client/dist/mcp-prompt-types.compile-check.js +50 -0
  119. package/node_modules/tiny-mcp-client/dist/mcp-resource-types.compile-check.d.ts +1 -0
  120. package/node_modules/tiny-mcp-client/dist/mcp-resource-types.compile-check.js +51 -0
  121. package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.d.ts +1 -0
  122. package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.js +89 -0
  123. package/node_modules/tiny-mcp-client/dist/mcp-transport-types.compile-check.d.ts +1 -0
  124. package/node_modules/tiny-mcp-client/dist/mcp-transport-types.compile-check.js +56 -0
  125. package/node_modules/tiny-mcp-client/dist/mcp-utility-types.compile-check.d.ts +1 -0
  126. package/node_modules/tiny-mcp-client/dist/mcp-utility-types.compile-check.js +145 -0
  127. package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +24 -0
  128. package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +385 -0
  129. package/node_modules/tiny-mcp-client/package.json +22 -0
  130. package/node_modules/tiny-mcp-client/src/http-oauth.integration.test.ts +823 -0
  131. package/node_modules/tiny-mcp-client/src/http-oauth.test.ts +882 -0
  132. package/node_modules/tiny-mcp-client/src/index.ts +94 -0
  133. package/node_modules/tiny-mcp-client/src/internal.ts +3566 -0
  134. package/node_modules/tiny-mcp-client/src/jsonrpc-types.compile-check.ts +66 -0
  135. package/node_modules/tiny-mcp-client/src/mcp-client-http-transport.integration.test.ts +222 -0
  136. package/node_modules/tiny-mcp-client/src/mcp-client-sdk.test.ts +1294 -0
  137. package/node_modules/tiny-mcp-client/src/mcp-client-tiny-stdio-test-server-tools.test.ts +143 -0
  138. package/node_modules/tiny-mcp-client/src/mcp-lifecycle-types.compile-check.ts +65 -0
  139. package/node_modules/tiny-mcp-client/src/mcp-prompt-types.compile-check.ts +66 -0
  140. package/node_modules/tiny-mcp-client/src/mcp-resource-types.compile-check.ts +70 -0
  141. package/node_modules/tiny-mcp-client/src/mcp-tool-types.compile-check.ts +117 -0
  142. package/node_modules/tiny-mcp-client/src/mcp-transport-types.compile-check.ts +75 -0
  143. package/node_modules/tiny-mcp-client/src/mcp-utility-types.compile-check.ts +181 -0
  144. package/node_modules/tiny-mcp-client/src/mock-servers.test.ts +980 -0
  145. package/node_modules/tiny-mcp-client/src/oauth-discovery.ts +583 -0
  146. package/node_modules/tiny-mcp-client/src/transports.test.ts +8139 -0
  147. package/node_modules/tiny-mcp-client/src/utilities.test.ts +372 -0
  148. package/node_modules/tiny-mcp-client/tsconfig.json +11 -0
  149. package/package.json +24 -11
@@ -0,0 +1,583 @@
1
+ import type {
2
+ OAuthAuthorizationServerMetadata,
3
+ OAuthDiscoveryResult,
4
+ OAuthMetadataFetch,
5
+ OAuthProtectedResourceMetadata,
6
+ OAuthUnauthorizedChallenge,
7
+ } from "mcp-oauth";
8
+ import { canonicalizeResourceIndicator } from "mcp-oauth";
9
+
10
+ export type {
11
+ OAuthAuthorizationServerMetadata,
12
+ OAuthDiscoveryResult,
13
+ OAuthMetadataFetch,
14
+ OAuthProtectedResourceMetadata,
15
+ OAuthUnauthorizedChallenge,
16
+ };
17
+
18
+ export interface OAuthDiscoveryCache {
19
+ get(
20
+ resourceUrl: string
21
+ ): OAuthDiscoveryResult | null | undefined | Promise<OAuthDiscoveryResult | null | undefined>;
22
+ set(resourceUrl: string, value: OAuthDiscoveryResult): void | Promise<void>;
23
+ }
24
+
25
+ export interface OAuthMetadataDiscoveryOptions {
26
+ fetch?: OAuthMetadataFetch;
27
+ cache?: OAuthDiscoveryCache;
28
+ }
29
+
30
+ export interface OAuthMetadataLookupOptions {
31
+ resourceMetadataUrl?: string | URL;
32
+ }
33
+
34
+ function defaultOAuthMetadataFetch(input: string | URL, init?: RequestInit): Promise<Response> {
35
+ return fetch(input, init);
36
+ }
37
+
38
+ function isObjectRecord(value: unknown): value is Record<string, unknown> {
39
+ return typeof value === "object" && value !== null && !Array.isArray(value);
40
+ }
41
+
42
+ function isStringArray(value: unknown): value is string[] {
43
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
44
+ }
45
+
46
+ function normalizeHostname(hostname: string): string {
47
+ return hostname.endsWith(".") ? hostname.slice(0, -1).toLowerCase() : hostname.toLowerCase();
48
+ }
49
+
50
+ function isLoopbackHostname(hostname: string): boolean {
51
+ const normalizedHostname = normalizeHostname(hostname);
52
+
53
+ return normalizedHostname === "localhost"
54
+ || normalizedHostname === "::1"
55
+ || normalizedHostname.startsWith("127.");
56
+ }
57
+
58
+ function assertSecureUrl(url: URL, label: string): void {
59
+ if (url.protocol === "https:") {
60
+ return;
61
+ }
62
+
63
+ if (url.protocol === "http:" && isLoopbackHostname(url.hostname)) {
64
+ return;
65
+ }
66
+
67
+ throw new Error(`${label} must use https unless it targets a loopback host`);
68
+ }
69
+
70
+ function validateProtectedResourceMetadata(
71
+ value: unknown,
72
+ resourceUrl: string
73
+ ): OAuthProtectedResourceMetadata {
74
+ if (!isObjectRecord(value)) {
75
+ throw new Error("Protected resource metadata must be a JSON object");
76
+ }
77
+
78
+ if (typeof value.resource !== "string" || value.resource.length === 0) {
79
+ throw new Error("Protected resource metadata must include a resource string");
80
+ }
81
+
82
+ const normalizedResource = canonicalizeResourceIndicator(value.resource);
83
+
84
+ if (normalizedResource !== resourceUrl) {
85
+ throw new Error(
86
+ `Protected resource metadata resource mismatch: expected ${resourceUrl}, received ${value.resource}`
87
+ );
88
+ }
89
+
90
+ if (!isStringArray(value.authorization_servers) || value.authorization_servers.length === 0) {
91
+ throw new Error(
92
+ "Protected resource metadata must include a non-empty authorization_servers array"
93
+ );
94
+ }
95
+
96
+ return {
97
+ ...value,
98
+ resource: normalizedResource,
99
+ } as OAuthProtectedResourceMetadata;
100
+ }
101
+
102
+ function validateAuthorizationServerMetadata(
103
+ value: unknown,
104
+ issuer: string
105
+ ): OAuthAuthorizationServerMetadata {
106
+ if (!isObjectRecord(value)) {
107
+ throw new Error("Authorization server metadata must be a JSON object");
108
+ }
109
+
110
+ if (typeof value.issuer !== "string" || value.issuer.length === 0) {
111
+ throw new Error("Authorization server metadata must include issuer");
112
+ }
113
+
114
+ if (value.issuer !== issuer) {
115
+ throw new Error(
116
+ `Authorization server metadata issuer mismatch: expected ${issuer}, received ${value.issuer}`
117
+ );
118
+ }
119
+
120
+ if (
121
+ typeof value.authorization_endpoint !== "string" ||
122
+ value.authorization_endpoint.length === 0
123
+ ) {
124
+ throw new Error("Authorization server metadata must include authorization_endpoint");
125
+ }
126
+
127
+ if (typeof value.token_endpoint !== "string" || value.token_endpoint.length === 0) {
128
+ throw new Error("Authorization server metadata must include token_endpoint");
129
+ }
130
+
131
+ if (
132
+ !isStringArray(value.response_types_supported) ||
133
+ !value.response_types_supported.includes("code")
134
+ ) {
135
+ throw new Error(
136
+ "Authorization server metadata must include response_types_supported containing code"
137
+ );
138
+ }
139
+
140
+ if (
141
+ !isStringArray(value.code_challenge_methods_supported) ||
142
+ !value.code_challenge_methods_supported.includes("S256")
143
+ ) {
144
+ throw new Error(
145
+ "Authorization server metadata must include code_challenge_methods_supported containing S256"
146
+ );
147
+ }
148
+
149
+ return value as OAuthAuthorizationServerMetadata;
150
+ }
151
+
152
+ async function readJsonResponse(response: Response, label: string): Promise<unknown> {
153
+ if (!response.ok) {
154
+ const statusDescriptor = `${response.status} ${response.statusText}`.trim();
155
+ throw new Error(`${label} request failed (${statusDescriptor})`);
156
+ }
157
+
158
+ try {
159
+ return await response.json();
160
+ } catch {
161
+ throw new Error(`${label} response must be valid JSON`);
162
+ }
163
+ }
164
+
165
+ function resolveWellKnownMetadataUrl(inputUrl: string | URL, suffix: string): string {
166
+ const url = new URL(typeof inputUrl === "string" ? inputUrl : inputUrl.toString());
167
+
168
+ const resourcePath = url.pathname === "/" ? "" : url.pathname;
169
+ url.pathname = `/.well-known/${suffix}${resourcePath}`;
170
+
171
+ return url.toString();
172
+ }
173
+
174
+ export function resolveProtectedResourceMetadataUrl(
175
+ resourceUrl: string | URL,
176
+ resourceMetadataUrl?: string | URL
177
+ ): string {
178
+ const resource = new URL(canonicalizeResourceIndicator(resourceUrl));
179
+ assertSecureUrl(resource, "Protected resource URL");
180
+
181
+ if (resourceMetadataUrl !== undefined) {
182
+ const resolvedResourceMetadataUrl = new URL(
183
+ typeof resourceMetadataUrl === "string" ? resourceMetadataUrl : resourceMetadataUrl.toString(),
184
+ resource
185
+ );
186
+ assertSecureUrl(resolvedResourceMetadataUrl, "Protected resource metadata URL");
187
+ return resolvedResourceMetadataUrl.toString();
188
+ }
189
+
190
+ const resolvedResourceMetadataUrl = new URL(
191
+ resolveWellKnownMetadataUrl(resource, "oauth-protected-resource")
192
+ );
193
+ assertSecureUrl(resolvedResourceMetadataUrl, "Protected resource metadata URL");
194
+ return resolvedResourceMetadataUrl.toString();
195
+ }
196
+
197
+ function normalizeAuthorizationServerIssuer(issuer: string | URL): string {
198
+ const input = typeof issuer === "string" ? issuer : issuer.toString();
199
+ const url = new URL(input);
200
+ if (url.search.length > 0 || url.hash.length > 0) {
201
+ throw new Error("Authorization server issuer must not include query or fragment");
202
+ }
203
+
204
+ assertSecureUrl(url, "Authorization server issuer");
205
+
206
+ if (url.pathname.length > 1 && url.pathname.endsWith("/")) {
207
+ url.pathname = url.pathname.slice(0, -1);
208
+ }
209
+
210
+ return url.pathname === "/" ? url.origin : url.toString();
211
+ }
212
+
213
+ export function resolveAuthorizationServerMetadataUrl(issuer: string | URL): string {
214
+ return resolveWellKnownMetadataUrl(
215
+ normalizeAuthorizationServerIssuer(issuer),
216
+ "oauth-authorization-server"
217
+ );
218
+ }
219
+
220
+ export class OAuthMetadataDiscovery {
221
+ private readonly fetchImpl: OAuthMetadataFetch;
222
+ private readonly cache: OAuthDiscoveryCache | undefined;
223
+ private readonly memoryCache = new Map<string, OAuthDiscoveryResult>();
224
+
225
+ constructor({ fetch = defaultOAuthMetadataFetch, cache }: OAuthMetadataDiscoveryOptions = {}) {
226
+ this.fetchImpl = fetch;
227
+ this.cache = cache;
228
+ }
229
+
230
+ async discover(
231
+ resourceUrl: string | URL,
232
+ { resourceMetadataUrl }: OAuthMetadataLookupOptions = {}
233
+ ): Promise<OAuthDiscoveryResult> {
234
+ const cacheKey = canonicalizeResourceIndicator(resourceUrl);
235
+ const resourceMetadataLocation = resolveProtectedResourceMetadataUrl(
236
+ cacheKey,
237
+ resourceMetadataUrl
238
+ );
239
+ const memoryCachedResult = this.memoryCache.get(cacheKey);
240
+ if (
241
+ memoryCachedResult !== undefined &&
242
+ (resourceMetadataUrl === undefined
243
+ || memoryCachedResult.resourceMetadataUrl === resourceMetadataLocation)
244
+ ) {
245
+ return memoryCachedResult;
246
+ }
247
+
248
+ const sharedCachedResult = await this.cache?.get(cacheKey);
249
+ if (
250
+ sharedCachedResult !== null &&
251
+ sharedCachedResult !== undefined &&
252
+ (resourceMetadataUrl === undefined
253
+ || sharedCachedResult.resourceMetadataUrl === resourceMetadataLocation)
254
+ ) {
255
+ this.memoryCache.set(cacheKey, sharedCachedResult);
256
+ return sharedCachedResult;
257
+ }
258
+
259
+ const resourceMetadataResponse = await this.fetchImpl(resourceMetadataLocation, {
260
+ method: "GET",
261
+ headers: {
262
+ Accept: "application/json",
263
+ },
264
+ });
265
+ const resourceMetadata = validateProtectedResourceMetadata(
266
+ await readJsonResponse(resourceMetadataResponse, "Protected resource metadata"),
267
+ cacheKey
268
+ );
269
+
270
+ const authorizationServerErrors: string[] = [];
271
+
272
+ for (const authorizationServer of resourceMetadata.authorization_servers) {
273
+ const normalizedAuthorizationServer = normalizeAuthorizationServerIssuer(
274
+ authorizationServer
275
+ );
276
+ const authorizationServerMetadataUrl =
277
+ resolveAuthorizationServerMetadataUrl(normalizedAuthorizationServer);
278
+
279
+ try {
280
+ const authorizationServerResponse = await this.fetchImpl(authorizationServerMetadataUrl, {
281
+ method: "GET",
282
+ headers: {
283
+ Accept: "application/json",
284
+ },
285
+ });
286
+ const authorizationServerMetadata = validateAuthorizationServerMetadata(
287
+ await readJsonResponse(
288
+ authorizationServerResponse,
289
+ "Authorization server metadata"
290
+ ),
291
+ normalizedAuthorizationServer
292
+ );
293
+
294
+ const result: OAuthDiscoveryResult = {
295
+ resource: resourceMetadata.resource,
296
+ resourceMetadataUrl: resourceMetadataLocation,
297
+ resourceMetadata,
298
+ authorizationServer: normalizedAuthorizationServer,
299
+ authorizationServerMetadataUrl,
300
+ authorizationServerMetadata,
301
+ };
302
+
303
+ this.memoryCache.set(cacheKey, result);
304
+ await this.cache?.set(cacheKey, result);
305
+ return result;
306
+ } catch (error) {
307
+ authorizationServerErrors.push(
308
+ `${authorizationServerMetadataUrl}: ${
309
+ error instanceof Error ? error.message : String(error)
310
+ }`
311
+ );
312
+ }
313
+ }
314
+
315
+ throw new Error(
316
+ `Unable to load authorization server metadata for ${cacheKey}: ${authorizationServerErrors.join(
317
+ "; "
318
+ )}`
319
+ );
320
+ }
321
+ }
322
+
323
+ export async function discoverOAuthMetadata(
324
+ resourceUrl: string | URL,
325
+ options: OAuthMetadataDiscoveryOptions & OAuthMetadataLookupOptions = {}
326
+ ): Promise<OAuthDiscoveryResult> {
327
+ const discovery = new OAuthMetadataDiscovery(options);
328
+ return discovery.discover(resourceUrl, options);
329
+ }
330
+
331
+ function skipOptionalWhitespace(value: string, start: number): number {
332
+ let index = start;
333
+ while (index < value.length && (value[index] === " " || value[index] === "\t")) {
334
+ index += 1;
335
+ }
336
+ return index;
337
+ }
338
+
339
+ function readToken(value: string, start: number): { token: string; nextIndex: number } | null {
340
+ let index = start;
341
+ while (index < value.length) {
342
+ const character = value[index];
343
+ if (
344
+ character === " "
345
+ || character === "\t"
346
+ || character === ","
347
+ || character === "="
348
+ || character === "\""
349
+ ) {
350
+ break;
351
+ }
352
+ index += 1;
353
+ }
354
+
355
+ if (index === start) {
356
+ return null;
357
+ }
358
+
359
+ return {
360
+ token: value.slice(start, index),
361
+ nextIndex: index,
362
+ };
363
+ }
364
+
365
+ function looksLikeAuthParam(value: string, start: number): boolean {
366
+ const token = readToken(value, start);
367
+ if (token === null) {
368
+ return false;
369
+ }
370
+
371
+ return value[skipOptionalWhitespace(value, token.nextIndex)] === "=";
372
+ }
373
+
374
+ function isToken68Character(character: string): boolean {
375
+ return (
376
+ (character >= "a" && character <= "z")
377
+ || (character >= "A" && character <= "Z")
378
+ || (character >= "0" && character <= "9")
379
+ || character === "-"
380
+ || character === "."
381
+ || character === "_"
382
+ || character === "~"
383
+ || character === "+"
384
+ || character === "/"
385
+ );
386
+ }
387
+
388
+ function readToken68(
389
+ value: string,
390
+ start: number
391
+ ): { token68: string; nextIndex: number } | null {
392
+ let nextIndex = start;
393
+ while (nextIndex < value.length) {
394
+ const character = value[nextIndex];
395
+ if (character === "," || character === " " || character === "\t") {
396
+ break;
397
+ }
398
+ nextIndex += 1;
399
+ }
400
+
401
+ const token68 = value.slice(start, nextIndex);
402
+ if (token68.length === 0) {
403
+ return null;
404
+ }
405
+
406
+ let index = 0;
407
+ while (index < token68.length && isToken68Character(token68[index]!)) {
408
+ index += 1;
409
+ }
410
+
411
+ if (index === 0) {
412
+ return null;
413
+ }
414
+
415
+ while (index < token68.length && token68[index] === "=") {
416
+ index += 1;
417
+ }
418
+
419
+ if (index !== token68.length) {
420
+ return null;
421
+ }
422
+
423
+ return { token68, nextIndex };
424
+ }
425
+
426
+ function readQuotedString(
427
+ value: string,
428
+ start: number
429
+ ): { parsedValue: string; nextIndex: number } | null {
430
+ if (value[start] !== "\"") {
431
+ return null;
432
+ }
433
+
434
+ let parsedValue = "";
435
+ let index = start + 1;
436
+ let escaping = false;
437
+
438
+ while (index < value.length) {
439
+ const character = value[index];
440
+ if (escaping) {
441
+ parsedValue += character;
442
+ escaping = false;
443
+ index += 1;
444
+ continue;
445
+ }
446
+
447
+ if (character === "\\") {
448
+ escaping = true;
449
+ index += 1;
450
+ continue;
451
+ }
452
+
453
+ if (character === "\"") {
454
+ return {
455
+ parsedValue,
456
+ nextIndex: index + 1,
457
+ };
458
+ }
459
+
460
+ parsedValue += character;
461
+ index += 1;
462
+ }
463
+
464
+ return null;
465
+ }
466
+
467
+ function parseAuthParam(
468
+ value: string,
469
+ start: number
470
+ ): { name: string; value: string; nextIndex: number } | null {
471
+ const token = readToken(value, start);
472
+ if (token === null) {
473
+ return null;
474
+ }
475
+
476
+ let index = skipOptionalWhitespace(value, token.nextIndex);
477
+ if (value[index] !== "=") {
478
+ return null;
479
+ }
480
+
481
+ index = skipOptionalWhitespace(value, index + 1);
482
+ if (index >= value.length) {
483
+ return null;
484
+ }
485
+
486
+ const quotedValue = readQuotedString(value, index);
487
+ if (quotedValue !== null) {
488
+ return {
489
+ name: token.token,
490
+ value: quotedValue.parsedValue,
491
+ nextIndex: quotedValue.nextIndex,
492
+ };
493
+ }
494
+
495
+ let nextIndex = index;
496
+ while (nextIndex < value.length) {
497
+ const character = value[nextIndex];
498
+ if (character === "," || character === " " || character === "\t") {
499
+ break;
500
+ }
501
+ nextIndex += 1;
502
+ }
503
+
504
+ return {
505
+ name: token.token,
506
+ value: value.slice(index, nextIndex),
507
+ nextIndex,
508
+ };
509
+ }
510
+
511
+ export function parseBearerWwwAuthenticateHeader(
512
+ headerValue: string | null
513
+ ): OAuthUnauthorizedChallenge | null {
514
+ if (headerValue === null) {
515
+ return null;
516
+ }
517
+
518
+ let index = 0;
519
+ let firstBearerChallenge: OAuthUnauthorizedChallenge | null = null;
520
+
521
+ while (index < headerValue.length) {
522
+ index = skipOptionalWhitespace(headerValue, index);
523
+ while (headerValue[index] === ",") {
524
+ index = skipOptionalWhitespace(headerValue, index + 1);
525
+ }
526
+
527
+ const scheme = readToken(headerValue, index);
528
+ if (scheme === null) {
529
+ break;
530
+ }
531
+
532
+ index = skipOptionalWhitespace(headerValue, scheme.nextIndex);
533
+ const params: Record<string, string> = {};
534
+
535
+ if (index < headerValue.length && headerValue[index] !== ",") {
536
+ const token68 = readToken68(headerValue, index);
537
+ if (token68 !== null) {
538
+ index = token68.nextIndex;
539
+ } else if (looksLikeAuthParam(headerValue, index)) {
540
+ while (index < headerValue.length) {
541
+ const parsedParam = parseAuthParam(headerValue, index);
542
+ if (parsedParam === null) {
543
+ break;
544
+ }
545
+
546
+ params[parsedParam.name] = parsedParam.value;
547
+ index = skipOptionalWhitespace(headerValue, parsedParam.nextIndex);
548
+
549
+ if (headerValue[index] !== ",") {
550
+ break;
551
+ }
552
+
553
+ const nextIndex = skipOptionalWhitespace(headerValue, index + 1);
554
+ if (!looksLikeAuthParam(headerValue, nextIndex)) {
555
+ index = nextIndex;
556
+ break;
557
+ }
558
+
559
+ index = nextIndex;
560
+ }
561
+ }
562
+ }
563
+
564
+ if (scheme.token.toLowerCase() === "bearer") {
565
+ const challenge: OAuthUnauthorizedChallenge = {
566
+ scheme: "Bearer",
567
+ params,
568
+ raw: headerValue,
569
+ };
570
+ if (Object.keys(params).length > 0) {
571
+ return challenge;
572
+ }
573
+
574
+ firstBearerChallenge ??= challenge;
575
+ }
576
+
577
+ if (headerValue[index] === ",") {
578
+ index += 1;
579
+ }
580
+ }
581
+
582
+ return firstBearerChallenge;
583
+ }