@wildo-ai/external-connectors-models 1.1.4 → 1.1.6
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/dist/esm/companion-exports.d.ts.map +1 -1
- package/dist/esm/core/commons.external-connectors-models.schemas.d.ts +23 -132
- package/dist/esm/core/commons.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/core/commons.external-connectors-models.schemas.js +23 -31
- package/dist/esm/core/commons.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/core/operation-classification.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/core/operation-classification.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/core/security.external-connectors-models.schemas.d.ts +40 -8
- package/dist/esm/core/security.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/core/security.external-connectors-models.schemas.js +61 -14
- package/dist/esm/core/security.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/index.d.ts +5 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/protocols/agent-client-protocol.external-connectors-models.schemas.d.ts +78 -163
- package/dist/esm/protocols/agent-client-protocol.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/agent-client-protocol.external-connectors-models.schemas.js +78 -93
- package/dist/esm/protocols/agent-client-protocol.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/billing.standard-interface.schemas.d.ts +258 -13
- package/dist/esm/protocols/billing.standard-interface.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/billing.standard-interface.schemas.js +41 -0
- package/dist/esm/protocols/billing.standard-interface.schemas.js.map +1 -1
- package/dist/esm/protocols/document-extraction.external-connectors-models.schemas.d.ts +57 -17
- package/dist/esm/protocols/document-extraction.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/document-extraction.external-connectors-models.schemas.js +26 -7
- package/dist/esm/protocols/document-extraction.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/email-inbound.standard-interface.schemas.d.ts +229 -0
- package/dist/esm/protocols/email-inbound.standard-interface.schemas.d.ts.map +1 -0
- package/dist/esm/protocols/email-inbound.standard-interface.schemas.js +160 -0
- package/dist/esm/protocols/email-inbound.standard-interface.schemas.js.map +1 -0
- package/dist/esm/protocols/email.external-connectors-models.schemas.d.ts +1 -56
- package/dist/esm/protocols/email.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/email.external-connectors-models.schemas.js +0 -20
- package/dist/esm/protocols/email.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/email.standard-interface.schemas.d.ts +4 -4
- package/dist/esm/protocols/email.standard-interface.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/embeddings.external-connectors-models.schemas.d.ts +1 -16
- package/dist/esm/protocols/embeddings.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/embeddings.external-connectors-models.schemas.js +4 -9
- package/dist/esm/protocols/embeddings.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/file-storage.standard-interface.d.ts +179 -0
- package/dist/esm/protocols/file-storage.standard-interface.d.ts.map +1 -0
- package/dist/esm/protocols/file-storage.standard-interface.js +79 -0
- package/dist/esm/protocols/file-storage.standard-interface.js.map +1 -0
- package/dist/esm/protocols/frontend.external-connectors-models.schemas.d.ts +6 -13
- package/dist/esm/protocols/frontend.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/frontend.external-connectors-models.schemas.js +0 -9
- package/dist/esm/protocols/frontend.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/hooks-capability.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/hooks-capability.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/hooks.external-connectors-models.schemas.d.ts +20 -16
- package/dist/esm/protocols/hooks.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/hooks.external-connectors-models.schemas.js +12 -9
- package/dist/esm/protocols/hooks.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/image.external-connectors-models.schemas.d.ts +1 -18
- package/dist/esm/protocols/image.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/image.external-connectors-models.schemas.js +1 -7
- package/dist/esm/protocols/image.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/llm.external-connectors-models.schemas.d.ts +100 -40
- package/dist/esm/protocols/llm.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/llm.external-connectors-models.schemas.js +164 -41
- package/dist/esm/protocols/llm.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/oauth2-provider-assurance.definitions.d.ts +7 -0
- package/dist/esm/protocols/oauth2-provider-assurance.definitions.d.ts.map +1 -0
- package/dist/esm/protocols/oauth2-provider-assurance.definitions.js +36 -0
- package/dist/esm/protocols/oauth2-provider-assurance.definitions.js.map +1 -0
- package/dist/esm/protocols/oauth2-provider-assurance.schemas.d.ts +23 -0
- package/dist/esm/protocols/oauth2-provider-assurance.schemas.d.ts.map +1 -0
- package/dist/esm/protocols/oauth2-provider-assurance.schemas.js +25 -0
- package/dist/esm/protocols/oauth2-provider-assurance.schemas.js.map +1 -0
- package/dist/esm/protocols/oauth2.external-connectors-models.schemas.d.ts +0 -148
- package/dist/esm/protocols/oauth2.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/oauth2.external-connectors-models.schemas.js +1 -40
- package/dist/esm/protocols/oauth2.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/provider-oauth2-token-endpoint-hosts.external-connectors-models.d.ts +39 -0
- package/dist/esm/protocols/provider-oauth2-token-endpoint-hosts.external-connectors-models.d.ts.map +1 -0
- package/dist/esm/protocols/provider-oauth2-token-endpoint-hosts.external-connectors-models.js +66 -0
- package/dist/esm/protocols/provider-oauth2-token-endpoint-hosts.external-connectors-models.js.map +1 -0
- package/dist/esm/protocols/rest-operations.external-connectors-models.schemas.d.ts +863 -16
- package/dist/esm/protocols/rest-operations.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/rest-operations.external-connectors-models.schemas.js +703 -0
- package/dist/esm/protocols/rest-operations.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/sms.external-connectors-models.schemas.d.ts +0 -39
- package/dist/esm/protocols/sms.external-connectors-models.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/sms.external-connectors-models.schemas.js +0 -13
- package/dist/esm/protocols/sms.external-connectors-models.schemas.js.map +1 -1
- package/dist/esm/protocols/sms.standard-interface.schemas.d.ts +1 -1
- package/dist/esm/protocols/sms.standard-interface.schemas.d.ts.map +1 -1
- package/dist/esm/protocols/sms.standard-interface.schemas.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -3
- package/dist/esm/core/protocols.external-connectors-models.schemas.d.ts +0 -74
- package/dist/esm/core/protocols.external-connectors-models.schemas.d.ts.map +0 -1
- package/dist/esm/core/protocols.external-connectors-models.schemas.js +0 -1
- package/dist/esm/core/protocols.external-connectors-models.schemas.js.map +0 -1
- package/dist/esm/protocols/rest-api.external-connectors-models.schemas.d.ts +0 -37
- package/dist/esm/protocols/rest-api.external-connectors-models.schemas.d.ts.map +0 -1
- package/dist/esm/protocols/rest-api.external-connectors-models.schemas.js +0 -10
- package/dist/esm/protocols/rest-api.external-connectors-models.schemas.js.map +0 -1
|
@@ -19,11 +19,35 @@
|
|
|
19
19
|
* path template, where its parameters ride — and none of that is backend runtime. The arrow already
|
|
20
20
|
* pointed this way, since this package has no dependency on the private backend and the private
|
|
21
21
|
* backend depends on it.
|
|
22
|
+
*
|
|
23
|
+
* ## This is the ONLY description of a REST provider — two earlier ones were deleted
|
|
24
|
+
*
|
|
25
|
+
* `rest-api.external-connectors-models.schemas.ts` described REST providers twice over, and both
|
|
26
|
+
* descriptions are gone. Neither should come back beside this module.
|
|
27
|
+
*
|
|
28
|
+
* - **An OpenAPI-shaped operation layer** (`REST_API_OperationSpecSchema`,
|
|
29
|
+
* `REST_API_OperationContextSchema`, `REST_API_OperationDefinition`, `API_Parameter*`,
|
|
30
|
+
* `API_ContentType`) — deleted 2026-09-06 by #331. Ten exported symbols and zero code readers: a
|
|
31
|
+
* provider that authored one compiled, shipped, and was invisible to every runtime.
|
|
32
|
+
* - **A profile / configuration / status triple** (`REST_API_Provider_Profile`,
|
|
33
|
+
* `REST_API_Provider_Configuration`, `REST_APIProvider_StatusSchema`) — deleted 2026-09-12 (#18).
|
|
34
|
+
* Its only reader was the `ExternalProvider_ExchangeProtocol` union, itself read by nothing but a
|
|
35
|
+
* drift test pinning it to the enum, and deleted in the same change. Its `pagination.style` was an
|
|
36
|
+
* anonymous `"offset" | "cursor" | "page" | "link"` restating {@link ProviderRestPaginationStyle}.
|
|
37
|
+
*
|
|
38
|
+
* WHERE a REST API lives and how it authenticates is the `rest` arm of the backend runtime contract
|
|
39
|
+
* (`@wildo-ai/external-connectors-private/backend`); WHAT can be called against it is
|
|
40
|
+
* {@link ProviderRestOperation}, executed by `ProviderOperationExecutorBackendService`. A second
|
|
41
|
+
* description of either is not a missing feature — it is the drift that makes an authoring surface
|
|
42
|
+
* untrustworthy, because an author cannot tell which of two shapes is the one that runs. If an
|
|
43
|
+
* OpenAPI document is ever read (a second mapper input), it feeds `ProviderRestOperation` like
|
|
44
|
+
* every other source.
|
|
22
45
|
*/
|
|
23
46
|
import { z } from 'zod';
|
|
24
47
|
import { API_HttpMethod } from '../core/commons.external-connectors-models.schemas';
|
|
25
48
|
import { ExternalOperation_Classification } from '../core/operation-classification.external-connectors-models.schemas';
|
|
26
49
|
import type { ExternalProvider_ConnectionTarget } from '../core/security.external-connectors-models.schemas';
|
|
50
|
+
/** @wildo_source:part:start external-connectors.models.rest-operation-contract facet:layer:shared facet:family:provider facet:audience:app-developer */
|
|
27
51
|
/** Where a declared parameter rides on the wire. */
|
|
28
52
|
export declare enum ProviderRestParameterLocation {
|
|
29
53
|
/** Substituted into a `{token}` of the path template. Always required — a path cannot have a hole. */
|
|
@@ -41,7 +65,30 @@ export declare enum ProviderRestRequestBodyEncoding {
|
|
|
41
65
|
FORM_URLENCODED = "form-urlencoded",
|
|
42
66
|
MULTIPART = "multipart",
|
|
43
67
|
/** A raw text body; `contentType` on the declaration says which media type is sent. */
|
|
44
|
-
TEXT = "text"
|
|
68
|
+
TEXT = "text",
|
|
69
|
+
/**
|
|
70
|
+
* Raw bytes (#367, folding #364) — an image, a PDF, a file upload a vendor takes as the whole body.
|
|
71
|
+
* `contentType` is REQUIRED, by the TEXT arm's rule: a body without its media type is not addressable.
|
|
72
|
+
* The input must supply a `Uint8Array` (a Node `Buffer` is one); nothing is converted into bytes here,
|
|
73
|
+
* because a string encoded under a guessed charset is exactly how an upload arrives corrupted.
|
|
74
|
+
*/
|
|
75
|
+
BINARY = "binary"
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* How the vendor's ANSWER is read (#367, folding #364).
|
|
79
|
+
*
|
|
80
|
+
* The request side could already send TEXT and the transport could already read TEXT and NONE, while
|
|
81
|
+
* the operation declaration could only say JSON — so an operation against a `text/csv` export could be
|
|
82
|
+
* authored and then failed at runtime as `INVALID_JSON`, a contract-breach message for a declaration
|
|
83
|
+
* the contract had no way to state. This is the missing half of that asymmetry.
|
|
84
|
+
*/
|
|
85
|
+
export declare enum ProviderRestResponseDecoding {
|
|
86
|
+
/** Parse the body as JSON. The default, and what an absent `responseDecoding` means. */
|
|
87
|
+
JSON = "json",
|
|
88
|
+
/** Keep the body as text — a CSV export, an XML document. The operation's output is that string. */
|
|
89
|
+
TEXT = "text",
|
|
90
|
+
/** Read nothing — a write whose answer carries no information (a 204). The output is `undefined`. */
|
|
91
|
+
NONE = "none"
|
|
45
92
|
}
|
|
46
93
|
/**
|
|
47
94
|
* How a listing operation continues. A HINT the caller reads, never something the executor acts
|
|
@@ -81,8 +128,20 @@ export interface ProviderRestOperationRequestBody {
|
|
|
81
128
|
* by a declared parameter — the ordinary shape, and the reason parameters declare `from`.
|
|
82
129
|
*/
|
|
83
130
|
readonly from?: string;
|
|
84
|
-
/** For `TEXT`: the media type actually sent (`text/csv`, `application/
|
|
131
|
+
/** For `TEXT` and `BINARY`: the media type actually sent (`text/csv`, `application/pdf`). Required for both. */
|
|
85
132
|
readonly contentType?: string;
|
|
133
|
+
/**
|
|
134
|
+
* A JSON body sent INSIDE one named property: `envelope: 'data'` sends `{ "data": { …the body… } }` (#333).
|
|
135
|
+
*
|
|
136
|
+
* Asana answers `data: Missing input` to a task whose fields arrive at the top level — its API reads
|
|
137
|
+
* every write from a `data` wrapper, which the source catalogue builds in its request helper and the
|
|
138
|
+
* transposer never saw. Declared here rather than modelled as a nested input, so the INPUT stays flat:
|
|
139
|
+
* the fields a person fills in, and the fields an `inputOptions` key names, are the fields they see.
|
|
140
|
+
*
|
|
141
|
+
* JSON only. Any other encoding with an envelope is refused by name when the request is compiled — a
|
|
142
|
+
* form, text or binary body has no property to wrap it in.
|
|
143
|
+
*/
|
|
144
|
+
readonly envelope?: string;
|
|
86
145
|
}
|
|
87
146
|
/** Where the payload sits inside a response, and how a listing continues. */
|
|
88
147
|
export interface ProviderRestOperationPagination {
|
|
@@ -95,6 +154,44 @@ export interface ProviderRestOperationPagination {
|
|
|
95
154
|
/** CURSOR/LINK: dot-path in the RESPONSE carrying the next cursor or the next URL. */
|
|
96
155
|
readonly nextPath?: string;
|
|
97
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Where an input's CHOICES come from: another operation of the same provider, run through the SAME
|
|
159
|
+
* connection, whose answer lists the values a person may pick (#333).
|
|
160
|
+
*
|
|
161
|
+
* The shape every workflow product converged on, and deliberately not a value stored on the connection.
|
|
162
|
+
* A workspace, a project, a channel are choices WITHIN an account, made per call: Zapier fills a
|
|
163
|
+
* dynamic dropdown with "a live API call", Make fills a module field through an RPC that "must have the
|
|
164
|
+
* same connection", n8n's resource locator lists through the node's credentials, and the Activepieces
|
|
165
|
+
* source this catalogue is scraped from declares Asana's workspace as a required dropdown whose options
|
|
166
|
+
* are fetched with the connected account. A connection records only what the vendor REPORTED when it
|
|
167
|
+
* was authorized ({@link ProviderRestOAuth2.accountRefResponsePath}).
|
|
168
|
+
*
|
|
169
|
+
* Declaring an options source does not make the options operation reachable from a browser: it is its
|
|
170
|
+
* own operation, and `providers.browserOperations` must list it like any other.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* // Asana: a task's project is chosen from the projects of the chosen workspace.
|
|
174
|
+
* inputOptions: {
|
|
175
|
+
* workspace: { operationId: 'list_workspaces', valuePath: 'gid', labelPath: 'name' },
|
|
176
|
+
* project: { operationId: 'list_projects', valuePath: 'gid', labelPath: 'name', dependsOn: { workspace: 'workspace' } },
|
|
177
|
+
* }
|
|
178
|
+
*/
|
|
179
|
+
export interface ProviderRestOperationInputOptions {
|
|
180
|
+
/** The operation that lists the choices. Same provider; its output (after its own `resultPath`) must be an array. */
|
|
181
|
+
readonly operationId: string;
|
|
182
|
+
/** That operation's catalogue domain, when it differs from the one being called. */
|
|
183
|
+
readonly domain?: string;
|
|
184
|
+
/** Dot-path, inside each listed item, to the value the input receives (`gid`). */
|
|
185
|
+
readonly valuePath: string;
|
|
186
|
+
/** Dot-path, inside each listed item, to the text a person reads (`name`). Vendor content, never derived from the value. */
|
|
187
|
+
readonly labelPath: string;
|
|
188
|
+
/**
|
|
189
|
+
* Inputs the options depend on: `{ <options-operation input field>: <this operation's input field> }`.
|
|
190
|
+
* A choice that is not yet made is REFUSED by name rather than listed as empty — "no projects" and
|
|
191
|
+
* "choose a workspace first" are different answers, and only one of them is true.
|
|
192
|
+
*/
|
|
193
|
+
readonly dependsOn?: Readonly<Record<string, string>>;
|
|
194
|
+
}
|
|
98
195
|
/**
|
|
99
196
|
* A provider operation, hand-authored. The unit an application addresses:
|
|
100
197
|
* `executeProviderOperation({ providerRef: 'slack', operationId: 'conversations.list', input })`.
|
|
@@ -138,14 +235,35 @@ export interface ProviderRestOperation {
|
|
|
138
235
|
* value is in naming the fields a caller may rely on, not in policing the vendor.
|
|
139
236
|
*/
|
|
140
237
|
readonly outputSchema?: z.ZodType;
|
|
141
|
-
/**
|
|
238
|
+
/**
|
|
239
|
+
* How the answer is read. Absent = `JSON`, so every operation authored before this field existed
|
|
240
|
+
* reads exactly as it did.
|
|
241
|
+
*
|
|
242
|
+
* It also sets the default `Accept` header — `application/json` for JSON, `*\/*` otherwise — which a
|
|
243
|
+
* declared `Accept` HEADER parameter overrides when a vendor needs a specific media type.
|
|
244
|
+
*
|
|
245
|
+
* Two combinations are refused by name when the request is compiled, because each describes a payload
|
|
246
|
+
* that cannot exist: `resultPath` beside `TEXT` or `NONE` (there is no structure to walk), and
|
|
247
|
+
* `outputSchema` beside `NONE` (there is nothing to parse). `outputSchema` beside `TEXT` is legal and
|
|
248
|
+
* validates the string.
|
|
249
|
+
*/
|
|
250
|
+
readonly responseDecoding?: ProviderRestResponseDecoding;
|
|
251
|
+
/** Dot-path to the payload inside the response body. Absent = the body IS the payload. JSON answers only. */
|
|
142
252
|
readonly resultPath?: string;
|
|
143
253
|
readonly pagination?: ProviderRestOperationPagination;
|
|
144
254
|
/**
|
|
145
|
-
* OAuth scopes (or vendor permission names) the credential must carry
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
255
|
+
* OAuth scopes (or vendor permission names) the credential must carry, as the exact strings the vendor
|
|
256
|
+
* grants. CHECKED before the request leaves (#979): `ProviderOperationExecutorBackendService` compares them
|
|
257
|
+
* with the scopes recorded on the spent grant and refuses with the missing ones named when any is absent.
|
|
258
|
+
* A grant that records no scopes (a pasted key, a vendor that does not echo `scope`) proceeds, and the
|
|
259
|
+
* call's audit row says the check could not be made — refusing there would break every such vendor.
|
|
260
|
+
*
|
|
261
|
+
* Until 2026-09-14 this said "declared and not checked"; checking needed the granted set on the grant,
|
|
262
|
+
* which the connected-accounts lane now stores. Two things not to confuse it
|
|
263
|
+
* with: `ProviderRestOAuth2.scopes` is the vendor's ADVISORY default set, and the wildo-cli
|
|
264
|
+
* `getProviderApiKey({ requiredScopes })` parameter is a list of secret-contract SLUGS — a
|
|
265
|
+
* different concept sharing the name, which no production caller feeds from this field. As of
|
|
266
|
+
* 2026-09-12 no generated provider declares it either.
|
|
149
267
|
*/
|
|
150
268
|
readonly requiredScopes?: readonly string[];
|
|
151
269
|
/**
|
|
@@ -159,6 +277,11 @@ export interface ProviderRestOperation {
|
|
|
159
277
|
readonly idempotent?: boolean;
|
|
160
278
|
/** Narrows the provider's semantics for this one operation (a slow export, a tight endpoint). */
|
|
161
279
|
readonly remoteCallSemantics?: Partial<ProviderRestCallSemantics>;
|
|
280
|
+
/**
|
|
281
|
+
* Where each input's choices come from, keyed by INPUT field name. Absent for an input a caller types.
|
|
282
|
+
* See {@link ProviderRestOperationInputOptions}.
|
|
283
|
+
*/
|
|
284
|
+
readonly inputOptions?: Readonly<Record<string, ProviderRestOperationInputOptions>>;
|
|
162
285
|
/** Set when the vendor has deprecated it; the executor logs once per operation, never refuses. */
|
|
163
286
|
readonly deprecated?: {
|
|
164
287
|
readonly reason: string;
|
|
@@ -172,7 +295,28 @@ export interface ProviderRestOperationCatalogue {
|
|
|
172
295
|
readonly domains: readonly string[];
|
|
173
296
|
/** Keyed by `operationId`. */
|
|
174
297
|
readonly operations: Readonly<Record<string, ProviderRestOperation>>;
|
|
298
|
+
/**
|
|
299
|
+
* Which package contributed an operation that the provider's OWN module does not declare, keyed by
|
|
300
|
+
* `operationId` (#1672). Absent on every catalogue a provider module loads itself; set only by the
|
|
301
|
+
* framework when it merges an opted-in AUGMENTATION into a provider it does not own.
|
|
302
|
+
*
|
|
303
|
+
* A catalogue value and not a property of the operation, on purpose: an operation declaration is the
|
|
304
|
+
* augmentation author's data, and a field the author could write would let a package claim to be
|
|
305
|
+
* somebody else. The merge refuses a loaded catalogue that already carries this field.
|
|
306
|
+
*
|
|
307
|
+
* Read by the operation executor, which writes the contributor into the `PROVIDER_OPERATION_CALL` audit
|
|
308
|
+
* row: a call spending a shared grant must say whose code declared the request it sent.
|
|
309
|
+
*/
|
|
310
|
+
readonly augmentedBy?: Readonly<Record<string, ProviderRestOperationContributor>>;
|
|
311
|
+
}
|
|
312
|
+
/** The package that contributed an operation to a provider it does not own (#1672). */
|
|
313
|
+
export interface ProviderRestOperationContributor {
|
|
314
|
+
/** The augmentation package's npm name, as its own manifest states it. */
|
|
315
|
+
readonly packageName: string;
|
|
316
|
+
/** The installed version of that package, as `wildo config sync` read it from the manifest. */
|
|
317
|
+
readonly packageVersion: string;
|
|
175
318
|
}
|
|
319
|
+
/** @wildo_source:part:end external-connectors.models.rest-operation-contract */
|
|
176
320
|
/**
|
|
177
321
|
* The lazy catalogue loader — the seam #330's host loader consumes (interface note on #331).
|
|
178
322
|
*
|
|
@@ -205,9 +349,96 @@ export interface ProviderRestCallSemantics {
|
|
|
205
349
|
/** Linear backoff base — attempt N waits `backoffMs * (N - 1)`. */
|
|
206
350
|
readonly backoffMs: number;
|
|
207
351
|
};
|
|
208
|
-
/**
|
|
352
|
+
/**
|
|
353
|
+
* This REF's share of its connection's allowance: a sliding-window ceiling counted for every call made
|
|
354
|
+
* through this ref, whichever account it spends (#1729).
|
|
355
|
+
*
|
|
356
|
+
* It is a CEILING UNDER the connection's own budgets ({@link ProviderRateBudgetDeclaration}, declared by
|
|
357
|
+
* the connection's base), never a statement of the vendor's limit: several refs of one shared connection
|
|
358
|
+
* each keep their own ceiling and all draw on the one vendor allowance together.
|
|
359
|
+
*/
|
|
209
360
|
readonly maxRequestsPerMinute?: number;
|
|
210
361
|
}
|
|
362
|
+
/** @wildo_source:part:start external-connectors.models.connection-rate-budgets facet:layer:shared facet:family:provider facet:audience:app-developer */
|
|
363
|
+
/**
|
|
364
|
+
* WHOSE allowance a vendor counts a request against, under one connection (#1729).
|
|
365
|
+
*
|
|
366
|
+
* A connection is one application registration at the vendor (one OAuth client, one API project) — the
|
|
367
|
+
* base provider's, however many refs spend it. Vendors meter on two axes of it, and a provider declares the
|
|
368
|
+
* one each of its limits uses:
|
|
369
|
+
*
|
|
370
|
+
* | vendor wording | member |
|
|
371
|
+
* |---|---|
|
|
372
|
+
* | Microsoft Graph "per app across all tenants"; Google "per minute per project" | {@link CONNECTION} |
|
|
373
|
+
* | Google "per minute per user per project"; Dataverse "for each user"; Graph "per app per tenant" / "per app and mailbox" when the grant owner IS that tenant or mailbox | {@link OWNER} |
|
|
374
|
+
*
|
|
375
|
+
* spec-cite: https://learn.microsoft.com/en-us/graph/throttling-limits — the global-limits table ("Per app across all
|
|
376
|
+
* tenants") and the scope list "per app across all tenants, per tenant for all apps, per app per tenant"; and
|
|
377
|
+
* https://developers.google.com/workspace/drive/api/guides/limits — "per minute per project" / "per minute per user per
|
|
378
|
+
* project". Fetched 2026-09-28.
|
|
379
|
+
*
|
|
380
|
+
* Deliberately NOT a member: "per tenant for all apps" (Graph). It counts OTHER applications' calls, which this
|
|
381
|
+
* deployment cannot see, so no local counter can hold it; the vendor's `429` is the only honest signal there.
|
|
382
|
+
*/
|
|
383
|
+
export declare enum ProviderRateBudgetPartition {
|
|
384
|
+
/** One allowance for the whole connection — every ref, every account it acts for, every process. */
|
|
385
|
+
CONNECTION = "connection",
|
|
386
|
+
/** One allowance per account the connection acts for — the grant's owner (a person, an organization, the deployment). */
|
|
387
|
+
OWNER = "owner"
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* One limit a vendor publishes, as the connection's BASE provider declares it (#1729).
|
|
391
|
+
*
|
|
392
|
+
* Read only from the connection's base: a member ref's declaration is inert, exactly like a member's OAuth client,
|
|
393
|
+
* because the limit belongs to the registration and the base owns the registration. Declared on the REST contract
|
|
394
|
+
* (`rest.rateBudgets`) for a connection whose tokens serve one resource, and on each OAuth2 AUDIENCE
|
|
395
|
+
* (`oauth2.audiences.<id>.rateBudgets`) for a connection spanning several — Microsoft Graph and a Dataverse
|
|
396
|
+
* environment are metered by different services with different numbers, so one list for both would be wrong for one.
|
|
397
|
+
*
|
|
398
|
+
* Counted by the engine BEFORE a request leaves: a call waits for room (bounded, see the executor's
|
|
399
|
+
* `rateBudgetWaitMs`) instead of spending a request the vendor would answer with `429`. The window is SLIDING, which
|
|
400
|
+
* is at least as strict as a vendor's fixed window of the same length.
|
|
401
|
+
*/
|
|
402
|
+
export interface ProviderRateBudgetDeclaration {
|
|
403
|
+
/** How many requests the window admits. */
|
|
404
|
+
readonly maxRequests: number;
|
|
405
|
+
/** The window's length, in seconds, as the vendor states it (Graph's global limit is per 10 s, Dataverse's per 300 s). */
|
|
406
|
+
readonly windowSeconds: number;
|
|
407
|
+
/** Whose allowance it is — see {@link ProviderRateBudgetPartition}. */
|
|
408
|
+
readonly partition: ProviderRateBudgetPartition;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Where a budget was COUNTED, as the call that spent it observed (#1729): `shared` when the deployment's shared store
|
|
412
|
+
* (Redis) counted it for every process of the application — backend replicas, minions, workers — and `process` when
|
|
413
|
+
* only this process did, because no shared store is configured here or it did not answer. Recorded on the call's audit
|
|
414
|
+
* row, so a per-process count is visible rather than implied.
|
|
415
|
+
*/
|
|
416
|
+
export declare enum RateBudgetCounting {
|
|
417
|
+
SHARED = "shared",
|
|
418
|
+
PROCESS = "process"
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* The defects of a connection's rate-budget declarations: a non-positive number, and a contract-level list beside
|
|
422
|
+
* `oauth2.audiences` — which would apply to nothing, since every call of such a connection is on one audience. A pure
|
|
423
|
+
* function so the registry refuses it at boot and a test pins it without a host.
|
|
424
|
+
*/
|
|
425
|
+
export declare function describeProviderRateBudgetDefects(input: {
|
|
426
|
+
readonly contractRateBudgets?: readonly ProviderRateBudgetDeclaration[];
|
|
427
|
+
readonly audiences?: Readonly<Record<string, {
|
|
428
|
+
readonly rateBudgets?: readonly ProviderRateBudgetDeclaration[];
|
|
429
|
+
}>>;
|
|
430
|
+
}): string[];
|
|
431
|
+
/**
|
|
432
|
+
* The declarations that apply to one call: the audience's own when the call's token is for an audience, else the
|
|
433
|
+
* contract's. Nothing is inherited between the two — see {@link describeProviderRateBudgetDefects}.
|
|
434
|
+
*/
|
|
435
|
+
export declare function selectProviderRateBudgetDeclarations(input: {
|
|
436
|
+
readonly contractRateBudgets?: readonly ProviderRateBudgetDeclaration[];
|
|
437
|
+
readonly audience?: {
|
|
438
|
+
readonly rateBudgets?: readonly ProviderRateBudgetDeclaration[];
|
|
439
|
+
};
|
|
440
|
+
}): readonly ProviderRateBudgetDeclaration[];
|
|
441
|
+
/** @wildo_source:part:end external-connectors.models.connection-rate-budgets */
|
|
211
442
|
/**
|
|
212
443
|
* A provider whose credential is MINTED by an authorization-code exchange rather than pasted.
|
|
213
444
|
*
|
|
@@ -239,11 +470,81 @@ export interface ProviderRestOAuth2 {
|
|
|
239
470
|
/** Where an authorization code (or a refresh token) is exchanged for an access token. */
|
|
240
471
|
readonly tokenUrl: string;
|
|
241
472
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
473
|
+
* Where a token is handed back to the vendor so the GRANT ends there too (#545).
|
|
474
|
+
*
|
|
475
|
+
* Deleting our copy of a token stops US using it. It does not stop the token working, and a
|
|
476
|
+
* refresh token frequently never expires on its own — so without this, "Disconnect" leaves the
|
|
477
|
+
* application listed in the person's own connected-apps screen at the vendor, and their belief
|
|
478
|
+
* that they have withdrawn access is not quite true.
|
|
479
|
+
*
|
|
480
|
+
* ## Why the field names RFC 7009 rather than "revocation"
|
|
481
|
+
*
|
|
482
|
+
* The name is the CONTRACT, not decoration. A URL here is POSTed
|
|
483
|
+
* `application/x-www-form-urlencoded` with a `token` parameter and an optional
|
|
484
|
+
* `token_type_hint`, and a 200 means the grant is gone — including for a token the server does
|
|
485
|
+
* not recognise.
|
|
486
|
+
* spec-cite: RFC 7009 §2.1 (revocation request) and §2.2 (revocation response), fetched
|
|
487
|
+
* 2026-09-18 — §2.2: *"The authorization server responds with HTTP status code 200 if the token
|
|
488
|
+
* has been revoked successfully or if the client submitted an invalid token"*, and *"invalid
|
|
489
|
+
* tokens do not cause an error response since the client cannot handle such an error in a
|
|
490
|
+
* reasonable way."*
|
|
491
|
+
*
|
|
492
|
+
* That last sentence is why the caller is best-effort and never branches on the body: there is
|
|
493
|
+
* nothing to branch on.
|
|
494
|
+
*
|
|
495
|
+
* **A vendor whose revocation is NOT RFC 7009 declares nothing here, deliberately.** Slack's
|
|
496
|
+
* `auth.revoke` is a Web API method and GitHub's is `DELETE /applications/{client_id}/token` with
|
|
497
|
+
* basic auth and a JSON body; neither is expressible as this URL, and bending the field to fit
|
|
498
|
+
* them would make a declaration that names RFC 7009 and means something else entirely. Those vendors
|
|
499
|
+
* need a per-vendor revocation ACTION, which is a bigger surface and is not built on speculation —
|
|
500
|
+
* until then the honest answer is an absent field, and the disconnect surface says so.
|
|
501
|
+
*
|
|
502
|
+
* Absent — not empty — when no revocation is declared, so "this vendor cannot be told" stays
|
|
503
|
+
* distinguishable from "nobody has looked".
|
|
504
|
+
*/
|
|
505
|
+
readonly rfc7009RevocationUrl?: string;
|
|
506
|
+
/**
|
|
507
|
+
* Scopes the vendor's own catalogue entry asked for. An application may request FEWER (a subset of
|
|
508
|
+
* these, once resolved), and an operation's `requiredScopes` is the finer statement. Kept because
|
|
509
|
+
* it is the only record of what the vendor expects for the operations transposed beside it.
|
|
510
|
+
*
|
|
511
|
+
* ## A scope may carry a placeholder, and it is resolved like an endpoint's (#991)
|
|
512
|
+
*
|
|
513
|
+
* Two shapes exist, classified by {@link classifyProviderOAuth2ScopePlaceholder}:
|
|
514
|
+
*
|
|
515
|
+
* - `{hostUrl}/.default` — the placeholder BEGINS the scope and is followed by `/`, so its value is
|
|
516
|
+
* an ORIGIN (Microsoft Dataverse names the resource a token is minted FOR by the environment URL).
|
|
517
|
+
* It is filled only within {@link endpointHostAllowances}, exactly like an origin in an endpoint:
|
|
518
|
+
* a caller-chosen resource would have the vendor mint a token for a host nobody declared.
|
|
519
|
+
* - `{permissions}` — the placeholder IS the whole scope entry, and its value is one or more scope
|
|
520
|
+
* tokens chosen from {@link scopeValueAllowances}. Such a value can itself name a resource
|
|
521
|
+
* (`https://graph.microsoft.com/.default`), so it is a closed choice, never free text.
|
|
522
|
+
*
|
|
523
|
+
* Any other placement (`api://{x}`, `{a}{b}`, `{x}.default`) is refused as malformed.
|
|
245
524
|
*/
|
|
246
525
|
readonly scopes?: readonly string[];
|
|
526
|
+
/**
|
|
527
|
+
* How this vendor lets an EXISTING grant grow by one permission (#1671) — declared by the provider module that
|
|
528
|
+
* owns the OAuth client, never guessed by the engine. See {@link ProviderOAuth2IncrementalConsent}.
|
|
529
|
+
*
|
|
530
|
+
* Absent means the vendor's behaviour is not recorded, so a re-authorization for a missing scope RESTATES what the
|
|
531
|
+
* grant already holds beside the addition: an ordinary authorization that replaces the previous one, which every
|
|
532
|
+
* RFC 6749 server honours. Declared, the re-authorization asks for the missing scope only and the stored granted
|
|
533
|
+
* set ACCUMULATES the previous consent with the new answer.
|
|
534
|
+
*/
|
|
535
|
+
readonly incrementalConsent?: ProviderOAuth2IncrementalConsent;
|
|
536
|
+
/**
|
|
537
|
+
* Scopes a person cannot grant for themselves: only an administrator of the account can (#1671). Microsoft's
|
|
538
|
+
* application permissions and its admin-restricted delegated permissions are the case — "Application permissions
|
|
539
|
+
* always require admin consent", and a delegated permission that requires admin consent answers a user with "an
|
|
540
|
+
* 'unauthorized to consent' error".
|
|
541
|
+
* spec-cite: https://learn.microsoft.com/en-us/entra/identity-platform/consent-types-developer — "Admin Consent for
|
|
542
|
+
* Application permissions" and "Admin Consent for Delegated Permissions", fetched 2026-09-27.
|
|
543
|
+
*
|
|
544
|
+
* Never put in a re-authorization request a person is sent to: an operation needing one is refused with the
|
|
545
|
+
* statement that an administrator must grant it, which is the only thing the person can act on.
|
|
546
|
+
*/
|
|
547
|
+
readonly administratorConsentScopes?: readonly string[];
|
|
247
548
|
/**
|
|
248
549
|
* Env-var names resolving the APPLICATION's registration with the vendor. These are the two values
|
|
249
550
|
* an operator genuinely does paste — they identify the integration, not the end user — and they
|
|
@@ -252,12 +553,13 @@ export interface ProviderRestOAuth2 {
|
|
|
252
553
|
readonly clientIdSecretRef?: string;
|
|
253
554
|
readonly clientSecretSecretRef?: string;
|
|
254
555
|
/**
|
|
255
|
-
* Placeholders the endpoints above carry, which a CONNECTION must
|
|
256
|
-
* URL at all — `{cloud}`, `{tenantId}`, `{region}`, `{domain}`.
|
|
556
|
+
* Placeholders the endpoints above carry, which a CONNECTION must answer before either URL is a
|
|
557
|
+
* URL at all — `{cloud}`, `{tenantId}`, `{region}`, `{domain}`. A PATH placeholder is answered freely;
|
|
558
|
+
* a HOST or ORIGIN one only from {@link ProviderRestOAuth2.endpointHostAllowances}.
|
|
257
559
|
*
|
|
258
|
-
*
|
|
259
|
-
* endpoints, **
|
|
260
|
-
* `{tenantId}` (6). Microsoft's authorize URL is
|
|
560
|
+
* Re-measured over the generated catalogue on 2026-09-12: of the 99 vendors that recovered both
|
|
561
|
+
* endpoints, **20 carry at least one placeholder**, led by `{cloud}` (18 occurrences), `{location}`
|
|
562
|
+
* (10) and `{tenantId}` (6). Microsoft's authorize URL is
|
|
261
563
|
* `https://{cloud}/{tenantId}/oauth2/v2.0/authorize`, and no amount of application configuration
|
|
262
564
|
* supplies those: which Azure cloud and which directory are facts about the ACCOUNT BEING
|
|
263
565
|
* CONNECTED, known only to the person connecting it.
|
|
@@ -274,9 +576,554 @@ export interface ProviderRestOAuth2 {
|
|
|
274
576
|
* Deliberately NOT a judgement about which connections a provider admits. That is a recorded
|
|
275
577
|
* decision in the catalogue record and is never read from a vendor's API surface; this is a
|
|
276
578
|
* mechanical fact about a string.
|
|
579
|
+
*
|
|
580
|
+
* A placeholder filled from {@link ProviderOAuth2EndpointHostAllowanceKind.DEPLOYMENT_BASE_URL} or
|
|
581
|
+
* {@link ProviderOAuth2EndpointHostAllowanceKind.DEPLOYMENT_CONFIGURED} is NOT listed: nobody
|
|
582
|
+
* connecting an account is asked for it, and a value they supply is refused.
|
|
583
|
+
*
|
|
584
|
+
* Derived from the {@link scopes} as well as the endpoints (#991): Dataverse's `{hostUrl}/.default`
|
|
585
|
+
* names a fact about the account (its environment URL) that no endpoint carries, and a placeholder
|
|
586
|
+
* only a scope mentions was otherwise never asked for — so the authorize request carried the literal
|
|
587
|
+
* brace to the vendor.
|
|
277
588
|
*/
|
|
278
589
|
readonly connectionParameters?: readonly string[];
|
|
590
|
+
/**
|
|
591
|
+
* How each placeholder that forms an endpoint's HOST or ORIGIN may be filled, keyed by
|
|
592
|
+
* placeholder name. A host/origin placeholder with NO entry here cannot be filled at all, and the
|
|
593
|
+
* connect flow refuses it.
|
|
594
|
+
*
|
|
595
|
+
* ## Why this exists: the token endpoint receives the application's client secret
|
|
596
|
+
*
|
|
597
|
+
* The exchange POSTs `client_id` and the APPLICATION's `client_secret` to the resolved `tokenUrl`.
|
|
598
|
+
* Until 2026-09-12 every placeholder was answered from the connect-start request body, so where a
|
|
599
|
+
* placeholder formed the HOST — Microsoft's `https://{cloud}/…/token`, Salesforce's
|
|
600
|
+
* `https://{environment}/…`, Zoho's `https://accounts.{location}/…` — any user allowed to start a
|
|
601
|
+
* connection chose where that secret was sent (`cloud=attacker.tld`). Measured over the generated
|
|
602
|
+
* catalogue that day: 34 host-position occurrences across 17 vendors (9 Microsoft, 6 Zoho,
|
|
603
|
+
* Salesforce, Workday), plus two whole-origin
|
|
604
|
+
* placeholders (`gitea`, `bigin-by-zoho`) that were safe only because percent-encoding made them
|
|
605
|
+
* undialable (#623).
|
|
606
|
+
*
|
|
607
|
+
* So a host/origin value is chosen from what the PROVIDER declares, never from what a caller
|
|
608
|
+
* types. Path and query placeholders (`{tenantId}`, Workday's `{tenant}`) stay free and are
|
|
609
|
+
* percent-encoded, because an encoded path segment cannot move the request to another host.
|
|
610
|
+
*
|
|
611
|
+
* Derived by the transposer where the source catalogue states a closed choice (a dropdown), and
|
|
612
|
+
* otherwise taken from a recorded human decision in the catalogue record — never guessed.
|
|
613
|
+
*
|
|
614
|
+
* Also governs a placeholder that forms the ORIGIN of a SCOPE (`{hostUrl}/.default`, #991). One
|
|
615
|
+
* allow-list per placeholder name, wherever the name forms a host: a scope's resource URI decides
|
|
616
|
+
* which host the vendor mints the token for, which is the same question an endpoint's host answers.
|
|
617
|
+
*/
|
|
618
|
+
readonly endpointHostAllowances?: Readonly<Record<string, ProviderOAuth2EndpointHostAllowance>>;
|
|
619
|
+
/**
|
|
620
|
+
* The closed set of values a WHOLE-SCOPE placeholder (`scopes: ['{permissions}']`) may be answered
|
|
621
|
+
* with, keyed by placeholder name (#991).
|
|
622
|
+
*
|
|
623
|
+
* Each value is substituted verbatim as that scope entry and may hold several space-delimited scope
|
|
624
|
+
* tokens — Microsoft Entra ID's permission presets are one such string each. Matched exactly, because
|
|
625
|
+
* scope values are case-sensitive.
|
|
626
|
+
* spec-cite: RFC 6749 §3.3 (the scope parameter is a list of space-delimited, case-sensitive strings).
|
|
627
|
+
*
|
|
628
|
+
* Closed rather than free because a scope value can itself name a resource
|
|
629
|
+
* (`https://graph.microsoft.com/.default`), and a whole-scope placeholder with NO entry here cannot be
|
|
630
|
+
* answered at all. Derived by the transposer from the source's own dropdown; never guessed.
|
|
631
|
+
*/
|
|
632
|
+
readonly scopeValueAllowances?: Readonly<Record<string, ProviderOAuth2ScopeValueAllowance>>;
|
|
633
|
+
/**
|
|
634
|
+
* Dot-path, inside the TOKEN ENDPOINT'S JSON answer, to the vendor's own identifier for the account
|
|
635
|
+
* that was just authorized — Slack's `team.id` (#333).
|
|
636
|
+
*
|
|
637
|
+
* Read once, at the exchange, into the stored grant's `accountRef`, so a person holding two
|
|
638
|
+
* connections to one vendor can tell which is which and the grant audit names the account. Absent
|
|
639
|
+
* means the vendor reports no account identity in its token answer (Asana's names only the user).
|
|
640
|
+
*
|
|
641
|
+
* Identity only: nothing looks a credential up by it, and an answer that lacks the path stores the
|
|
642
|
+
* grant without it — a missing label is not a reason to discard an authorization the person just gave.
|
|
643
|
+
*/
|
|
644
|
+
readonly accountRefResponsePath?: string;
|
|
645
|
+
/**
|
|
646
|
+
* Facts about the connected ACCOUNT that the vendor reports in its token answer and OPERATIONS consume,
|
|
647
|
+
* keyed by the fact's name (#333, #1047) — Salesforce's `instance_url`, Pipedrive's `api_domain`.
|
|
648
|
+
*
|
|
649
|
+
* Captured at the authorization-code exchange and again at every refresh, into the credential row's
|
|
650
|
+
* `connectionFacts` with provenance `REPORTED_BY_VENDOR`. Distinct from {@link accountRefResponsePath}:
|
|
651
|
+
* that is a LABEL a person reads, sealed in the grant; these are values requests are built from, kept
|
|
652
|
+
* client-readable beside the answers a person typed at connect.
|
|
653
|
+
*
|
|
654
|
+
* A fact that carries an `origin` allowance is an https ORIGIN operations may dial, so it decides where a
|
|
655
|
+
* request carrying the credential goes. It is therefore admitted exactly as an ORIGIN placeholder is
|
|
656
|
+
* ({@link endpointHostAllowances}): refused at connect when absent or outside the allowance, and
|
|
657
|
+
* re-checked on every call that uses it.
|
|
658
|
+
*/
|
|
659
|
+
readonly reportedAccountFacts?: Readonly<Record<string, ProviderOAuth2ReportedAccountFact>>;
|
|
660
|
+
/**
|
|
661
|
+
* The RESOURCE AUDIENCES one grant of this client can mint tokens for, keyed by audience id (#1725), and the
|
|
662
|
+
* HOSTS a token of each may be sent to.
|
|
663
|
+
*
|
|
664
|
+
* ## Why a grant needs more than one access token
|
|
665
|
+
*
|
|
666
|
+
* Microsoft issues an access token for ONE resource per request: the token redemption's scopes "must all be from a
|
|
667
|
+
* single resource", and a refresh request spanning several resources returns "a token for the resource specified
|
|
668
|
+
* in the first scope". A refresh token, though, is not tied to a resource: "A client can use a refresh token to
|
|
669
|
+
* acquire access tokens across any combination of resource and tenant where it has permission to do so." So a
|
|
670
|
+
* connection spanning Graph and Dataverse keeps ONE refresh token and mints a token per audience from it.
|
|
671
|
+
* spec-cite: https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow — the `scope` rows
|
|
672
|
+
* of "Redeem a code for an access token" and "Refresh the access token"; and
|
|
673
|
+
* https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens — the opening section. Fetched 2026-09-27.
|
|
674
|
+
*
|
|
675
|
+
* ## Why the HOSTS are part of the declaration
|
|
676
|
+
*
|
|
677
|
+
* An access token is a bearer credential for its resource. Declaring which hosts receive a token of each audience is
|
|
678
|
+
* what lets the engine attach a token only to a request bound for its own resource (the token-host binding), so an
|
|
679
|
+
* operation — the base's own, a member's, or an augmentation's (#1672) — can never carry a Graph token to another
|
|
680
|
+
* host.
|
|
681
|
+
*
|
|
682
|
+
* Absent means the provider mints one token for one resource, which is every provider except a connection base that
|
|
683
|
+
* spans several. When present, {@link primaryAudience} names the audience the AUTHORIZATION grants — the resource of
|
|
684
|
+
* the connect request's scopes — whose token is the one stored with the grant; every other audience's token is minted
|
|
685
|
+
* from the refresh token on demand and cached, never stored.
|
|
686
|
+
*/
|
|
687
|
+
readonly audiences?: Readonly<Record<string, ProviderRestOAuth2Audience>>;
|
|
688
|
+
/** The audience the authorization itself grants — see {@link audiences}. Required when `audiences` is declared. */
|
|
689
|
+
readonly primaryAudience?: string;
|
|
690
|
+
}
|
|
691
|
+
/** @wildo_source:part:start external-connectors.models.oauth2-audiences facet:layer:shared facet:family:provider facet:audience:app-developer */
|
|
692
|
+
/** The placeholder a {@link ProviderRestOAuth2Audience.mintScopes} entry uses for the https origin of the request's host. */
|
|
693
|
+
export declare const PROVIDER_OAUTH2_AUDIENCE_ORIGIN_PLACEHOLDER = "{audienceOrigin}";
|
|
694
|
+
/** One resource audience of a connection's grant. See {@link ProviderRestOAuth2.audiences}. */
|
|
695
|
+
export interface ProviderRestOAuth2Audience {
|
|
696
|
+
/**
|
|
697
|
+
* The hosts a token of this audience may be sent to: exact lower-case hosts, and dotted suffixes admitting
|
|
698
|
+
* `<dns-labels><suffix>` (never the bare suffix). Every host is https.
|
|
699
|
+
*/
|
|
700
|
+
readonly hosts: {
|
|
701
|
+
readonly values: readonly string[];
|
|
702
|
+
readonly hostSuffixes?: readonly string[];
|
|
703
|
+
};
|
|
704
|
+
/**
|
|
705
|
+
* The scopes that mint a token for this audience, all of ONE resource. An entry may begin with
|
|
706
|
+
* {@link PROVIDER_OAUTH2_AUDIENCE_ORIGIN_PLACEHOLDER} — the https origin of the request's own host, admitted by
|
|
707
|
+
* {@link hosts} first — for a resource named per customer (a Dataverse environment's `<environment-url>/.default`).
|
|
708
|
+
*/
|
|
709
|
+
readonly mintScopes: readonly string[];
|
|
710
|
+
/**
|
|
711
|
+
* The vendor's published limits for THIS resource (#1729) — see {@link ProviderRateBudgetDeclaration}. Per audience
|
|
712
|
+
* because each resource is metered by its own service: Graph's limits say nothing about a Dataverse environment.
|
|
713
|
+
*/
|
|
714
|
+
readonly rateBudgets?: readonly ProviderRateBudgetDeclaration[];
|
|
715
|
+
}
|
|
716
|
+
/** The audience a request host belongs to, with its id — see {@link selectProviderOAuth2AudienceForHost}. */
|
|
717
|
+
export interface ProviderOAuth2AudienceSelection {
|
|
718
|
+
readonly id: string;
|
|
719
|
+
readonly audience: ProviderRestOAuth2Audience;
|
|
720
|
+
readonly primary: boolean;
|
|
279
721
|
}
|
|
722
|
+
/** Whether `host` is admitted by one audience's hosts: an exact value, or `<labels><suffix>` for a declared suffix. */
|
|
723
|
+
export declare function providerOAuth2AudienceAdmitsHost(audience: ProviderRestOAuth2Audience, host: string): boolean;
|
|
724
|
+
/**
|
|
725
|
+
* The ONE audience whose hosts admit `host`, or `undefined` when none does. The declaration guarantees at most one
|
|
726
|
+
* ({@link describeProviderOAuth2AudienceDefects} refuses overlapping hosts), so the answer never depends on order.
|
|
727
|
+
*/
|
|
728
|
+
export declare function selectProviderOAuth2AudienceForHost(oauth2: Pick<ProviderRestOAuth2, 'audiences' | 'primaryAudience'>, host: string): ProviderOAuth2AudienceSelection | undefined;
|
|
729
|
+
/**
|
|
730
|
+
* The scopes that mint `audience`'s token for a request to `origin` — `{audienceOrigin}` replaced by that origin.
|
|
731
|
+
* The caller has already admitted the origin's host through {@link selectProviderOAuth2AudienceForHost}; nothing a
|
|
732
|
+
* caller supplies reaches a scope any other way.
|
|
733
|
+
*/
|
|
734
|
+
export declare function resolveProviderOAuth2AudienceMintScopes(audience: ProviderRestOAuth2Audience, origin: string): readonly string[];
|
|
735
|
+
/**
|
|
736
|
+
* The declared audience a scope a connection might REQUEST names (#1671): its id, or `undefined` when the scope names a
|
|
737
|
+
* resource none of the connection's audiences covers. Only meaningful when `audiences` is declared.
|
|
738
|
+
*
|
|
739
|
+
* A scope that is an absolute URI names the resource a token is minted for (`https://graph.microsoft.com/Mail.Send`,
|
|
740
|
+
* `https://contoso.crm.dynamics.com/user_impersonation`): the resource of one of an audience's fixed mint scopes (the
|
|
741
|
+
* mint scope without its last path segment), or — for a mint scope beginning `{audienceOrigin}` — an https origin whose
|
|
742
|
+
* host the audience admits. A scope that is not an absolute URI (`offline_access`, `Files.Read`) names no resource, and
|
|
743
|
+
* the vendor reads it against its default one, the PRIMARY audience: "omitting the resource identifier in the `scope`
|
|
744
|
+
* parameter defaults to Microsoft Graph".
|
|
745
|
+
* spec-cite: https://learn.microsoft.com/en-us/entra/identity-platform/consent-types-developer — the note under
|
|
746
|
+
* "Admin Consent for Delegated Permissions", fetched 2026-09-27.
|
|
747
|
+
*/
|
|
748
|
+
export declare function findProviderOAuth2ScopeAudience(oauth2: Pick<ProviderRestOAuth2, 'audiences' | 'primaryAudience'>, scope: string): string | undefined;
|
|
749
|
+
/**
|
|
750
|
+
* Whether a scope a connection might REQUEST names a resource among the connection's declared audiences (#1671) — see
|
|
751
|
+
* {@link findProviderOAuth2ScopeAudience}. Asking a person to consent to it is asking them to let this application reach
|
|
752
|
+
* THAT resource, so a scope from a member ref or an opted-in augmentation (#1672) is requestable only when it names one
|
|
753
|
+
* the connection declares. `true` without looking further when the connection declares no audiences: it mints one token
|
|
754
|
+
* for one resource and its scopes are the vendor's own vocabulary (Google's scope URIs name permissions, not resources).
|
|
755
|
+
*/
|
|
756
|
+
export declare function providerOAuth2ScopeNamesDeclaredAudience(oauth2: Pick<ProviderRestOAuth2, 'audiences' | 'primaryAudience'>, scope: string): boolean;
|
|
757
|
+
/** @wildo_source:part:end external-connectors.models.oauth2-audiences */
|
|
758
|
+
/** @wildo_source:part:start external-connectors.models.oauth2-incremental-consent facet:layer:shared facet:family:provider facet:audience:app-developer */
|
|
759
|
+
/**
|
|
760
|
+
* How a vendor extends an existing delegated grant with more scopes — see {@link ProviderRestOAuth2.incrementalConsent}.
|
|
761
|
+
*/
|
|
762
|
+
export declare enum ProviderOAuth2IncrementalConsentForm {
|
|
763
|
+
/**
|
|
764
|
+
* The vendor keeps what the person already consented to, and a new authorization request naming only the added
|
|
765
|
+
* scopes ADDS them: Microsoft's delegated permissions "will be granted in addition to any other permissions that
|
|
766
|
+
* were already granted for that app (i.e. incremental)".
|
|
767
|
+
* spec-cite: https://learn.microsoft.com/en-us/entra/identity-platform/consent-types-developer — "Incremental and
|
|
768
|
+
* dynamic user consent", fetched 2026-09-27.
|
|
769
|
+
*/
|
|
770
|
+
ADDITIVE = "ADDITIVE",
|
|
771
|
+
/**
|
|
772
|
+
* The vendor adds the new scopes to the existing grant only when the request carries `include_granted_scopes=true`:
|
|
773
|
+
* "If you set this parameter's value to `true` and the authorization request is granted, then the new access token
|
|
774
|
+
* will also cover any scopes to which the user previously granted the application access."
|
|
775
|
+
* spec-cite: https://developers.google.com/identity/protocols/oauth2/web-server#incrementalAuth — the
|
|
776
|
+
* `include_granted_scopes` parameter row, fetched 2026-09-27.
|
|
777
|
+
*/
|
|
778
|
+
INCLUDE_GRANTED_SCOPES = "INCLUDE_GRANTED_SCOPES"
|
|
779
|
+
}
|
|
780
|
+
/** A vendor's incremental-consent behaviour. See {@link ProviderRestOAuth2.incrementalConsent}. */
|
|
781
|
+
export interface ProviderOAuth2IncrementalConsent {
|
|
782
|
+
readonly form: ProviderOAuth2IncrementalConsentForm;
|
|
783
|
+
/**
|
|
784
|
+
* Scopes every re-authorization request repeats beside the added ones — protocol scopes the answer needs rather than
|
|
785
|
+
* permissions (Microsoft's `offline_access`, without which a code redemption returns no refresh token). Each must be
|
|
786
|
+
* among the provider's declared {@link ProviderRestOAuth2.scopes}.
|
|
787
|
+
*/
|
|
788
|
+
readonly restatedScopes?: readonly string[];
|
|
789
|
+
}
|
|
790
|
+
/** @wildo_source:part:end external-connectors.models.oauth2-incremental-consent */
|
|
791
|
+
/**
|
|
792
|
+
* Why a declared audience set cannot be honoured, or an empty list. Checked where a provider is composed, so a
|
|
793
|
+
* malformed declaration never reaches a request:
|
|
794
|
+
* - `primaryAudience` names no declared audience, or is missing while audiences are declared;
|
|
795
|
+
* - the primary audience's mint scopes are not among the authorization's own `scopes` (its token is the one the
|
|
796
|
+
* code redemption returns, so the authorization must be FOR that resource) or use the origin placeholder;
|
|
797
|
+
* - an audience with no host or no mint scope, a suffix without a leading dot, an origin placeholder anywhere but
|
|
798
|
+
* the start of a scope, or any other `{placeholder}` in a mint scope;
|
|
799
|
+
* - two audiences admitting one host — a request host must select exactly one token.
|
|
800
|
+
*/
|
|
801
|
+
export declare function describeProviderOAuth2AudienceDefects(oauth2: Pick<ProviderRestOAuth2, 'audiences' | 'primaryAudience' | 'scopes'>): string[];
|
|
802
|
+
/** One vendor-reported account fact. See {@link ProviderRestOAuth2.reportedAccountFacts}. */
|
|
803
|
+
export interface ProviderOAuth2ReportedAccountFact {
|
|
804
|
+
/** Dot-path inside the token endpoint's JSON answer. A string or finite number there is the value. */
|
|
805
|
+
readonly responsePath: string;
|
|
806
|
+
/**
|
|
807
|
+
* Present when the value is an https ORIGIN operations dial. The same DECLARED_VALUES vocabulary an
|
|
808
|
+
* ORIGIN placeholder uses: exact origins, or `https://<dns-labels><suffix>` for each dotted suffix.
|
|
809
|
+
*/
|
|
810
|
+
readonly origin?: Extract<ProviderOAuth2EndpointHostAllowance, {
|
|
811
|
+
readonly kind: ProviderOAuth2EndpointHostAllowanceKind.DECLARED_VALUES;
|
|
812
|
+
}>;
|
|
813
|
+
}
|
|
814
|
+
/** The values a whole-scope placeholder admits. See {@link ProviderRestOAuth2.scopeValueAllowances}. */
|
|
815
|
+
export interface ProviderOAuth2ScopeValueAllowance {
|
|
816
|
+
readonly values: readonly string[];
|
|
817
|
+
/** Values the vendor's source offers and the catalogue declines, matched exactly. See {@link ProviderOAuth2UnsupportedValues}. */
|
|
818
|
+
readonly unsupportedValues?: ProviderOAuth2UnsupportedValues;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Values the vendor's OWN source offers for a placeholder and this catalogue deliberately does NOT admit, each mapped
|
|
822
|
+
* to the reason (#1937). Keyed by the value as the source writes it.
|
|
823
|
+
*
|
|
824
|
+
* Not a second allow-list: the admitted set (`values`, `hosts`) alone decides what is admitted, and a value found
|
|
825
|
+
* here is refused exactly as any other unadmitted value is (`VALUE_NOT_ADMITTED`, which also retires a STORED
|
|
826
|
+
* connection that carries it). What this adds is the WHY. A person who chose, or a connection that stored, a value
|
|
827
|
+
* the catalogue declined is told that it is unsupported and for what reason, instead of being handed a list of other
|
|
828
|
+
* values as though they had made a typing mistake.
|
|
829
|
+
*
|
|
830
|
+
* The first entry is the US Government Microsoft clouds (`login.microsoftonline.us`, GCC High and DoD), declared out
|
|
831
|
+
* of scope by product decision on 2026-09-29. The transposer writes these from the vendor record's declared
|
|
832
|
+
* exclusion (`_code_archive/providers/catalogue-record/vendors/<vendor>.json` since #2019 archived the catalogue; a
|
|
833
|
+
* registry provider adopted from it states them in its own module under `providers-registry/<ref>/`), so reversing a
|
|
834
|
+
* decision is record edits and a regeneration, never an engine change. How many edits depends on what the decline also removed: for the US
|
|
835
|
+
* Government clouds, three (the catalogue README lists them).
|
|
836
|
+
*/
|
|
837
|
+
export type ProviderOAuth2UnsupportedValues = Readonly<Record<string, string>>;
|
|
838
|
+
/** How a placeholder's value is compared with a declared one. */
|
|
839
|
+
export declare enum ProviderOAuth2ValueComparison {
|
|
840
|
+
/** A host or an origin: DNS names compare case-insensitively. spec-cite: RFC 4343 §2. */
|
|
841
|
+
CASE_INSENSITIVE = "CASE_INSENSITIVE",
|
|
842
|
+
/** A scope value: scope strings are case-sensitive. spec-cite: RFC 6749 §3.3. */
|
|
843
|
+
EXACT = "EXACT"
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* The declared reason a value is unsupported, or `undefined` when it is not declared unsupported. Compared as the
|
|
847
|
+
* placeholder's values are: case-insensitively for a host or origin, exactly for a scope value.
|
|
848
|
+
*/
|
|
849
|
+
export declare function findProviderOAuth2UnsupportedValueReason(unsupportedValues: ProviderOAuth2UnsupportedValues | undefined, value: string, comparison: ProviderOAuth2ValueComparison): string | undefined;
|
|
850
|
+
/**
|
|
851
|
+
* Where a `{placeholder}` sits inside one OAuth2 SCOPE entry. See {@link classifyProviderOAuth2ScopePlaceholder}.
|
|
852
|
+
*
|
|
853
|
+
* Deliberately not {@link ProviderOAuth2EndpointPlaceholderPosition}: a scope is not a URL, and the
|
|
854
|
+
* endpoint scan would call `{permissions}` an ORIGIN (it begins the string and nothing follows it).
|
|
855
|
+
*/
|
|
856
|
+
export declare enum ProviderOAuth2ScopePlaceholderPosition {
|
|
857
|
+
/**
|
|
858
|
+
* The placeholder begins the scope and is followed by `/` — `{hostUrl}/.default`. Its value is an
|
|
859
|
+
* https ORIGIN, admitted only within {@link ProviderRestOAuth2.endpointHostAllowances}.
|
|
860
|
+
*/
|
|
861
|
+
ORIGIN = "ORIGIN",
|
|
862
|
+
/**
|
|
863
|
+
* The placeholder is the entire scope entry — `{permissions}`. Its value is one of
|
|
864
|
+
* {@link ProviderRestOAuth2.scopeValueAllowances}.
|
|
865
|
+
*/
|
|
866
|
+
WHOLE_SCOPE = "WHOLE_SCOPE"
|
|
867
|
+
}
|
|
868
|
+
/** Why a scope entry cannot be resolved safely, whatever is supplied. See {@link findProviderOAuth2ScopeTemplateDefects}. */
|
|
869
|
+
export declare enum ProviderOAuth2ScopeTemplateDefect {
|
|
870
|
+
/**
|
|
871
|
+
* A placeholder sits anywhere except at the start of the scope followed by `/` or the end —
|
|
872
|
+
* `api://{x}/.default`, `{x}.default`, `openid {x}`. Such a value would be concatenated with text
|
|
873
|
+
* around it into a resource or scope nobody declared.
|
|
874
|
+
*/
|
|
875
|
+
UNSUPPORTED_PLACEHOLDER_POSITION = "UNSUPPORTED_PLACEHOLDER_POSITION",
|
|
876
|
+
/** More than one placeholder in one scope entry: each value validated alone can compose an undeclared one. */
|
|
877
|
+
SEVERAL_PLACEHOLDERS = "SEVERAL_PLACEHOLDERS",
|
|
878
|
+
/** A `{` or `}` that is not part of a well-formed placeholder: a vendor can never grant a scope carrying one. */
|
|
879
|
+
STRAY_BRACE = "STRAY_BRACE"
|
|
880
|
+
}
|
|
881
|
+
/** One `{placeholder}` in a scope entry. */
|
|
882
|
+
export interface ProviderOAuth2ScopePlaceholderOccurrence {
|
|
883
|
+
readonly name: string;
|
|
884
|
+
readonly position: ProviderOAuth2ScopePlaceholderPosition;
|
|
885
|
+
/** The whole `{name}` token, for substitution. */
|
|
886
|
+
readonly token: string;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* The defects that make a scope entry unsafe to resolve, or an empty list. Shared by the runtime (which
|
|
890
|
+
* refuses) and the transposer (which refuses to emit), so neither admits a shape the other rejects.
|
|
891
|
+
*/
|
|
892
|
+
export declare function findProviderOAuth2ScopeTemplateDefects(scope: string): readonly ProviderOAuth2ScopeTemplateDefect[];
|
|
893
|
+
/**
|
|
894
|
+
* Classify the one placeholder a scope entry may carry, or `undefined` when it carries none.
|
|
895
|
+
*
|
|
896
|
+
* ONE function, shared by the transposer and the runtime, like its endpoint counterpart. Meaningful only
|
|
897
|
+
* for a scope with no {@link findProviderOAuth2ScopeTemplateDefects}; every consumer refuses those first.
|
|
898
|
+
*/
|
|
899
|
+
export declare function classifyProviderOAuth2ScopePlaceholder(scope: string): ProviderOAuth2ScopePlaceholderOccurrence | undefined;
|
|
900
|
+
/**
|
|
901
|
+
* Why a declared whole-scope allowance cannot be applied, or an empty list. Enforced at generation so a
|
|
902
|
+
* broken set refuses the batch; the runtime matches exactly and never trusts a value it did not declare.
|
|
903
|
+
*/
|
|
904
|
+
export declare function describeProviderOAuth2ScopeValueAllowanceDefects(allowance: ProviderOAuth2ScopeValueAllowance): readonly string[];
|
|
905
|
+
/**
|
|
906
|
+
* Where a `{placeholder}` sits inside an OAuth2 endpoint template — the classification the host
|
|
907
|
+
* allow-list keys on. See {@link classifyProviderOAuth2EndpointPlaceholders}.
|
|
908
|
+
*/
|
|
909
|
+
export declare enum ProviderOAuth2EndpointPlaceholderPosition {
|
|
910
|
+
/**
|
|
911
|
+
* The placeholder BEGINS the template, so its value is the whole scheme + authority (+ any path
|
|
912
|
+
* prefix): `{baseUrl}/login/oauth/authorize`. The most dangerous position — the value names the
|
|
913
|
+
* scheme as well as the host.
|
|
914
|
+
*/
|
|
915
|
+
ORIGIN = "ORIGIN",
|
|
916
|
+
/**
|
|
917
|
+
* The placeholder sits between `://` and the first `/`, `?` or `#` after it:
|
|
918
|
+
* `https://{cloud}/common/…`, `https://accounts.{location}/…`. Its value decides which host
|
|
919
|
+
* receives the request.
|
|
920
|
+
*/
|
|
921
|
+
HOST = "HOST",
|
|
922
|
+
/** Anywhere after the authority. Percent-encoded, so it cannot move the request to another host. */
|
|
923
|
+
PATH_OR_QUERY = "PATH_OR_QUERY"
|
|
924
|
+
}
|
|
925
|
+
/** How a host- or origin-forming placeholder may be filled. See {@link ProviderRestOAuth2.endpointHostAllowances}. */
|
|
926
|
+
export declare enum ProviderOAuth2EndpointHostAllowanceKind {
|
|
927
|
+
/**
|
|
928
|
+
* Chosen at connect time from values the provider DECLARES — Microsoft's commercial login cloud,
|
|
929
|
+
* Zoho's data centres. A value outside the set is refused; a declared value is substituted
|
|
930
|
+
* verbatim (it is a hostname or origin, not a path segment to encode).
|
|
931
|
+
*
|
|
932
|
+
* `hostSuffixes` additionally admits `<dns-label>(.<dns-label>)*` followed by one of the suffixes,
|
|
933
|
+
* for a vendor whose per-customer host is a subdomain it controls (Salesforce My Domain,
|
|
934
|
+
* `<name>.my.salesforce.com`). On a HOST placeholder a suffix applies only when the placeholder is
|
|
935
|
+
* the WHOLE host. On an ORIGIN placeholder it admits exactly `https://<dns-labels><suffix>` — the
|
|
936
|
+
* scheme fixed to https, no port, path, userinfo, query or fragment — which is what a per-customer
|
|
937
|
+
* origin such as a Dataverse environment URL (`https://<org>.crm4.dynamics.com`, #991) needs.
|
|
938
|
+
*/
|
|
939
|
+
DECLARED_VALUES = "DECLARED_VALUES",
|
|
940
|
+
/**
|
|
941
|
+
* Filled from the DEPLOYMENT's configured base URL for this provider — the REST contract's
|
|
942
|
+
* `baseUrl`, or the environment variable its `baseUrlEnvVarName` names. For a self-hosted vendor
|
|
943
|
+
* (Gitea) the operator names the instance once; a person connecting an account is never asked, and
|
|
944
|
+
* a value they supply is refused. Admitted in the ORIGIN position only.
|
|
945
|
+
*/
|
|
946
|
+
DEPLOYMENT_BASE_URL = "DEPLOYMENT_BASE_URL",
|
|
947
|
+
/**
|
|
948
|
+
* Filled from a deployment environment variable dedicated to THIS placeholder, named by the
|
|
949
|
+
* allowance's `envVarName` (#992). For a vendor whose endpoints sit on more than one per-tenant host
|
|
950
|
+
* — Workday's `https://{authHost}/…/authorize` and `https://{apiHost}/…/token` — one base URL cannot
|
|
951
|
+
* name both, so each host is its own operator setting (`WORKDAY_AUTH_HOST`, `WORKDAY_API_HOST`).
|
|
952
|
+
*
|
|
953
|
+
* Like DEPLOYMENT_BASE_URL, never asked of a person connecting, and a value they supply is refused.
|
|
954
|
+
* The trade is explicit: the application serves the one vendor tenant (data centre) the operator
|
|
955
|
+
* names. Admitted on a HOST placeholder that is the whole host, with a DNS-name value.
|
|
956
|
+
*/
|
|
957
|
+
DEPLOYMENT_CONFIGURED = "DEPLOYMENT_CONFIGURED",
|
|
958
|
+
/**
|
|
959
|
+
* DERIVED from another connect-time answer through a declared table (#1937): Microsoft Graph's operation host follows
|
|
960
|
+
* the login cloud the connection chose (`login.microsoftonline.com` → `graph.microsoft.com`), because a token minted by
|
|
961
|
+
* one national cloud is not accepted by another. The Microsoft source also maps the US Government login cloud; that
|
|
962
|
+
* row is declined (`unsupportedValues`), because US Government clouds are out of scope (decision of 2026-09-29).
|
|
963
|
+
*
|
|
964
|
+
* Never asked of a person and never taken from a request: the value is looked up from the ANSWER the connection
|
|
965
|
+
* stored for `answer`, and an answer the table does not name is refused. `whenUnanswered` is the host for a connection
|
|
966
|
+
* that answered nothing (the application's own pasted credential); absent, such a connection is refused. The table is
|
|
967
|
+
* the allow-list, so no other host can result. Admitted on a HOST placeholder that is the whole host.
|
|
968
|
+
*
|
|
969
|
+
* spec-cite: https://learn.microsoft.com/en-us/graph/deployments — "Access tokens acquired for a national cloud
|
|
970
|
+
* deployment are not interchangeable with those acquired for the global service or any other national cloud."
|
|
971
|
+
*/
|
|
972
|
+
DERIVED_FROM_CONNECTION_ANSWER = "DERIVED_FROM_CONNECTION_ANSWER"
|
|
973
|
+
}
|
|
974
|
+
export type ProviderOAuth2EndpointHostAllowance = {
|
|
975
|
+
readonly kind: ProviderOAuth2EndpointHostAllowanceKind.DECLARED_VALUES;
|
|
976
|
+
/** Exact admitted values, compared case-insensitively. A hostname for HOST, an origin for ORIGIN. */
|
|
977
|
+
readonly values: readonly string[];
|
|
978
|
+
/**
|
|
979
|
+
* A whole HOST, or an ORIGIN (admitting `https://<labels><suffix>` only). Each MUST begin with `.`,
|
|
980
|
+
* so `evilmy.salesforce.com` never matches `.my.salesforce.com`.
|
|
981
|
+
*/
|
|
982
|
+
readonly hostSuffixes?: readonly string[];
|
|
983
|
+
/**
|
|
984
|
+
* ORIGIN only: an admitted value must ALSO have the same origin as the deployment's configured base
|
|
985
|
+
* URL (the REST contract's `baseUrl` / `baseUrlEnvVarName`) (#991 review F4).
|
|
986
|
+
*
|
|
987
|
+
* For a vendor whose token is minted for a per-customer environment and whose operations dial the
|
|
988
|
+
* operator's configured instance — Dataverse mints for the scope's `{hostUrl}` while operations go to
|
|
989
|
+
* `MICROSOFT_DYNAMICS_CRM_BASE_URL` — a connection naming another environment would hold a token that
|
|
990
|
+
* fails every call. So the two are required to agree at connect, and an unset base URL refuses the
|
|
991
|
+
* connect as a deployment gap rather than minting a token nothing can use.
|
|
992
|
+
*/
|
|
993
|
+
readonly originMatchesDeploymentBaseUrl?: true;
|
|
994
|
+
/** Values the vendor's source offers and the catalogue declines, with the reason. See {@link ProviderOAuth2UnsupportedValues}. */
|
|
995
|
+
readonly unsupportedValues?: ProviderOAuth2UnsupportedValues;
|
|
996
|
+
} | {
|
|
997
|
+
readonly kind: ProviderOAuth2EndpointHostAllowanceKind.DEPLOYMENT_CONFIGURED;
|
|
998
|
+
/**
|
|
999
|
+
* The deployment environment variable holding the host — derived by the transposer as
|
|
1000
|
+
* `<PROVIDER_ENV_PREFIX>_<PLACEHOLDER_IN_SCREAMING_SNAKE>` and declared as a REGISTRATION secret slot,
|
|
1001
|
+
* without which the runtime environment builder would strip it.
|
|
1002
|
+
*/
|
|
1003
|
+
readonly envVarName: string;
|
|
1004
|
+
} | {
|
|
1005
|
+
readonly kind: ProviderOAuth2EndpointHostAllowanceKind.DERIVED_FROM_CONNECTION_ANSWER;
|
|
1006
|
+
/** The connect-time answer the host is derived from (`cloud`) — itself a placeholder of the same provider. */
|
|
1007
|
+
readonly answer: string;
|
|
1008
|
+
/** Per answer value (compared case-insensitively), the host it selects. DNS names. */
|
|
1009
|
+
readonly hosts: Readonly<Record<string, string>>;
|
|
1010
|
+
/** The host when the connection answered nothing; absent, an unanswered connection is refused. */
|
|
1011
|
+
readonly whenUnanswered?: string;
|
|
1012
|
+
/**
|
|
1013
|
+
* ANSWER values the vendor's source maps to a host and the catalogue declines, with the reason — so a stored
|
|
1014
|
+
* connection carrying one is refused at use naming why. See {@link ProviderOAuth2UnsupportedValues}.
|
|
1015
|
+
*/
|
|
1016
|
+
readonly unsupportedValues?: ProviderOAuth2UnsupportedValues;
|
|
1017
|
+
} | {
|
|
1018
|
+
readonly kind: ProviderOAuth2EndpointHostAllowanceKind.DEPLOYMENT_BASE_URL;
|
|
1019
|
+
/**
|
|
1020
|
+
* A path the configured base URL carries and the OAuth2 endpoints do NOT — stripped before
|
|
1021
|
+
* substitution, and REQUIRED to be present. Gitea's operation base is `<instance>/api/v1` while
|
|
1022
|
+
* its OAuth2 endpoints hang off `<instance>`; stripping keeps an instance served under a sub-path
|
|
1023
|
+
* correct, where taking only the origin would drop it.
|
|
1024
|
+
*/
|
|
1025
|
+
readonly baseUrlPathSuffix?: string;
|
|
1026
|
+
};
|
|
1027
|
+
/** One `{placeholder}` occurrence in an OAuth2 endpoint template. */
|
|
1028
|
+
export interface ProviderOAuth2EndpointPlaceholderOccurrence {
|
|
1029
|
+
readonly name: string;
|
|
1030
|
+
readonly position: ProviderOAuth2EndpointPlaceholderPosition;
|
|
1031
|
+
/** Offset of the opening brace in the template. */
|
|
1032
|
+
readonly index: number;
|
|
1033
|
+
/** The whole `{name}` token, for substitution. */
|
|
1034
|
+
readonly token: string;
|
|
1035
|
+
/**
|
|
1036
|
+
* HOST only: the token is the ENTIRE authority (`https://{cloud}/…`), as opposed to one part of it
|
|
1037
|
+
* (`https://accounts.{location}/…`). A declared host SUFFIX is only meaningful for a whole host — on a
|
|
1038
|
+
* partial one it would be concatenated with the text around it and admit a host nobody declared.
|
|
1039
|
+
*/
|
|
1040
|
+
readonly occupiesWholeHost: boolean;
|
|
1041
|
+
/**
|
|
1042
|
+
* HOST only: when the token OPENS the authority and is followed by literal text up to the end of the authority —
|
|
1043
|
+
* `https://{subdomain}.zendesk.com/…` → `.zendesk.com` — that literal text (#1015). A declared host SUFFIX equal to it
|
|
1044
|
+
* bounds the composed host exactly as it bounds a whole host: the value is DNS labels, the template's own literal
|
|
1045
|
+
* supplies the suffix, and `<labels>.zendesk.com` is the only shape that can result. Absent for a whole host, for a
|
|
1046
|
+
* token that does not open the host (`accounts.{location}`), and for an ORIGIN or PATH token.
|
|
1047
|
+
*/
|
|
1048
|
+
readonly literalHostSuffix?: string;
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Why an OAuth2 endpoint TEMPLATE cannot be resolved safely, whatever values are supplied.
|
|
1052
|
+
* See {@link findProviderOAuth2EndpointTemplateDefects}.
|
|
1053
|
+
*/
|
|
1054
|
+
export declare enum ProviderOAuth2EndpointTemplateDefect {
|
|
1055
|
+
/**
|
|
1056
|
+
* The template neither begins `http://` / `https://` nor begins with a placeholder. The WHATWG URL
|
|
1057
|
+
* parser reads `https:/{h}/t`, `https:{h}/t` and `https:` + two backslashes + `{h}/t` as `https://h/t`, so a string scan
|
|
1058
|
+
* for `://` would call `{h}` a PATH placeholder, encode it (a hostname survives encoding untouched),
|
|
1059
|
+
* and send the request — and the client secret — to whatever a caller typed.
|
|
1060
|
+
*/
|
|
1061
|
+
NOT_AN_ABSOLUTE_TEMPLATE = "NOT_AN_ABSOLUTE_TEMPLATE",
|
|
1062
|
+
/**
|
|
1063
|
+
* An ORIGIN placeholder is followed by something other than `/`, `?`, `#` or the end —
|
|
1064
|
+
* `{baseUrl}.evil.test/…`, `{baseUrl}:8443/…`, `{a}{b}/…`. The text after it would extend the host
|
|
1065
|
+
* the origin value names.
|
|
1066
|
+
*/
|
|
1067
|
+
ORIGIN_NOT_TERMINATED = "ORIGIN_NOT_TERMINATED",
|
|
1068
|
+
/**
|
|
1069
|
+
* One authority carries more than one placeholder (`https://{a}{b}/…`, `https://{sub}.{domain}/…`).
|
|
1070
|
+
* Each value validated alone can still compose a host no declaration admits.
|
|
1071
|
+
*/
|
|
1072
|
+
SEVERAL_PLACEHOLDERS_IN_AUTHORITY = "SEVERAL_PLACEHOLDERS_IN_AUTHORITY",
|
|
1073
|
+
/**
|
|
1074
|
+
* The authority after `://` is empty, begins with `/` or a backslash, or carries a backslash or `@`
|
|
1075
|
+
* before the first `/`, `?` or `#`. The parser skips extra slashes (`https:///{h}/t` dials `{h}` as the
|
|
1076
|
+
* HOST, while the `://` scan sees an empty authority and calls `{h}` a PATH), reads a backslash as a
|
|
1077
|
+
* path separator, and
|
|
1078
|
+
* reads text before `@` as userinfo — each makes the scan and the parser disagree about the host.
|
|
1079
|
+
*/
|
|
1080
|
+
MALFORMED_AUTHORITY = "MALFORMED_AUTHORITY"
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* A DNS name: one or more LDH labels, lower-case (callers lower-case first).
|
|
1084
|
+
* spec-cite: RFC 1123 §2.1 (a label may begin with a digit) and RFC 1035 §2.3.1 (letters, digits,
|
|
1085
|
+
* hyphen; 63 octets per label).
|
|
1086
|
+
*/
|
|
1087
|
+
export declare const PROVIDER_OAUTH2_DNS_NAME_PATTERN: RegExp;
|
|
1088
|
+
/**
|
|
1089
|
+
* Why a DECLARED host allowance cannot be applied where its placeholder sits — the position rules the
|
|
1090
|
+
* transposer enforces at GENERATION time, so a broken declaration refuses the batch instead of being
|
|
1091
|
+
* emitted and refused at every connect. Empty when the allowance is sound.
|
|
1092
|
+
*
|
|
1093
|
+
* The runtime resolver refuses each of these too; this exists so they are caught where a human is
|
|
1094
|
+
* already looking at a diff rather than where a person is pressing Connect.
|
|
1095
|
+
*/
|
|
1096
|
+
export declare function describeProviderOAuth2EndpointHostAllowanceDefects(placeholder: {
|
|
1097
|
+
readonly position: ProviderOAuth2EndpointPlaceholderPosition;
|
|
1098
|
+
readonly occupiesWholeHost: boolean;
|
|
1099
|
+
readonly literalHostSuffix?: string;
|
|
1100
|
+
}, allowance: ProviderOAuth2EndpointHostAllowance): readonly string[];
|
|
1101
|
+
/**
|
|
1102
|
+
* The defects that make a template unsafe to resolve, or an empty list. Shared by the runtime (which
|
|
1103
|
+
* refuses) and the transposer (which refuses to emit), so neither can admit a shape the other rejects.
|
|
1104
|
+
*/
|
|
1105
|
+
export declare function findProviderOAuth2EndpointTemplateDefects(template: string): readonly ProviderOAuth2EndpointTemplateDefect[];
|
|
1106
|
+
/** The placeholder syntax an endpoint template uses — the same braces the operation path templates use. */
|
|
1107
|
+
export declare const PROVIDER_OAUTH2_ENDPOINT_PLACEHOLDER_PATTERN: RegExp;
|
|
1108
|
+
/**
|
|
1109
|
+
* Classify every placeholder in an OAuth2 endpoint template by the URL component it forms.
|
|
1110
|
+
*
|
|
1111
|
+
* ONE function, shared by the transposer (which decides which placeholders need an allowance) and
|
|
1112
|
+
* the runtime (which refuses an unallowed one), so the two cannot disagree about what a host is.
|
|
1113
|
+
*
|
|
1114
|
+
* Deliberately a string scan, not a URL parse: a template whose host is still `{cloud}` is not a
|
|
1115
|
+
* parseable URL, which is exactly the population this classifies.
|
|
1116
|
+
*
|
|
1117
|
+
* - offset 0 → {@link ProviderOAuth2EndpointPlaceholderPosition.ORIGIN};
|
|
1118
|
+
* - between the first `://` and the next `/`, `?` or `#` → HOST;
|
|
1119
|
+
* - otherwise → PATH_OR_QUERY.
|
|
1120
|
+
*
|
|
1121
|
+
* This classification is only meaningful for a template with no
|
|
1122
|
+
* {@link findProviderOAuth2EndpointTemplateDefects}: a template that does not begin `http(s)://` or a
|
|
1123
|
+
* placeholder has no authority this scan can find, and every consumer must refuse it FIRST rather than
|
|
1124
|
+
* trust the PATH_OR_QUERY answer it would get here.
|
|
1125
|
+
*/
|
|
1126
|
+
export declare function classifyProviderOAuth2EndpointPlaceholders(template: string): readonly ProviderOAuth2EndpointPlaceholderOccurrence[];
|
|
280
1127
|
/**
|
|
281
1128
|
* Vendors whose credential is COMPUTED per request rather than sent as a fixed header.
|
|
282
1129
|
*
|