@every-app/sdk 0.3.0-rc.1 → 0.3.0-rc.3

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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cloudflare/index.d.ts +2 -2
  3. package/dist/cloudflare/index.d.ts.map +1 -1
  4. package/dist/cloudflare/index.js +1 -1
  5. package/dist/cloudflare/sentinel.d.ts +39 -1
  6. package/dist/cloudflare/sentinel.d.ts.map +1 -1
  7. package/dist/cloudflare/sentinel.js +22 -1
  8. package/dist/cloudflare/worker.d.ts +205 -7
  9. package/dist/cloudflare/worker.d.ts.map +1 -1
  10. package/dist/cloudflare/worker.js +174 -11
  11. package/dist/internal/aiDefault.d.ts +141 -0
  12. package/dist/internal/aiDefault.d.ts.map +1 -0
  13. package/dist/internal/aiDefault.js +204 -0
  14. package/dist/internal/aiProviders.d.ts +4 -1
  15. package/dist/internal/aiProviders.d.ts.map +1 -1
  16. package/dist/internal/aiProviders.js +5 -1
  17. package/dist/internal/appSettings.d.ts +20 -2
  18. package/dist/internal/appSettings.d.ts.map +1 -1
  19. package/dist/internal/appSettings.js +53 -7
  20. package/dist/internal/audit.d.ts +108 -0
  21. package/dist/internal/audit.d.ts.map +1 -0
  22. package/dist/internal/audit.js +235 -0
  23. package/dist/internal/capabilities.d.ts +2315 -141
  24. package/dist/internal/capabilities.d.ts.map +1 -1
  25. package/dist/internal/capabilities.js +122 -0
  26. package/dist/internal/catalog/commerce.d.ts +394 -0
  27. package/dist/internal/catalog/commerce.d.ts.map +1 -0
  28. package/dist/internal/catalog/commerce.js +477 -0
  29. package/dist/internal/catalog/comms.d.ts +338 -0
  30. package/dist/internal/catalog/comms.d.ts.map +1 -0
  31. package/dist/internal/catalog/comms.js +477 -0
  32. package/dist/internal/catalog/index.d.ts +1936 -0
  33. package/dist/internal/catalog/index.d.ts.map +1 -0
  34. package/dist/internal/catalog/index.js +19 -0
  35. package/dist/internal/catalog/inference.d.ts +393 -0
  36. package/dist/internal/catalog/inference.d.ts.map +1 -0
  37. package/dist/internal/catalog/inference.js +457 -0
  38. package/dist/internal/catalog/infra.d.ts +423 -0
  39. package/dist/internal/catalog/infra.d.ts.map +1 -0
  40. package/dist/internal/catalog/infra.js +543 -0
  41. package/dist/internal/catalog/search.d.ts +400 -0
  42. package/dist/internal/catalog/search.d.ts.map +1 -0
  43. package/dist/internal/catalog/search.js +463 -0
  44. package/dist/internal/devProviders.d.ts +2 -2
  45. package/dist/internal/devProviders.d.ts.map +1 -1
  46. package/dist/internal/devProviders.js +19 -1
  47. package/dist/internal/events.d.ts +13 -2
  48. package/dist/internal/events.d.ts.map +1 -1
  49. package/dist/internal/events.js +21 -3
  50. package/dist/internal/gatewaySettings.d.ts +2 -1
  51. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  52. package/dist/internal/gatewaySettings.js +10 -2
  53. package/dist/internal/index.d.ts +5 -0
  54. package/dist/internal/index.d.ts.map +1 -1
  55. package/dist/internal/index.js +5 -0
  56. package/dist/internal/oauthPresets.d.ts +29 -0
  57. package/dist/internal/oauthPresets.d.ts.map +1 -0
  58. package/dist/internal/oauthPresets.js +534 -0
  59. package/dist/internal/oauthScopes.d.ts +56 -1
  60. package/dist/internal/oauthScopes.d.ts.map +1 -1
  61. package/dist/internal/oauthScopes.js +98 -4
  62. package/dist/internal/oauthSpec.d.ts +59 -0
  63. package/dist/internal/oauthSpec.d.ts.map +1 -0
  64. package/dist/internal/oauthSpec.js +676 -0
  65. package/dist/internal/protocol.d.ts +11 -0
  66. package/dist/internal/protocol.d.ts.map +1 -1
  67. package/dist/internal/protocol.js +9 -0
  68. package/dist/internal/providerRegistry.d.ts +17 -6
  69. package/dist/internal/providerRegistry.d.ts.map +1 -1
  70. package/dist/internal/providerRegistry.js +51 -2
  71. package/dist/internal/providerSpec.d.ts +11 -7
  72. package/dist/internal/providerSpec.d.ts.map +1 -1
  73. package/dist/internal/providerSpec.js +142 -33
  74. package/dist/internal/signInProviders.d.ts +104 -0
  75. package/dist/internal/signInProviders.d.ts.map +1 -0
  76. package/dist/internal/signInProviders.js +245 -0
  77. package/dist/server/ai.d.ts +45 -0
  78. package/dist/server/ai.d.ts.map +1 -0
  79. package/dist/server/ai.js +40 -0
  80. package/dist/server/connectUrl.d.ts +21 -0
  81. package/dist/server/connectUrl.d.ts.map +1 -0
  82. package/dist/server/connectUrl.js +37 -0
  83. package/dist/server/gatewayFetch.d.ts.map +1 -1
  84. package/dist/server/gatewayFetch.js +72 -8
  85. package/dist/server/index.d.ts +4 -2
  86. package/dist/server/index.d.ts.map +1 -1
  87. package/dist/server/index.js +3 -1
  88. package/dist/server/integrationFetch.d.ts +39 -2
  89. package/dist/server/integrationFetch.d.ts.map +1 -1
  90. package/dist/server/integrationFetch.js +177 -13
  91. package/package.json +12 -12
