@every-app/sdk 0.3.0-rc.1 → 0.3.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/cloudflare/catalogSettings.generated.d.ts +81 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
- package/dist/cloudflare/catalogSettings.generated.js +174 -0
- package/dist/cloudflare/index.d.ts +3 -2
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +4 -1
- package/dist/cloudflare/sentinel.d.ts +60 -2
- package/dist/cloudflare/sentinel.d.ts.map +1 -1
- package/dist/cloudflare/sentinel.js +37 -2
- package/dist/cloudflare/worker.d.ts +357 -63
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +363 -104
- package/dist/dev/index.d.ts +1 -1
- package/dist/dev/index.d.ts.map +1 -1
- package/dist/dev/index.js +1 -1
- package/dist/dev/keypair.d.ts +2 -0
- package/dist/dev/keypair.d.ts.map +1 -1
- package/dist/dev/keypair.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/internal/aiDefault.d.ts +204 -0
- package/dist/internal/aiDefault.d.ts.map +1 -0
- package/dist/internal/aiDefault.js +334 -0
- package/dist/internal/aiProviders.d.ts +7 -2
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +17 -4
- package/dist/internal/appSettings.d.ts +82 -4
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +204 -29
- package/dist/internal/audit.d.ts +108 -0
- package/dist/internal/audit.d.ts.map +1 -0
- package/dist/internal/audit.js +235 -0
- package/dist/internal/capabilities.d.ts +2268 -438
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +128 -539
- package/dist/internal/catalog/commerce.d.ts +394 -0
- package/dist/internal/catalog/commerce.d.ts.map +1 -0
- package/dist/internal/catalog/commerce.js +477 -0
- package/dist/internal/catalog/comms.d.ts +375 -0
- package/dist/internal/catalog/comms.d.ts.map +1 -0
- package/dist/internal/catalog/comms.js +520 -0
- package/dist/internal/catalog/index.d.ts +1973 -0
- package/dist/internal/catalog/index.d.ts.map +1 -0
- package/dist/internal/catalog/index.js +19 -0
- package/dist/internal/catalog/inference.d.ts +393 -0
- package/dist/internal/catalog/inference.d.ts.map +1 -0
- package/dist/internal/catalog/inference.js +460 -0
- package/dist/internal/catalog/infra.d.ts +423 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -0
- package/dist/internal/catalog/infra.js +543 -0
- package/dist/internal/catalog/search.d.ts +400 -0
- package/dist/internal/catalog/search.d.ts.map +1 -0
- package/dist/internal/catalog/search.js +463 -0
- package/dist/internal/devProviders.d.ts +2 -2
- package/dist/internal/devProviders.d.ts.map +1 -1
- package/dist/internal/devProviders.js +19 -1
- package/dist/internal/events.d.ts +13 -2
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +47 -4
- package/dist/internal/gatewaySettings.d.ts +3 -2
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +12 -6
- package/dist/internal/identity.d.ts +41 -4
- package/dist/internal/identity.d.ts.map +1 -1
- package/dist/internal/identity.js +48 -10
- package/dist/internal/index.d.ts +6 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +6 -1
- package/dist/internal/members.d.ts +3 -2
- package/dist/internal/members.d.ts.map +1 -1
- package/dist/internal/oauthClient.d.ts +10 -12
- package/dist/internal/oauthClient.d.ts.map +1 -1
- package/dist/internal/oauthClient.js +20 -81
- package/dist/internal/oauthPresets.d.ts +33 -0
- package/dist/internal/oauthPresets.d.ts.map +1 -0
- package/dist/internal/oauthPresets.js +1064 -0
- package/dist/internal/oauthScopes.d.ts +56 -1
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +98 -4
- package/dist/internal/oauthSpec.d.ts +102 -0
- package/dist/internal/oauthSpec.d.ts.map +1 -0
- package/dist/internal/oauthSpec.js +789 -0
- package/dist/internal/protocol.d.ts +64 -1
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +51 -1
- package/dist/internal/providerRegistry.d.ts +77 -6
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +178 -8
- package/dist/internal/providerSpec.d.ts +18 -7
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +442 -34
- package/dist/internal/signInProviders.d.ts +104 -0
- package/dist/internal/signInProviders.d.ts.map +1 -0
- package/dist/internal/signInProviders.js +245 -0
- package/dist/manifest.d.ts +238 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +30 -0
- package/dist/server/ai.d.ts +111 -0
- package/dist/server/ai.d.ts.map +1 -0
- package/dist/server/ai.js +145 -0
- package/dist/server/aiBinding.d.ts +68 -0
- package/dist/server/aiBinding.d.ts.map +1 -0
- package/dist/server/aiBinding.js +294 -0
- package/dist/server/attribution.d.ts +9 -5
- package/dist/server/attribution.d.ts.map +1 -1
- package/dist/server/attribution.js +12 -4
- package/dist/server/authorizeUrl.d.ts +1 -1
- package/dist/server/authorizeUrl.d.ts.map +1 -1
- package/dist/server/authorizeUrl.js +2 -6
- package/dist/server/connectUrl.d.ts +21 -0
- package/dist/server/connectUrl.d.ts.map +1 -0
- package/dist/server/connectUrl.js +37 -0
- package/dist/server/context.d.ts +68 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +117 -0
- package/dist/server/emailBinding.d.ts +72 -0
- package/dist/server/emailBinding.d.ts.map +1 -0
- package/dist/server/emailBinding.js +124 -0
- package/dist/server/emit.d.ts +1 -1
- package/dist/server/emit.d.ts.map +1 -1
- package/dist/server/emit.js +7 -7
- package/dist/server/env.d.ts +44 -0
- package/dist/server/env.d.ts.map +1 -0
- package/dist/server/env.js +1 -0
- package/dist/server/everyApp.d.ts +27 -7
- package/dist/server/everyApp.d.ts.map +1 -1
- package/dist/server/everyApp.js +106 -5
- package/dist/server/fromMcpTools.d.ts +89 -0
- package/dist/server/fromMcpTools.d.ts.map +1 -0
- package/dist/server/fromMcpTools.js +192 -0
- package/dist/server/gatewayFetch.d.ts +42 -18
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +138 -19
- package/dist/server/gatewayUrl.d.ts +13 -0
- package/dist/server/gatewayUrl.d.ts.map +1 -0
- package/dist/server/gatewayUrl.js +34 -0
- package/dist/server/index.d.ts +15 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +12 -2
- package/dist/server/integrationFetch.d.ts +70 -39
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +244 -82
- package/dist/server/listMembers.d.ts +1 -1
- package/dist/server/listMembers.d.ts.map +1 -1
- package/dist/server/listMembers.js +8 -11
- package/dist/server/refusal.d.ts +55 -0
- package/dist/server/refusal.d.ts.map +1 -0
- package/dist/server/refusal.js +193 -0
- package/dist/server/sendEmail.d.ts +1 -1
- package/dist/server/sendEmail.d.ts.map +1 -1
- package/dist/server/sendEmail.js +7 -7
- package/dist/server/toolsEndpoint.d.ts.map +1 -1
- package/dist/server/toolsEndpoint.js +11 -5
- package/dist/tanstack-start/index.d.ts +9 -0
- package/dist/tanstack-start/index.d.ts.map +1 -0
- package/dist/tanstack-start/index.js +29 -0
- package/dist/testing/index.d.ts +93 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +156 -0
- package/package.json +35 -14
|
@@ -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,
|
|
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
|
|
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 =
|
|
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
|
|
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,102 @@
|
|
|
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, type OAuthScopeSet } 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
|
+
/**
|
|
37
|
+
* One scope set (`{ label, access, scopes, userScopes?, routes? }`) at
|
|
38
|
+
* `at`, its routes on `hosts` alone; undefined when a field is wrong,
|
|
39
|
+
* every problem reported. `overlay`: a set added to a built-in client
|
|
40
|
+
* must name a scope and say what it unlocks (a set with no routes unlocks
|
|
41
|
+
* nothing on the held-token path); a spec's own set may leave both empty
|
|
42
|
+
* (Notion's sets carry no scope string).
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseScopeSet(raw: unknown, at: string, hosts: readonly string[], problems: SpecProblem[], { overlay }?: {
|
|
45
|
+
overlay?: boolean;
|
|
46
|
+
}): OAuthScopeSet | undefined;
|
|
47
|
+
/** What `parseScopeSetOverlay` returns. */
|
|
48
|
+
export type ParsedScopeSetOverlay = {
|
|
49
|
+
ok: true;
|
|
50
|
+
name: string;
|
|
51
|
+
set: OAuthScopeSet;
|
|
52
|
+
} | {
|
|
53
|
+
ok: false;
|
|
54
|
+
problems: SpecProblem[];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* A scope set an operator adds to a built-in OAuth client
|
|
58
|
+
* (`everyapp provider scope-set add google-workspace gmail-send.json`):
|
|
59
|
+
* `{ "name": "gmail.send", "label", "access", "scopes", "routes" }`, held
|
|
60
|
+
* to the rules a built-in set is (the set's shape, routes on the client's
|
|
61
|
+
* `api` hosts alone, anchored `posts`) plus two of its own: `access` is
|
|
62
|
+
* said outright (a write set is never implied by its scopes) and `routes`
|
|
63
|
+
* is required. A `provider` field, when present, must be `id`. The name
|
|
64
|
+
* may not be one of the client's own sets: an overlay adds, never
|
|
65
|
+
* replaces. Nothing here reads a value.
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseScopeSetOverlay(input: unknown, client: {
|
|
68
|
+
id: string;
|
|
69
|
+
capability: OAuthClientCapability;
|
|
70
|
+
}): ParsedScopeSetOverlay;
|
|
71
|
+
/**
|
|
72
|
+
* `capability` with `sets` added to its scope policy: the sets after the
|
|
73
|
+
* client's own, in the order given, and each set's scopes added to the
|
|
74
|
+
* authorize URL's list (what the operator's client must be allowed), as
|
|
75
|
+
* `parseOAuthUserSpec` builds it. A name the client already has is left
|
|
76
|
+
* as the client's (the registry refuses such an overlay before this).
|
|
77
|
+
*/
|
|
78
|
+
export declare function withScopeSets(capability: OAuthClientCapability, sets: Readonly<Record<string, OAuthScopeSet>>): OAuthClientCapability;
|
|
79
|
+
/** What `parseOAuthUserSpec` returns on success, before the registry's rules. */
|
|
80
|
+
export interface ParsedOAuthSpec {
|
|
81
|
+
capability: OAuthClientCapability;
|
|
82
|
+
/** Every host a row's rules must accept: the API hosts and the endpoints'. */
|
|
83
|
+
hosts: string[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Parse the OAuth half of a spec whose `auth.kind` is `oauth2-user`:
|
|
87
|
+
* `auth` itself, `scopeSets`, `alwaysRequested` and `tolerated`, with
|
|
88
|
+
* the common fields already parsed. Every problem is reported; on
|
|
89
|
+
* success the catalog-shaped capability with its `flow`.
|
|
90
|
+
*/
|
|
91
|
+
export declare function parseOAuthUserSpec(input: Record<string, unknown>, common: OAuthSpecCommon, problems: SpecProblem[]): ParsedOAuthSpec | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* The JSON spec of an OAuth capability that has a `flow` (`provider
|
|
94
|
+
* show`, a preset's round trip): `parseProviderSpec` of it gives the
|
|
95
|
+
* capability back. A catalog client without a `flow` has no spec.
|
|
96
|
+
*/
|
|
97
|
+
export declare function oauthSpecOf(id: string, capability: OAuthClientCapability): Record<string, unknown> | null;
|
|
98
|
+
/** The methods a held-token write may use on `capability` beyond its reads. */
|
|
99
|
+
export declare function oauthWriteMethods(capability: OAuthClientCapability): readonly string[];
|
|
100
|
+
/** The methods a read grant may use on `capability` (GET and HEAD unless the spec says). */
|
|
101
|
+
export declare function oauthReadMethods(capability: OAuthClientCapability): readonly string[];
|
|
102
|
+
//# sourceMappingURL=oauthSpec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthSpec.d.ts","sourceRoot":"","sources":["../../src/internal/oauthSpec.ts"],"names":[],"mappings":"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"}
|