@@ -7,7 +7,7 @@
7
7
  * check, the SDK's local-dev twin of it and, later, the proxy and the
8
8
  * outbound Worker. Nothing here reads a secret or a token value.
9
9
  */
10
- import type { OAuthScopePolicy } from "./capabilities.js";
10
+ import type { OAuthScopeDialect, OAuthScopePolicy } from "./capabilities.js";
11
11
  import type { TokenRequest } from "./oauthClient.js";
12
12
  /** Scopes by the token they belong to: the bot or only token, and Slack's user token. */
13
13
  export interface ScopeList {
@@ -58,6 +58,54 @@ export declare function carriesToken(json: unknown): boolean;
58
58
  * hold to anything.
59
59
  */
60
60
  export declare function grantedScopes(policy: OAuthScopePolicy, json: unknown): GrantedScopes | null;
61
+ /**
62
+ * What a token response granted, for the Gateway-owned flow: as
63
+ * {@link grantedScopes}, except that a `space` or `comma` provider that
64
+ * names no `scope` at all is taken at its word (RFC 6749 §5.1: the field
65
+ * is optional when the grant matches the request), so `requested` stands.
66
+ * The three parsed dialects keep failing closed.
67
+ */
68
+ export declare function grantedScopesOrRequested(policy: OAuthScopePolicy, json: unknown, requested: ScopeList): GrantedScopes | null;
69
+ /** Why the proxy refuses a held-token call, with what would let it through. */
70
+ export type HeldTokenRefusal = {
71
+ ok: false;
72
+ code: "integration_not_connected";
73
+ /** The declared sets the connection was not granted. */
74
+ missingSets: string[];
75
+ } | {
76
+ ok: false;
77
+ code: "integration_scope_not_allowed" | "integration_method_not_allowed";
78
+ message: string;
79
+ };
80
+ /**
81
+ * Whether an app's call with a held token goes through, and under which
82
+ * set (docs/design/2026-10-oauth-providers.md section 7). In order: every
83
+ * set the app declares must be among the connection's (else the person
84
+ * connects again: `integration_not_connected` with the missing sets); the
85
+ * host must be one of the provider's `api` hosts; the path passes the
86
+ * segment rules (`scopeRouteAllows`'s: no batch tunnels, no dot segments,
87
+ * no denied segments); then a declared set with routes must cover the
88
+ * call, or a declared set without routes covers its hosts by access: a
89
+ * read set unlocks `readMethods` (GET and HEAD by default), a write set
90
+ * every method the provider offers. `writes` says whether the method is
91
+ * one the grant allows beyond reads, decided by the caller as for an
92
+ * API-key provider.
93
+ */
94
+ export declare function heldTokenCallAllowed({ policy, declaredSets, connectionSets, hosts, method, upstream, readMethods, writes, }: {
95
+ policy: OAuthScopePolicy;
96
+ declaredSets: readonly string[];
97
+ connectionSets: readonly string[];
98
+ /** The provider's `api` hosts. */
99
+ hosts: readonly string[];
100
+ method: string;
101
+ upstream: URL;
102
+ readMethods?: readonly string[];
103
+ /** Whether `method` is a write the app's grant allows. */
104
+ writes: boolean;
105
+ }): {
106
+ ok: true;
107
+ set: string;
108
+ } | HeldTokenRefusal;
61
109
  /**
62
110
  * What `granted` carries beyond `allowed`, per token kind, for a refusal's
63
111
  * message and log line: the user token's extras as `user:<scope>`. Both
@@ -109,6 +157,13 @@ export declare function rewriteTokenRequestScope(policy: OAuthScopePolicy, sets:
109
157
  * the token to a path this never matched.
110
158
  */
111
159
  export declare function scopeRouteAllows(policy: OAuthScopePolicy, sets: readonly string[], method: string, upstream: URL): string | null;
160
+ /** `scopes` in the dialect's one spelling, deduplicated, order kept. */
161
+ /**
162
+ * `scopes` in the dialect's canonical spelling (Google's long names,
163
+ * Microsoft's bare lower-case names), for comparing a set's scopes with
164
+ * what a token response says it granted.
165
+ */
166
+ export declare function canonicalScopes(dialect: OAuthScopeDialect, scopes: readonly string[]): string[];
112
167
  /**
113
168
  * `text` as a JSON object, or null for anything else (an array, a scalar,
114
169
  * not JSON). Shared with the gateway's read of a provider's token answer.
@@ -1 +1 @@
1
- {"version":3,"file":"oauthScopes.d.ts","sourceRoot":"","sources":["../../src/internal/oauthScopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAEV,gBAAgB,EAEjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,yFAAyF;AACzF,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,4FAA4F;AAC5F,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GACtC,MAAM,EAAE,GAAG,IAAI,CAejB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,SAAS,CASX;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,SAAS,CASX;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CASnD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,OAAO,GACZ,aAAa,GAAG,IAAI,CA6BtB;AA+BD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,SAAS,GACjB,MAAM,EAAE,CASV;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,YAAY,GACpB,aAAa,GAAG,IAAI,CAStB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,YAAY,GACpB,YAAY,CA+Bd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,GAAG,GACZ,MAAM,GAAG,IAAI,CAsBf;AAwJD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMvE"}
1
+ {"version":3,"file":"oauthScopes.d.ts","sourceRoot":"","sources":["../../src/internal/oauthScopes.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ export function declaredScopeSets(policy, entry) {
30
30
  export function requestedScopes(policy, sets) {
31
31
  const scopes = [];
32
32
  const userScopes = [];
33
- for (const [, set] of declaredSets(policy, sets)) {
33
+ for (const [, set] of setsOf(policy, sets)) {
34
34
  pushNew(scopes, set.scopes);
35
35
  pushNew(userScopes, set.userScopes ?? []);
36
36
  }
@@ -103,7 +103,91 @@ export function grantedScopes(policy, json) {
103
103
  }
104
104
  case "slack":
105
105
  return slackGrantedScopes(body);
106
+ case "space":
107
+ case "comma": {
108
+ const scopes = splitScopes(policy.dialect, body["scope"]);
109
+ return scopes ? { scopes } : null;
110
+ }
111
+ }
112
+ }
113
+ /**
114
+ * What a token response granted, for the Gateway-owned flow: as
115
+ * {@link grantedScopes}, except that a `space` or `comma` provider that
116
+ * names no `scope` at all is taken at its word (RFC 6749 §5.1: the field
117
+ * is optional when the grant matches the request), so `requested` stands.
118
+ * The three parsed dialects keep failing closed.
119
+ */
120
+ export function grantedScopesOrRequested(policy, json, requested) {
121
+ const granted = grantedScopes(policy, json);
122
+ if (granted)
123
+ return granted;
124
+ const body = record(json);
125
+ if (body &&
126
+ body["scope"] === undefined &&
127
+ (policy.dialect === "space" || policy.dialect === "comma")) {
128
+ return { scopes: canonical(policy.dialect, requested.scopes) };
106
129
  }
130
+ return null;
131
+ }
132
+ /**
133
+ * Whether an app's call with a held token goes through, and under which
134
+ * set (docs/design/2026-10-oauth-providers.md section 7). In order: every
135
+ * set the app declares must be among the connection's (else the person
136
+ * connects again: `integration_not_connected` with the missing sets); the
137
+ * host must be one of the provider's `api` hosts; the path passes the
138
+ * segment rules (`scopeRouteAllows`'s: no batch tunnels, no dot segments,
139
+ * no denied segments); then a declared set with routes must cover the
140
+ * call, or a declared set without routes covers its hosts by access: a
141
+ * read set unlocks `readMethods` (GET and HEAD by default), a write set
142
+ * every method the provider offers. `writes` says whether the method is
143
+ * one the grant allows beyond reads, decided by the caller as for an
144
+ * API-key provider.
145
+ */
146
+ export function heldTokenCallAllowed({ policy, declaredSets, connectionSets, hosts, method, upstream, readMethods = ["GET", "HEAD"], writes, }) {
147
+ const missingSets = declaredSets.filter((name) => !connectionSets.includes(name));
148
+ if (missingSets.length > 0) {
149
+ return { ok: false, code: "integration_not_connected", missingSets };
150
+ }
151
+ if (upstream.protocol !== "https:" || !hosts.includes(upstream.host)) {
152
+ return {
153
+ ok: false,
154
+ code: "integration_scope_not_allowed",
155
+ message: `${upstream.host} is not a host this provider's token reaches (${hosts.join(", ")}).`,
156
+ };
157
+ }
158
+ if (!safeSegments(policy, upstream.pathname)) {
159
+ return {
160
+ ok: false,
161
+ code: "integration_scope_not_allowed",
162
+ message: `${upstream.pathname} is a path no scope set unlocks.`,
163
+ };
164
+ }
165
+ const verb = method.toUpperCase();
166
+ const reads = readMethods.includes(verb);
167
+ // A set's routes decide first: a read-shaped POST a read set lists
168
+ // under `posts` (a search, a query) is a read whatever the grant.
169
+ const routed = scopeRouteAllows(policy, declaredSets, verb, upstream);
170
+ if (routed)
171
+ return { ok: true, set: routed };
172
+ if (!reads && !writes) {
173
+ return {
174
+ ok: false,
175
+ code: "integration_method_not_allowed",
176
+ message: `${verb} ${upstream.pathname}: calls may use ${readMethods.join(" or ")} with access: "read"; ${verb} needs access: "write" in the app's capabilities entry, on a provider that offers it, or a scope set whose routes list it.`,
177
+ };
178
+ }
179
+ for (const [name, set] of setsOf(policy, declaredSets)) {
180
+ if (set.routes.length > 0)
181
+ continue;
182
+ if (reads || (writes && set.access === "write")) {
183
+ return { ok: true, set: name };
184
+ }
185
+ }
186
+ return {
187
+ ok: false,
188
+ code: "integration_scope_not_allowed",
189
+ message: `${verb} ${upstream.pathname} is outside the scope sets this app declares (${declaredSets.join(", ")}).`,
190
+ };
107
191
  }
108
192
  /**
109
193
  * Slack answers with up to two tokens, each with its own scopes: the bot
@@ -243,7 +327,7 @@ export function rewriteTokenRequestScope(policy, sets, request) {
243
327
  export function scopeRouteAllows(policy, sets, method, upstream) {
244
328
  if (upstream.protocol !== "https:")
245
329
  return null;
246
- const onHost = declaredSets(policy, sets).flatMap(([name, set]) => set.routes
330
+ const onHost = setsOf(policy, sets).flatMap(([name, set]) => set.routes
247
331
  .filter((route) => route.host === upstream.host)
248
332
  .map((route) => [name, route]));
249
333
  if (onHost.length === 0)
@@ -318,7 +402,7 @@ function safeSegment(text, denied) {
318
402
  return !text.split("/").some((part) => DOT_SEGMENT.test(part));
319
403
  }
320
404
  /** The policy's sets in registry order, keeping those `names` declares. */
321
- function declaredSets(policy, names) {
405
+ function setsOf(policy, names) {
322
406
  return Object.entries(policy.sets).filter(([name]) => names.includes(name));
323
407
  }
324
408
  const GOOGLE_ALIASES = new Map([
@@ -327,6 +411,14 @@ const GOOGLE_ALIASES = new Map([
327
411
  ]);
328
412
  const GRAPH_PREFIX = /^https:\/\/graph\.microsoft\.com\//i;
329
413
  /** `scopes` in the dialect's one spelling, deduplicated, order kept. */
414
+ /**
415
+ * `scopes` in the dialect's canonical spelling (Google's long names,
416
+ * Microsoft's bare lower-case names), for comparing a set's scopes with
417
+ * what a token response says it granted.
418
+ */
419
+ export function canonicalScopes(dialect, scopes) {
420
+ return canonical(dialect, [...scopes]);
421
+ }
330
422
  function canonical(dialect, scopes) {
331
423
  return pushNew([], scopes.map((scope) => {
332
424
  switch (dialect) {
@@ -335,6 +427,8 @@ function canonical(dialect, scopes) {
335
427
  case "microsoft":
336
428
  return scope.replace(GRAPH_PREFIX, "").toLowerCase();
337
429
  case "slack":
430
+ case "space":
431
+ case "comma":
338
432
  return scope;
339
433
  }
340
434
  }));
@@ -347,7 +441,7 @@ function canonical(dialect, scopes) {
347
441
  function splitScopes(dialect, value) {
348
442
  if (typeof value !== "string")
349
443
  return null;
350
- const separator = dialect === "slack" ? /[,\s]+/ : /\s+/;
444
+ const separator = dialect === "slack" || dialect === "comma" ? /[,\s]+/ : /\s+/;
351
445
  const scopes = canonical(dialect, value.split(separator).filter(Boolean));
352
446
  return scopes.length > 0 ? scopes : null;
353
447
  }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The `oauth2-user` half of a provider spec
3
+ * (docs/design/2026-10-oauth-providers.md section 3): a registered OAuth
4
+ * provider, or a preset (./oauthPresets.ts), as JSON, turned into the
5
+ * same {@link OAuthClientCapability} a catalog client is, with a `flow`
6
+ * the Gateway runs itself. `parseProviderSpec` (./providerSpec.ts) hands
7
+ * the common fields here once it sees `auth.kind: "oauth2-user"`; nothing
8
+ * here reads a value: a spec names Gateway settings, never holds one.
9
+ */
10
+ import { type CapabilityAccess, type OAuthClientCapability } from "./capabilities.js";
11
+ export interface SpecProblem {
12
+ path: string;
13
+ message: string;
14
+ }
15
+ /** A scope set's name: `identity`, or `<area>.<access>` (`repo.read`), lowercase. */
16
+ export declare const SCOPE_SET_NAME: RegExp;
17
+ /** Segments no set unlocks on any host: batch tunnels carry whole requests inside. */
18
+ export declare const DEFAULT_DENIED_SEGMENTS: readonly string[];
19
+ /** The fields of `auth` when its kind is `oauth2-user`. */
20
+ export declare const OAUTH_AUTH_FIELDS: readonly ["kind", "authorizeUrl", "tokenUrl", "revokeUrl", "revokeMethod", "clientId", "clientSecret", "inject", "pkce", "scopeDialect", "authorizeParams", "tokenHeaders", "identity"];
21
+ /** The common fields `parseProviderSpec` parsed before handing over. */
22
+ export interface OAuthSpecCommon {
23
+ id: string;
24
+ name: string;
25
+ docs: string;
26
+ hosts: readonly string[];
27
+ description?: string;
28
+ reference?: string;
29
+ access?: readonly CapabilityAccess[];
30
+ readMethods?: readonly string[];
31
+ writeMethods?: readonly string[];
32
+ hint?: string;
33
+ example?: string;
34
+ tags?: readonly string[];
35
+ }
36
+ /** What `parseOAuthUserSpec` returns on success, before the registry's rules. */
37
+ export interface ParsedOAuthSpec {
38
+ capability: OAuthClientCapability;
39
+ /** Every host a row's rules must accept: the API hosts and the endpoints'. */
40
+ hosts: string[];
41
+ }
42
+ /**
43
+ * Parse the OAuth half of a spec whose `auth.kind` is `oauth2-user`:
44
+ * `auth` itself, `scopeSets`, `alwaysRequested` and `tolerated`, with
45
+ * the common fields already parsed. Every problem is reported; on
46
+ * success the catalog-shaped capability with its `flow`.
47
+ */
48
+ export declare function parseOAuthUserSpec(input: Record<string, unknown>, common: OAuthSpecCommon, problems: SpecProblem[]): ParsedOAuthSpec | undefined;
49
+ /**
50
+ * The JSON spec of an OAuth capability that has a `flow` (`provider
51
+ * show`, a preset's round trip): `parseProviderSpec` of it gives the
52
+ * capability back. A catalog client without a `flow` has no spec.
53
+ */
54
+ export declare function oauthSpecOf(id: string, capability: OAuthClientCapability): Record<string, unknown> | null;
55
+ /** The methods a held-token write may use on `capability` beyond its reads. */
56
+ export declare function oauthWriteMethods(capability: OAuthClientCapability): readonly string[];
57
+ /** The methods a read grant may use on `capability` (GET and HEAD unless the spec says). */
58
+ export declare function oauthReadMethods(capability: OAuthClientCapability): readonly string[];
59
+ //# sourceMappingURL=oauthSpec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthSpec.d.ts","sourceRoot":"","sources":["../../src/internal/oauthSpec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAQ3B,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qFAAqF;AACrF,eAAO,MAAM,cAAc,QAC0B,CAAC;AA4BtD,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAAwB,CAAC;AAE9E,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,yLAcpB,CAAC;AAEX,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACrC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAmXD,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,qBAAqB,CAAC;IAClC,8EAA8E;IAC9E,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,WAAW,EAAE,GACtB,eAAe,GAAG,SAAS,CAqN7B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,qBAAqB,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAyFhC;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,qBAAqB,GAChC,SAAS,MAAM,EAAE,CAEnB;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,qBAAqB,GAChC,SAAS,MAAM,EAAE,CAEnB"}