@wildo-ai/external-connectors-models 1.1.11 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/esm/companion-exports.d.ts.map +1 -1
  2. package/dist/esm/companion-exports.js.map +1 -1
  3. package/dist/esm/core/commons.external-connectors-models.schemas.d.ts +10 -0
  4. package/dist/esm/core/commons.external-connectors-models.schemas.d.ts.map +1 -1
  5. package/dist/esm/core/commons.external-connectors-models.schemas.js +10 -0
  6. package/dist/esm/core/commons.external-connectors-models.schemas.js.map +1 -1
  7. package/dist/esm/core/oauth2.enums.d.ts.map +1 -1
  8. package/dist/esm/core/oauth2.enums.js.map +1 -1
  9. package/dist/esm/core/operation-classification.external-connectors-models.schemas.d.ts.map +1 -1
  10. package/dist/esm/core/operation-classification.external-connectors-models.schemas.js.map +1 -1
  11. package/dist/esm/core/operations.external-connectors-models.schemas.d.ts.map +1 -1
  12. package/dist/esm/core/operations.external-connectors-models.schemas.js.map +1 -1
  13. package/dist/esm/core/security.external-connectors-models.schemas.d.ts.map +1 -1
  14. package/dist/esm/core/security.external-connectors-models.schemas.js.map +1 -1
  15. package/dist/esm/index.d.ts +6 -0
  16. package/dist/esm/index.d.ts.map +1 -1
  17. package/dist/esm/index.js +6 -0
  18. package/dist/esm/index.js.map +1 -1
  19. package/dist/esm/protocols/agent-client-protocol.external-connectors-models.schemas.d.ts +20 -46
  20. package/dist/esm/protocols/agent-client-protocol.external-connectors-models.schemas.d.ts.map +1 -1
  21. package/dist/esm/protocols/agent-client-protocol.external-connectors-models.schemas.js +34 -5
  22. package/dist/esm/protocols/agent-client-protocol.external-connectors-models.schemas.js.map +1 -1
  23. package/dist/esm/protocols/billing.standard-interface.schemas.d.ts +56 -3
  24. package/dist/esm/protocols/billing.standard-interface.schemas.d.ts.map +1 -1
  25. package/dist/esm/protocols/billing.standard-interface.schemas.js +15 -2
  26. package/dist/esm/protocols/billing.standard-interface.schemas.js.map +1 -1
  27. package/dist/esm/protocols/content-safety.external-connectors-models.schemas.d.ts.map +1 -1
  28. package/dist/esm/protocols/content-safety.external-connectors-models.schemas.js.map +1 -1
  29. package/dist/esm/protocols/document-extraction.external-connectors-models.schemas.d.ts.map +1 -1
  30. package/dist/esm/protocols/document-extraction.external-connectors-models.schemas.js.map +1 -1
  31. package/dist/esm/protocols/electronic-signature.external-connectors-models.schemas.d.ts +157 -0
  32. package/dist/esm/protocols/electronic-signature.external-connectors-models.schemas.d.ts.map +1 -0
  33. package/dist/esm/protocols/electronic-signature.external-connectors-models.schemas.js +134 -0
  34. package/dist/esm/protocols/electronic-signature.external-connectors-models.schemas.js.map +1 -0
  35. package/dist/esm/protocols/email-inbound.standard-interface.schemas.d.ts +85 -0
  36. package/dist/esm/protocols/email-inbound.standard-interface.schemas.d.ts.map +1 -1
  37. package/dist/esm/protocols/email-inbound.standard-interface.schemas.js +46 -2
  38. package/dist/esm/protocols/email-inbound.standard-interface.schemas.js.map +1 -1
  39. package/dist/esm/protocols/email.external-connectors-models.schemas.d.ts.map +1 -1
  40. package/dist/esm/protocols/email.external-connectors-models.schemas.js.map +1 -1
  41. package/dist/esm/protocols/email.standard-interface.schemas.d.ts.map +1 -1
  42. package/dist/esm/protocols/email.standard-interface.schemas.js.map +1 -1
  43. package/dist/esm/protocols/embeddings.external-connectors-models.schemas.d.ts +49 -6
  44. package/dist/esm/protocols/embeddings.external-connectors-models.schemas.d.ts.map +1 -1
  45. package/dist/esm/protocols/embeddings.external-connectors-models.schemas.js +89 -6
  46. package/dist/esm/protocols/embeddings.external-connectors-models.schemas.js.map +1 -1
  47. package/dist/esm/protocols/file-storage.standard-interface.d.ts +9 -2
  48. package/dist/esm/protocols/file-storage.standard-interface.d.ts.map +1 -1
  49. package/dist/esm/protocols/file-storage.standard-interface.js +6 -0
  50. package/dist/esm/protocols/file-storage.standard-interface.js.map +1 -1
  51. package/dist/esm/protocols/frontend.external-connectors-models.schemas.d.ts.map +1 -1
  52. package/dist/esm/protocols/frontend.external-connectors-models.schemas.js.map +1 -1
  53. package/dist/esm/protocols/graphql-operations.external-connectors-models.schemas.d.ts +254 -0
  54. package/dist/esm/protocols/graphql-operations.external-connectors-models.schemas.d.ts.map +1 -0
  55. package/dist/esm/protocols/graphql-operations.external-connectors-models.schemas.js +480 -0
  56. package/dist/esm/protocols/graphql-operations.external-connectors-models.schemas.js.map +1 -0
  57. package/dist/esm/protocols/hooks-capability.external-connectors-models.schemas.d.ts.map +1 -1
  58. package/dist/esm/protocols/hooks-capability.external-connectors-models.schemas.js.map +1 -1
  59. package/dist/esm/protocols/hooks.external-connectors-models.schemas.d.ts +95 -1
  60. package/dist/esm/protocols/hooks.external-connectors-models.schemas.d.ts.map +1 -1
  61. package/dist/esm/protocols/hooks.external-connectors-models.schemas.js +110 -0
  62. package/dist/esm/protocols/hooks.external-connectors-models.schemas.js.map +1 -1
  63. package/dist/esm/protocols/image.external-connectors-models.schemas.d.ts.map +1 -1
  64. package/dist/esm/protocols/image.external-connectors-models.schemas.js.map +1 -1
  65. package/dist/esm/protocols/llm.external-connectors-models.schemas.d.ts +95 -3
  66. package/dist/esm/protocols/llm.external-connectors-models.schemas.d.ts.map +1 -1
  67. package/dist/esm/protocols/llm.external-connectors-models.schemas.js +147 -15
  68. package/dist/esm/protocols/llm.external-connectors-models.schemas.js.map +1 -1
  69. package/dist/esm/protocols/oauth2-provider-assurance.definitions.d.ts.map +1 -1
  70. package/dist/esm/protocols/oauth2-provider-assurance.definitions.js.map +1 -1
  71. package/dist/esm/protocols/oauth2-provider-assurance.schemas.d.ts.map +1 -1
  72. package/dist/esm/protocols/oauth2-provider-assurance.schemas.js.map +1 -1
  73. package/dist/esm/protocols/oauth2.external-connectors-models.schemas.d.ts.map +1 -1
  74. package/dist/esm/protocols/oauth2.external-connectors-models.schemas.js.map +1 -1
  75. package/dist/esm/protocols/provider-configuration-authoring.external-connectors-models.schemas.d.ts +52 -0
  76. package/dist/esm/protocols/provider-configuration-authoring.external-connectors-models.schemas.d.ts.map +1 -0
  77. package/dist/esm/protocols/provider-configuration-authoring.external-connectors-models.schemas.js +132 -0
  78. package/dist/esm/protocols/provider-configuration-authoring.external-connectors-models.schemas.js.map +1 -0
  79. package/dist/esm/protocols/provider-connection-parameters.external-connectors-models.d.ts +111 -0
  80. package/dist/esm/protocols/provider-connection-parameters.external-connectors-models.d.ts.map +1 -0
  81. package/dist/esm/protocols/provider-connection-parameters.external-connectors-models.js +203 -0
  82. package/dist/esm/protocols/provider-connection-parameters.external-connectors-models.js.map +1 -0
  83. package/dist/esm/protocols/provider-oauth2-token-endpoint-hosts.external-connectors-models.d.ts +6 -0
  84. package/dist/esm/protocols/provider-oauth2-token-endpoint-hosts.external-connectors-models.d.ts.map +1 -1
  85. package/dist/esm/protocols/provider-oauth2-token-endpoint-hosts.external-connectors-models.js +13 -7
  86. package/dist/esm/protocols/provider-oauth2-token-endpoint-hosts.external-connectors-models.js.map +1 -1
  87. package/dist/esm/protocols/provider-vendor-registration.external-connectors-models.d.ts +50 -0
  88. package/dist/esm/protocols/provider-vendor-registration.external-connectors-models.d.ts.map +1 -0
  89. package/dist/esm/protocols/provider-vendor-registration.external-connectors-models.js +30 -0
  90. package/dist/esm/protocols/provider-vendor-registration.external-connectors-models.js.map +1 -0
  91. package/dist/esm/protocols/rest-operations.external-connectors-models.schemas.d.ts +730 -17
  92. package/dist/esm/protocols/rest-operations.external-connectors-models.schemas.d.ts.map +1 -1
  93. package/dist/esm/protocols/rest-operations.external-connectors-models.schemas.js +822 -20
  94. package/dist/esm/protocols/rest-operations.external-connectors-models.schemas.js.map +1 -1
  95. package/dist/esm/protocols/service-account-token.external-connectors-models.schemas.d.ts +73 -0
  96. package/dist/esm/protocols/service-account-token.external-connectors-models.schemas.d.ts.map +1 -0
  97. package/dist/esm/protocols/service-account-token.external-connectors-models.schemas.js +82 -0
  98. package/dist/esm/protocols/service-account-token.external-connectors-models.schemas.js.map +1 -0
  99. package/dist/esm/protocols/sms.external-connectors-models.schemas.d.ts.map +1 -1
  100. package/dist/esm/protocols/sms.external-connectors-models.schemas.js.map +1 -1
  101. package/dist/esm/protocols/sms.standard-interface.schemas.d.ts +79 -1
  102. package/dist/esm/protocols/sms.standard-interface.schemas.d.ts.map +1 -1
  103. package/dist/esm/protocols/sms.standard-interface.schemas.js +93 -0
  104. package/dist/esm/protocols/sms.standard-interface.schemas.js.map +1 -1
  105. package/dist/esm/protocols/subject-erasure-effect.standard-interface.d.ts.map +1 -1
  106. package/dist/esm/protocols/subject-erasure-effect.standard-interface.js.map +1 -1
  107. package/dist/tsconfig.build.tsbuildinfo +1 -1
  108. package/package.json +5 -5
  109. package/dist/esm/.native.tsbuildinfo +0 -1
@@ -46,7 +46,8 @@
46
46
  import { z } from 'zod';
47
47
  import { API_HttpMethod } from '../core/commons.external-connectors-models.schemas';
48
48
  import { ExternalOperation_Classification } from '../core/operation-classification.external-connectors-models.schemas';
49
- import type { ExternalProvider_ConnectionTarget } from '../core/security.external-connectors-models.schemas';
49
+ import { ExternalProvider_Connection_OwnerScope, type ExternalProvider_ConnectionTarget } from '../core/security.external-connectors-models.schemas';
50
+ import type { ProviderRestOperationGraphql } from './graphql-operations.external-connectors-models.schemas';
50
51
  /** @wildo_source:part:start external-connectors.models.rest-operation-contract facet:layer:shared facet:family:provider facet:audience:app-developer */
51
52
  /** Where a declared parameter rides on the wire. */
52
53
  export declare enum ProviderRestParameterLocation {
@@ -140,6 +141,19 @@ export interface ProviderRestOperationParameter {
140
141
  * above it; the origin itself cannot move, as for every path value.
141
142
  */
142
143
  readonly multiSegment?: true;
144
+ /**
145
+ * PATH only (#3397): the value is the CONNECTION's vendor-reported fact of this name, never the caller's input. Every
146
+ * QuickBooks path is `/v3/company/{realmId}/…`, and the company is the one the person connected, so asking each caller
147
+ * for it would let a call name a company its connection was not made for and leave every application re-typing it.
148
+ *
149
+ * The fact must be declared on the connection's OAuth2 contract with a
150
+ * {@link ProviderOAuth2ReportedAccountFact.pathSegmentPattern}; the access resolver re-admits the stored value on every
151
+ * call and hands it to the request compiler, which fills nothing else from it. A caller value under the same input
152
+ * name is refused rather than ignored, and the operation's `inputSchema` must not declare one
153
+ * ({@link describeProviderRestOperationConnectionFactDefects}). Exclusive with `from`, `constantValue` and
154
+ * `multiSegment`.
155
+ */
156
+ readonly connectionFact?: string;
143
157
  readonly description?: string;
144
158
  }
145
159
  /** How the request body is built from the operation's input. */
@@ -208,12 +222,50 @@ export interface ProviderRestOperationInputOptions {
208
222
  /** Dot-path, inside each listed item, to the text a person reads (`name`). Vendor content, never derived from the value. */
209
223
  readonly labelPath: string;
210
224
  /**
211
- * Inputs the options depend on: `{ <options-operation input field>: <this operation's input field> }`.
225
+ * Inputs the options depend on: `{ <options-operation input field>: <this operation's input field PATH> }`.
226
+ * The value side is a dot path into this operation's input, like the `inputOptions` key (#3399): Jira's issue
227
+ * types depend on `project.key`. The key side is a top-level field of the options operation's own input.
212
228
  * A choice that is not yet made is REFUSED by name rather than listed as empty — "no projects" and
213
229
  * "choose a workspace first" are different answers, and only one of them is true.
214
230
  */
215
231
  readonly dependsOn?: Readonly<Record<string, string>>;
232
+ /**
233
+ * Fixed values the options operation is called with, beside what `dependsOn` forwards (#3399): Jira lists only the
234
+ * projects a person may create in (`action: 'create'`), Box lists the root folder (`folder_id: '0'`). A field may
235
+ * not be both fixed and forwarded.
236
+ */
237
+ readonly input?: Readonly<Record<string, string | number | boolean>>;
238
+ /**
239
+ * Dot path, inside the options operation's output, to the listed items, when that output is an object rather than the
240
+ * list itself (#3399): QuickBooks answers a query as `{ Customer: [...] }`, Box a folder as `{ entries: [...] }`. An
241
+ * object without that path lists nothing, which is how QuickBooks answers a query with no rows; anything else at the
242
+ * path that is not a list is an unreadable answer. Absent, the output itself must be the list.
243
+ */
244
+ readonly itemsPath?: string;
245
+ /**
246
+ * Keeps only the listed items whose value at each dot path equals the given text (#3399): a folder's items are files,
247
+ * folders and web links, and only a folder can be a parent (`{ type: 'folder' }`). Absent, every item is a choice.
248
+ */
249
+ readonly itemWhere?: Readonly<Record<string, string>>;
216
250
  }
251
+ /**
252
+ * Why an operation's `inputOptions` cannot be honoured, or an empty list (#3399). Each key, and each `dependsOn` value,
253
+ * is a dot path into the operation's input (`project.key`, `CustomerRef.value`; a top-level field is a one-segment
254
+ * path), and must name a field the operation's `inputSchema` declares. Checked where a catalogue loads (the operation
255
+ * executor, an augmentation or supplement merge) and at publish, so a mistyped path is refused once, by name, instead of
256
+ * offering a picker that fills nothing. An operation without an `inputSchema` passes its input through unvalidated and
257
+ * declares no fields, so there is nothing to check its paths against; only their shape is.
258
+ */
259
+ export declare function describeProviderRestOperationInputOptionsDefects(operation: Pick<ProviderRestOperation, 'operationId' | 'inputSchema' | 'inputOptions'>): string[];
260
+ /**
261
+ * Why an operation's {@link ProviderRestOperationParameter.connectionFact} parameters cannot be honoured, or an empty
262
+ * list (#3397). Each must be a PATH parameter the path template uses, fed by nothing else (no `from`, `constantValue` or
263
+ * `multiSegment`), and the operation's `inputSchema` must not declare a field of its name: a caller is never asked for a
264
+ * value the connection supplies, and one it sends anyway is refused at the call. With `reportedAccountFacts` (the
265
+ * connection's OAuth2 declaration), each named fact must also be declared there with a `pathSegmentPattern`; without
266
+ * it the request compiler refuses the call, so a module walk passes them to catch it before any call.
267
+ */
268
+ export declare function describeProviderRestOperationConnectionFactDefects(operation: Pick<ProviderRestOperation, 'operationId' | 'pathTemplate' | 'parameters' | 'inputSchema'>, reportedAccountFacts?: Readonly<Record<string, ProviderOAuth2ReportedAccountFact>>): string[];
217
269
  /**
218
270
  * A provider operation, hand-authored. The unit an application addresses:
219
271
  * `executeProviderOperation({ providerRef: 'slack', operationId: 'conversations.list', input })`.
@@ -315,10 +367,19 @@ export interface ProviderRestOperation {
315
367
  /** Narrows the provider's semantics for this one operation (a slow export, a tight endpoint). */
316
368
  readonly remoteCallSemantics?: Partial<ProviderRestCallSemantics>;
317
369
  /**
318
- * Where each input's choices come from, keyed by INPUT field name. Absent for an input a caller types.
319
- * See {@link ProviderRestOperationInputOptions}.
370
+ * Where each input's choices come from, keyed by a dot PATH into the input (#3399): `workspace` for a top-level
371
+ * field, `project.key` or `CustomerRef.value` for the value a vendor nests inside a reference object. Absent for an
372
+ * input a caller types. Every path must name a field of `inputSchema`
373
+ * ({@link describeProviderRestOperationInputOptionsDefects}). See {@link ProviderRestOperationInputOptions}.
320
374
  */
321
375
  readonly inputOptions?: Readonly<Record<string, ProviderRestOperationInputOptions>>;
376
+ /**
377
+ * Present for a GraphQL operation (#3392): the provider-owned document the call sends to `pathTemplate` (the vendor's
378
+ * GraphQL endpoint), with the operation's input as its variables. A 2xx answer carrying `errors` fails the call; the
379
+ * output is read from the answer's `data`. See {@link ProviderRestOperationGraphql} for what such an operation may and
380
+ * may not declare beside it.
381
+ */
382
+ readonly graphql?: ProviderRestOperationGraphql;
322
383
  /** Set when the vendor has deprecated it; the executor logs once per operation, never refuses. */
323
384
  readonly deprecated?: {
324
385
  readonly reason: string;
@@ -420,6 +481,22 @@ export declare function describeProviderRestApplicationOutcomeDefects(outcome: P
420
481
  export declare const PROVIDER_CLIENT_CREDENTIALS_TENANT_PLACEHOLDER = "{tenant}";
421
482
  /** A tenant value the token URL admits: a GUID or a DNS name. Nothing that can leave the URL's path segment. */
422
483
  export declare const PROVIDER_CLIENT_CREDENTIALS_TENANT_SHAPE: RegExp;
484
+ /**
485
+ * The placeholder that BEGINS a client-credentials token URL whose token endpoint is on the deployment's own vendor
486
+ * instance (#3402): `{deploymentOrigin}/oauth2/v1/token`. Filled with the ORIGIN of the base URL the engine resolved for
487
+ * the REST contract (from `baseUrlHostEnvVarName` or `baseUrlEnvVarName`), never from a connection or a request.
488
+ *
489
+ * Okta is the case: every org has its own host (`<org>.okta.com`, or a custom domain), and its org authorization server's
490
+ * token endpoint is on that host. A fixed {@link ProviderRestClientCredentials.tokenHosts} list cannot admit a host only
491
+ * the operator knows, and a `{tenant}` value would be a second, separately typed copy of the host the operations already
492
+ * go to, which could disagree with it. Filling the token endpoint from the SAME resolved origin keeps them one value: the
493
+ * client's assertion is sent to the host the operator named, and to nothing a person connecting could choose.
494
+ *
495
+ * spec-cite: Okta Developer, "Implement OAuth for Okta with a service app"
496
+ * (https://developer.okta.com/docs/guides/implement-oauth-for-okta-serviceapp/main/, fetched 2026-11-10) — the token
497
+ * request is `POST https://{yourOktaDomain}/oauth2/v1/token`, and the assertion's `aud` is that URL.
498
+ */
499
+ export declare const PROVIDER_CLIENT_CREDENTIALS_DEPLOYMENT_ORIGIN_PLACEHOLDER = "{deploymentOrigin}";
423
500
  /** What a {@link ProviderClientCredentialsVerifiedFact} records: a value the person typed and the vendor accepted. */
424
501
  export declare enum ProviderClientCredentialsFactSource {
425
502
  /** The client id the token endpoint issued a token for: Microsoft's bot App ID. */
@@ -451,9 +528,14 @@ export interface ProviderClientCredentialsVerifiedFact {
451
528
  * secret the connection holds (#2663). The Bot Framework is the first: a Teams bot's App ID and secret, and for a
452
529
  * single-tenant bot the tenant it is registered in, mint a token for `https://api.botframework.com/.default`.
453
530
  *
531
+ * Declared beside `oauth2` (#3398), it is the APPLICATION's own connection: Box's client-credentials grant and Zoom's
532
+ * server-to-server OAuth renew the application's token while people and organizations connect through the exchange.
533
+ * {@link providerClientCredentialsServesOwnerScope} says which door a connection takes.
534
+ *
454
535
  * The token is minted per call when no unexpired one is cached, never stored, and attached where
455
- * {@link ProviderProtocolRuntimeContract}'s `apiKey` says, so `apiKey.secretRef` names the client SECRET (the slot
456
- * whose absence means "not configured"). The client id, secret and tenant are read from the connection: the
536
+ * {@link ProviderProtocolRuntimeContract}'s `apiKey` says, so (with no `oauth2` beside it) `apiKey.secretRef` names the
537
+ * client's credential, its SECRET or its private key (#3402), the slot whose absence means "not configured". The
538
+ * client id, credential and tenant are read from the connection: the
457
539
  * application's own environment for an APPLICATION connection, the organization's typed credential for an
458
540
  * ORGANIZATION one.
459
541
  *
@@ -468,16 +550,62 @@ export interface ProviderClientCredentialsVerifiedFact {
468
550
  * `https://api.botframework.com/.default`, fetched 2026-10-06.
469
551
  */
470
552
  export interface ProviderRestClientCredentials {
471
- /** The https token endpoint. May carry {@link PROVIDER_CLIENT_CREDENTIALS_TENANT_PLACEHOLDER}. */
553
+ /**
554
+ * The https token endpoint. May carry {@link PROVIDER_CLIENT_CREDENTIALS_TENANT_PLACEHOLDER}, or BEGIN with
555
+ * {@link PROVIDER_CLIENT_CREDENTIALS_DEPLOYMENT_ORIGIN_PLACEHOLDER} when the token endpoint is on the deployment's own
556
+ * vendor instance (Okta's org, #3402).
557
+ */
472
558
  readonly tokenUrl: string;
473
- /** The hosts the token endpoint may be on once filled. The client secret is sent nowhere else. */
559
+ /**
560
+ * The hosts the token endpoint may be on once filled. The client secret is sent nowhere else. EMPTY exactly when the
561
+ * token URL begins with {@link PROVIDER_CLIENT_CREDENTIALS_DEPLOYMENT_ORIGIN_PLACEHOLDER}: its host is the one the
562
+ * operator named for the operations, and no fixed list can name it.
563
+ */
474
564
  readonly tokenHosts: readonly string[];
475
- /** The `scope` parameter, sent as written. */
476
- readonly scope: string;
565
+ /**
566
+ * The `scope` parameter, sent as written. Omitted, no `scope` is sent: RFC 6749 makes it optional, and Box, Zoom and
567
+ * Salesforce grant the scopes configured on the app instead (#3398).
568
+ * spec-cite: RFC 6749 §4.4.2 — `scope`: "OPTIONAL. The scope of the access request as described by Section 3.3."
569
+ * Fetched 2026-10-06.
570
+ */
571
+ readonly scope?: string;
572
+ /**
573
+ * The `grant_type` sent. Omitted, `client_credentials` (RFC 6749 §4.4). See {@link ProviderClientCredentialsGrantType}.
574
+ */
575
+ readonly grantType?: ProviderClientCredentialsGrantType;
576
+ /**
577
+ * Further parameters of the token request a vendor requires, by name: a fixed value, or one read from a slot of the
578
+ * connection (#3398). Box's client-credentials grant names the subject it authenticates as (`box_subject_type`,
579
+ * `box_subject_id`); Zoom's server-to-server grant names the account (`account_id`). A parameter the grant itself
580
+ * defines (`grant_type`, `client_id`, `client_secret`, `scope`, the client assertion) is refused here: each has its own
581
+ * field. See {@link ProviderClientCredentialsTokenRequestParameter}.
582
+ */
583
+ readonly tokenRequestParameters?: Readonly<Record<string, ProviderClientCredentialsTokenRequestParameter>>;
584
+ /**
585
+ * How the client authenticates to the token endpoint. Omitted,
586
+ * {@link ProviderOAuth2ClientAuthenticationMethod.CLIENT_SECRET_POST} with a client secret: the id and secret in the
587
+ * body, as Microsoft's identity platform documents. {@link ProviderOAuth2ClientAuthenticationMethod.CLIENT_SECRET_BASIC}
588
+ * sends them in the Basic header only (Zoom's server-to-server OAuth documents nothing else). With
589
+ * {@link privateKeyJwt} the method is {@link ProviderOAuth2ClientAuthenticationMethod.PRIVATE_KEY_JWT}, implied when
590
+ * omitted (#3402).
591
+ */
592
+ readonly tokenEndpointAuthMethod?: ProviderOAuth2ClientAuthenticationMethod;
477
593
  /** The slot holding the client id. */
478
594
  readonly clientIdSecretRef: string;
479
- /** The slot holding the client secret. Equal to the contract's `apiKey.secretRef`. */
480
- readonly clientSecretSecretRef: string;
595
+ /**
596
+ * The slot holding the client secret. Exactly one of this and {@link privateKeyJwt} (#3402): a client authenticates
597
+ * with a shared secret or with a key it alone holds, never both. The client's CREDENTIAL slot (this one, or the
598
+ * private key's) equals the contract's `apiKey.secretRef` when the contract declares no `oauth2` (the slot whose
599
+ * absence means "not configured"); beside `oauth2`, `apiKey.secretRef` is the slot of the people's and organizations'
600
+ * minted tokens and this one serves the application alone.
601
+ */
602
+ readonly clientSecretSecretRef?: string;
603
+ /**
604
+ * The client authenticates with a JWT it signs with its own private key (`private_key_jwt`, #3402) instead of a
605
+ * secret: Okta's OAuth service apps accept nothing else for the Management API. See
606
+ * {@link ProviderClientCredentialsPrivateKeyJwt}.
607
+ */
608
+ readonly privateKeyJwt?: ProviderClientCredentialsPrivateKeyJwt;
481
609
  /** The slot filling {@link PROVIDER_CLIENT_CREDENTIALS_TENANT_PLACEHOLDER}. Required exactly when the token URL carries it. */
482
610
  readonly tenantSecretRef?: string;
483
611
  /** Facts an ORGANIZATION connection records once the vendor accepted its credential, keyed by fact name. */
@@ -485,12 +613,171 @@ export interface ProviderRestClientCredentials {
485
613
  /** Seconds a token is kept when the answer states no `expires_in`. Omitted, it is not cached. */
486
614
  readonly tokenLifetimeFallbackSeconds?: number;
487
615
  }
616
+ /**
617
+ * The `grant_type` a client-credentials token request sends. Narrower than `OAuth2_GrantType` on purpose: these are the
618
+ * grants the client-credentials minter performs, each with nothing but the client's own credential and the declared
619
+ * parameters, and a member joins in the change that implements it.
620
+ */
621
+ export declare enum ProviderClientCredentialsGrantType {
622
+ /**
623
+ * The OAuth 2.0 client-credentials grant: the default.
624
+ * spec-cite: RFC 6749 §4.4.2 — `grant_type`: "REQUIRED. Value MUST be set to "client_credentials"." Fetched 2026-10-06.
625
+ */
626
+ CLIENT_CREDENTIALS = "client_credentials",
627
+ /**
628
+ * Zoom's server-to-server OAuth: the same exchange (a client authenticated with Basic, one token back, no refresh
629
+ * token) under Zoom's own grant name, with the account named in `account_id`.
630
+ * spec-cite: https://developers.zoom.us/docs/internal-apps/s2s-oauth/ — "Use the `account_credentials` grant type to
631
+ * generate an access token."; "Set up the body of the access token request using the `account_credentials`
632
+ * `grant_type` and your `account_id`." Fetched 2026-11-09.
633
+ */
634
+ ACCOUNT_CREDENTIALS = "account_credentials"
635
+ }
636
+ /**
637
+ * One further parameter of a client-credentials token request ({@link ProviderRestClientCredentials.tokenRequestParameters}):
638
+ * a fixed `value` the module states (`box_subject_type: enterprise`), or a `secretRef` naming the connection slot that
639
+ * holds it (Box's enterprise id, Zoom's account id), read like the client id: from the application's environment for
640
+ * the application's connection, from the organization's credential for an organization's.
641
+ */
642
+ export type ProviderClientCredentialsTokenRequestParameter = {
643
+ readonly value: string;
644
+ readonly secretRef?: never;
645
+ } | {
646
+ readonly secretRef: string;
647
+ readonly value?: never;
648
+ };
649
+ /**
650
+ * The algorithms a client assertion is signed with (#3402). The JWS names (RFC 7518 §3.1) of the two asymmetric
651
+ * families every vendor measured accepts: Okta lists "RS256, RS384, RS512, ES256, ES384, and ES512" for a service app's
652
+ * key. A member joins when a vendor needs it.
653
+ * spec-cite: RFC 7518 §3.1 — "RS256 | RSASSA-PKCS1-v1_5 using SHA-256" and "ES256 | ECDSA using P-256 and SHA-256";
654
+ * https://developer.okta.com/docs/guides/implement-oauth-for-okta-serviceapp/main/ — the supported algorithms. Both
655
+ * fetched 2026-11-09.
656
+ */
657
+ export declare enum ProviderClientAssertionSigningAlgorithm {
658
+ /** ECDSA on P-256 with SHA-256: a short key and signature, and the default to choose for a new key. */
659
+ ES256 = "ES256",
660
+ /** RSASSA-PKCS1-v1_5 with SHA-256, for a vendor or key store that issues RSA keys. */
661
+ RS256 = "RS256"
662
+ }
663
+ /** The longest a client assertion may live: Okta refuses one whose `exp` is more than an hour away. */
664
+ export declare const PROVIDER_CLIENT_ASSERTION_MAXIMUM_LIFETIME_SECONDS = 3600;
665
+ /** How long a client assertion lives when the declaration says nothing: long enough for one request, and no more. */
666
+ export declare const PROVIDER_CLIENT_ASSERTION_DEFAULT_LIFETIME_SECONDS = 300;
667
+ /**
668
+ * `private_key_jwt` client authentication for a client-credentials token request (#3402). The client signs, per token
669
+ * request, a JWT whose `iss` and `sub` are its client id, whose `aud` is the token endpoint it is sent to, with a short
670
+ * `exp` and a fresh `jti`, and sends it as `client_assertion`.
671
+ *
672
+ * The private key is read from a slot of the connection, as an unencrypted PEM (PKCS#8, or an RSA PKCS#1 / EC key the
673
+ * signer re-wraps), like a secret: the application's configured provider secrets for its own connection, an organization's sealed
674
+ * credential row for an organization's.
675
+ *
676
+ * ## Why the platform keyring does not hold this key (decided 2026-11-10, #3402)
677
+ *
678
+ * The key layer (#2916) holds keys by PURPOSE, each signing the platform's own claims under its own `typ` and verified
679
+ * from public halves the platform publishes. A client key is the opposite: its public half is REGISTERED BY THE OPERATOR
680
+ * at the vendor (Okta's app, a Google service account), the vendor is its only verifier, and the vendor fixes the
681
+ * algorithm (Okta's console and Google's uploaded keys are RSA, while the keyring signs ES256 only). A keyring rotation
682
+ * would also silently break every connection whose vendor still holds the old public half. So custody signing for vendor
683
+ * clients is a key purpose of its own (RS256 across the keyring providers, a public half handed to the operator, rotation
684
+ * coordinated with the vendor registration), recorded on #2916, and not a missing step of this declaration.
685
+ *
686
+ * spec-cite: RFC 7523 §2.2 — "The value of the "client_assertion_type" is
687
+ * "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"."; §3 — the JWT "MUST contain an "iss" (issuer) claim", for
688
+ * client authentication "the subject MUST be the "client_id" of the OAuth client", an "aud" (audience) claim "containing
689
+ * a value that identifies the authorization server", and an "exp" (expiration time) claim; OpenID Connect Core 1.0 §9,
690
+ * `private_key_jwt` — `iss` and `sub` "MUST contain the client_id of the OAuth Client", "The Audience SHOULD be the URL
691
+ * of the Authorization Server's Token Endpoint", `jti` REQUIRED: "These tokens MUST only be used once". Fetched
692
+ * 2026-11-09 from https://www.rfc-editor.org/rfc/rfc7523 and https://openid.net/specs/openid-connect-core-1_0.html.
693
+ */
694
+ export interface ProviderClientCredentialsPrivateKeyJwt {
695
+ /** The slot holding the client's private key: an unencrypted PEM, PKCS#8 (`-----BEGIN PRIVATE KEY-----`) or PKCS#1 / EC. */
696
+ readonly privateKeySecretRef: string;
697
+ /** The JWS algorithm the key signs with. Must match the key: an EC P-256 key for ES256, an RSA key for RS256. */
698
+ readonly algorithm: ProviderClientAssertionSigningAlgorithm;
699
+ /**
700
+ * The slot holding the key id sent as the assertion's `kid` header, when the vendor locates the key by it (Okta
701
+ * requires a `kid` on every key registered with the app). Omitted, no `kid` is sent.
702
+ */
703
+ readonly keyIdSecretRef?: string;
704
+ /** Seconds until the assertion's `exp`. Omitted, {@link PROVIDER_CLIENT_ASSERTION_DEFAULT_LIFETIME_SECONDS}. */
705
+ readonly assertionLifetimeSeconds?: number;
706
+ }
707
+ /**
708
+ * The slot of a client-credentials declaration's own CREDENTIAL: the client secret, or the private key (#3402).
709
+ * `undefined` only for a declaration {@link describeProviderRestClientCredentialsDefects} refuses.
710
+ */
711
+ export declare function providerClientCredentialsCredentialSlot(credentials: ProviderRestClientCredentials): string | undefined;
712
+ /**
713
+ * The parameter names {@link ProviderRestClientCredentials.tokenRequestParameters} may not declare: each is written by
714
+ * the grant itself (the grant type, the client's credential, the scope, a client assertion), so a second value would
715
+ * either be ignored or override the declared one.
716
+ */
717
+ export declare const PROVIDER_CLIENT_CREDENTIALS_RESERVED_PARAMETER_NAMES: readonly string[];
718
+ /**
719
+ * Whether the client-credentials door of a REST contract serves a connection of this owner scope (#3398).
720
+ *
721
+ * Alone, it serves every admitted connection: the application's own (from its environment) and an organization's (from
722
+ * the credential the organization typed, as a Teams bot). Beside `oauth2`, a person or an organization connects through
723
+ * the authorization-code exchange, and the client-credentials grant serves the APPLICATION's own account only, so that
724
+ * account renews itself instead of resting on a pasted token that expires within the hour.
725
+ */
726
+ export declare function providerClientCredentialsServesOwnerScope(contract: {
727
+ readonly clientCredentials?: ProviderRestClientCredentials;
728
+ readonly oauth2?: unknown;
729
+ }, ownerScope: ExternalProvider_Connection_OwnerScope): boolean;
730
+ /**
731
+ * The slot a connection of this owner scope AUTHENTICATES with, as the engine's REST access resolver reads it: the
732
+ * client-credentials grant's own credential (its client secret or private key) when that door serves the owner scope
733
+ * ({@link providerClientCredentialsServesOwnerScope}), else `apiKey.secretRef`, the stored or pasted token itself.
734
+ *
735
+ * For a contract declaring `clientCredentials` beside `oauth2` (Box, Zoom, #3398) the two differ by owner: the
736
+ * application's own account mints from the server app's secret, while a person's connection reads its exchanged token
737
+ * from `apiKey.secretRef`. A tool standing in for one connection (`wildo providers probe`, `connect`, the doctor's
738
+ * configuration checks, all APPLICATION-owned) asks this instead of reading `apiKey.secretRef`, so it reads the slot the
739
+ * engine would. `undefined` when the contract names neither.
740
+ */
741
+ export declare function providerRestConnectionCredentialSlot(contract: {
742
+ readonly clientCredentials?: ProviderRestClientCredentials;
743
+ readonly oauth2?: unknown;
744
+ readonly apiKey?: {
745
+ readonly secretRef?: string;
746
+ };
747
+ }, ownerScope: ExternalProvider_Connection_OwnerScope): string | undefined;
488
748
  /**
489
749
  * Why a client-credentials declaration cannot be honoured, or an empty list. Checked where a provider is composed:
490
750
  * a token URL that is not https, a token host that is not a DNS name or that the URL is not on, a tenant placeholder
491
751
  * with no tenant slot (or the reverse), or any other placeholder in the URL.
492
752
  */
493
753
  export declare function describeProviderRestClientCredentialsDefects(credentials: ProviderRestClientCredentials): string[];
754
+ /**
755
+ * Why a REST contract's `clientCredentials` sits wrongly beside the contract's other authentication fields, or an empty
756
+ * list (#2663, #3398). Checked where a provider is composed, beside {@link describeProviderRestClientCredentialsDefects}.
757
+ *
758
+ * - beside `requestSigning`: two ways to authenticate one call;
759
+ * - alone, `apiKey.secretRef` must be the client secret's slot, the one whose absence means "not configured";
760
+ * - beside `oauth2` the grant serves the APPLICATION's own account only ({@link providerClientCredentialsServesOwnerScope}),
761
+ * so the module must admit that connection; `apiKey.secretRef` names the exchanged tokens' slot and may not be the
762
+ * client secret's (a person's token would be read from it); and `verifiedAccountFacts`, which only an organization's
763
+ * connection records, would record nothing.
764
+ * - a token URL on {@link PROVIDER_CLIENT_CREDENTIALS_DEPLOYMENT_ORIGIN_PLACEHOLDER} (#3402) needs a base URL the
765
+ * DEPLOYMENT names (`baseUrlHostEnvVarName` or `baseUrlEnvVarName`), and may serve the APPLICATION's connection only:
766
+ * the deployment's instance is the application's own (its Okta org), so an organization minting there would
767
+ * authenticate against the application's vendor tenant, never its own.
768
+ */
769
+ export declare function describeProviderRestClientCredentialsPlacementDefects(contract: {
770
+ readonly clientCredentials?: ProviderRestClientCredentials;
771
+ readonly oauth2?: unknown;
772
+ readonly requestSigning?: unknown;
773
+ readonly apiKey: {
774
+ readonly secretRef: string;
775
+ };
776
+ readonly baseUrlHostEnvVarName?: string;
777
+ readonly baseUrlEnvVarName?: string;
778
+ }, admitsApplicationConnection: boolean,
779
+ /** Whether the module admits an ORGANIZATION or USER connection. Omitted, `false`. */
780
+ admitsNonApplicationConnection?: boolean): string[];
494
781
  /**
495
782
  * The vendor's error code at `errorCodePath` in a parsed answer, when it is code-shaped. Used for a refused 2xx and for
496
783
  * a non-2xx answer alike: a vendor that states its code in the body states it in the same place whatever the status.
@@ -702,8 +989,108 @@ export interface ProviderRestOAuth2 {
702
989
  *
703
990
  * Absent — not empty — when no revocation is declared, so "this vendor cannot be told" stays
704
991
  * distinguishable from "nobody has looked".
992
+ *
993
+ * The client authenticates here exactly as it does at the token endpoint ({@link tokenEndpointAuthMethod}), and the
994
+ * body is form-encoded unless {@link revocationRequestBodyEncoding} says otherwise: a vendor that takes RFC 7009's
995
+ * `token` parameter and answers its 200, but wants Basic or a JSON body, is still declared here.
705
996
  */
706
997
  readonly rfc7009RevocationUrl?: string;
998
+ /**
999
+ * The vendor's USERINFO endpoint (#3397): a GET carrying the access token the authorization just minted as a Bearer
1000
+ * credential, whose JSON answer is read by every fact declared with
1001
+ * {@link ProviderOAuth2ReportedFactSource.USERINFO_RESPONSE}. DocuSign names the connected account and its API host
1002
+ * nowhere else: `GET https://account(-d).docusign.com/oauth/userinfo` lists the person's `accounts`, each with its
1003
+ * `account_id` and `base_uri`.
1004
+ *
1005
+ * Called ONCE, at the authorization-code exchange that makes (or remakes) the connection, and only when some fact
1006
+ * reads it; never at a refresh. The vendor's own guidance is to call it at setup and store the answer, and DocuSign
1007
+ * enforces an hourly limit on these requests per user and per integration key, so a call per refresh would spend that
1008
+ * limit to restate facts that change "very infrequently".
1009
+ * spec-cite: DocuSign, "UserInfo" (https://developers.docusign.com/platform/auth/reference/user-info/, fetched
1010
+ * 2026-11-09) — "Your integration should call this method to get the information needed to begin making API calls
1011
+ * during setup, then store it for use in future calls" and "Docusign enforces hourly limits on the number of
1012
+ * `/oauth/userinfo` requests per user ID and per integration key".
1013
+ *
1014
+ * Resolved like {@link tokenUrl}, through the same {@link endpointHostAllowances} and deployment values, because it
1015
+ * receives a credential too (the person's access token). It belongs to the same authorization server, so it may carry
1016
+ * only the host-forming placeholders the token endpoint carries, each in the same position
1017
+ * ({@link describeProviderOAuth2ReportedFactUseDefects}); every reader of "which placeholders does this client have"
1018
+ * therefore stays complete reading the authorize and token endpoints alone. A redirect is refused, never followed.
1019
+ */
1020
+ readonly userInfoUrl?: string;
1021
+ /**
1022
+ * How the client authenticates to the vendor's token endpoint: at the code exchange, at every refresh (and every
1023
+ * per-audience mint, which is a refresh), and at the revocation endpoint. Named after the client metadata field
1024
+ * `token_endpoint_auth_method`, whose values it uses. See {@link ProviderOAuth2ClientAuthenticationMethod}.
1025
+ *
1026
+ * Absent means {@link ProviderOAuth2ClientAuthenticationMethod.CLIENT_SECRET_POST}: `client_id` and `client_secret` in
1027
+ * the body, the engine's behaviour before this field existed, so no existing provider sends a different byte. Declare
1028
+ * {@link ProviderOAuth2ClientAuthenticationMethod.CLIENT_SECRET_BASIC} for a vendor that documents the Basic header
1029
+ * (Zoom, Notion, Intuit). With Basic the body carries neither `client_id` nor `client_secret`: the header identifies
1030
+ * the client, and a credential is sent on one channel only.
1031
+ * spec-cite: RFC 6749 §4.1.3 — `client_id` is "REQUIRED, if the client is not authenticating with the authorization
1032
+ * server" as Section 3.2.1 describes. Fetched 2026-11-06.
1033
+ *
1034
+ * ## Why the revocation endpoint does not get a method of its own
1035
+ *
1036
+ * RFC 7009 sends the client's credentials at the revocation endpoint "as described in Section 2.3. of [RFC6749]", the
1037
+ * same section as the token endpoint, and every vendor measured on 2026-11-06 (Zoom, Notion, Intuit) authenticates
1038
+ * both endpoints the same way. A separate member would be a choice no vendor has needed; it is added when one does.
1039
+ * spec-cite: RFC 7009 §2.1 — "The client also includes its authentication credentials as described in Section 2.3.
1040
+ * of [RFC6749]." Fetched 2026-11-06.
1041
+ */
1042
+ readonly tokenEndpointAuthMethod?: ProviderOAuth2ClientAuthenticationMethod;
1043
+ /**
1044
+ * How the token request's parameters are written (exchange, refresh, per-audience mint). Absent means
1045
+ * {@link ProviderOAuth2RequestBodyEncoding.FORM}, as RFC 6749 specifies. Notion's token endpoint takes JSON only.
1046
+ */
1047
+ readonly tokenRequestBodyEncoding?: ProviderOAuth2RequestBodyEncoding;
1048
+ /**
1049
+ * Extra parameters the AUTHORIZATION-CODE exchange carries beside the specification's own (follow-up to #3392), each a
1050
+ * fixed value the provider module owns: a vendor that issues a different kind of token when asked in the exchange.
1051
+ * Shopify issues an expiring offline token (an hour, with a refresh token) only when the exchange carries `expiring=1`,
1052
+ * and a non-expiring one otherwise; a public app on non-expiring tokens is refused from April 2027.
1053
+ * spec-cite: https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens — the `expiring` row: "Set
1054
+ * to `1` for an expiring offline token, or `0` (default) for a non-expiring one.", fetched 2026-11-10.
1055
+ *
1056
+ * Sent at the code exchange only: a refresh renews the grant the exchange made and Shopify's refresh request carries no
1057
+ * such parameter. A name the engine writes itself (`grant_type`, `code`, `redirect_uri`, `code_verifier`, the client's
1058
+ * credentials, `refresh_token`, `scope`) is refused, so a declaration cannot change what the exchange is
1059
+ * ({@link describeProviderOAuth2TokenRequestDefects}).
1060
+ *
1061
+ * @example
1062
+ * codeExchangeParameters: { expiring: '1' }
1063
+ */
1064
+ readonly codeExchangeParameters?: Readonly<Record<string, string>>;
1065
+ /**
1066
+ * The refusals of a REFRESH that the vendor documents as a dead refresh token, beside the specification's
1067
+ * `invalid_grant` (follow-up to #3392). A refresh refused with one of them retires the stored connection the way
1068
+ * `invalid_grant` does, rather than being retried at every call. Shopify answers a refresh token that can no longer be
1069
+ * used with `invalid_request` and a fixed description, and says to treat it as final.
1070
+ * spec-cite: https://shopify.dev/docs/api/usage/response-codes — "A refresh token that can no longer be used returns this
1071
+ * status with the error code `invalid_request` and the description `This request requires an active refresh_token`.
1072
+ * Treat it as final rather than retrying.", fetched 2026-11-10.
1073
+ *
1074
+ * `error` is the OAuth2 `error` code. `errorDescription`, when given, must equal the answer's `error_description`
1075
+ * exactly: `invalid_request` alone also names a malformed request, which says nothing about the person's grant, so a
1076
+ * vendor that reuses a generic code is matched on its description too. Read at a refresh only, never at the exchange.
1077
+ * `invalid_client` is refused: it names the APPLICATION's registration, and retiring every connection over it would
1078
+ * blame the people who connected (see `providerOAuth2GrantWasPermanentlyRejected`).
1079
+ *
1080
+ * @example
1081
+ * refreshRefusals: [{ error: 'invalid_request', errorDescription: 'This request requires an active refresh_token' }]
1082
+ */
1083
+ readonly refreshRefusals?: readonly ProviderOAuth2RefreshRefusal[];
1084
+ /**
1085
+ * How the revocation request's parameters are written. Absent means {@link ProviderOAuth2RequestBodyEncoding.FORM}, as
1086
+ * RFC 7009 specifies, whatever {@link tokenRequestBodyEncoding} says: the two encodings are declared separately because
1087
+ * a vendor can differ between its two endpoints. Intuit's own client library form-encodes the token request and sends
1088
+ * the revocation request as JSON.
1089
+ * spec-cite: intuit/oauth-jsclient `OAuthClient.prototype.revoke` and `_tokenRequestHeaders`
1090
+ * (https://raw.githubusercontent.com/intuit/oauth-jsclient/e65c49b0fd1c5356ba24ddbbe910b8bc71b6f75f/src/OAuthClient.js,
1091
+ * fetched 2026-11-06).
1092
+ */
1093
+ readonly revocationRequestBodyEncoding?: ProviderOAuth2RequestBodyEncoding;
707
1094
  /**
708
1095
  * Scopes the vendor's own catalogue entry asked for. An application may request FEWER (a subset of
709
1096
  * these, once resolved), and an operation's `requiredScopes` is the finer statement. Kept because
@@ -829,7 +1216,8 @@ export interface ProviderRestOAuth2 {
829
1216
  *
830
1217
  * ## Why this exists: the token endpoint receives the application's client secret
831
1218
  *
832
- * The exchange POSTs `client_id` and the APPLICATION's `client_secret` to the resolved `tokenUrl`.
1219
+ * The exchange sends `client_id` and the APPLICATION's `client_secret` to the resolved `tokenUrl`, in the body or in a
1220
+ * Basic header ({@link ProviderRestOAuth2.tokenEndpointAuthMethod}); either way the host receives the secret.
833
1221
  * Until 2026-09-12 every placeholder was answered from the connect-start request body, so where a
834
1222
  * placeholder formed the HOST — Microsoft's `https://{cloud}/…/token`, Salesforce's
835
1223
  * `https://{environment}/…`, Zoho's `https://accounts.{location}/…` — any user allowed to start a
@@ -980,6 +1368,12 @@ export interface ProviderRestOAuth2Audience {
980
1368
  readonly hosts: {
981
1369
  readonly values: readonly string[];
982
1370
  readonly hostSuffixes?: readonly string[];
1371
+ /**
1372
+ * The most DNS labels a host may carry before one of `hostSuffixes`; absent, any number (#3640). The same bound,
1373
+ * with the same meaning, as a host allowance's `hostSuffixMaxLabels`: declare it where the vendor documents its
1374
+ * hosts as one shape (`<store>.myshopify.com`), so a token is never sent to a deeper name under the suffix.
1375
+ */
1376
+ readonly hostSuffixMaxLabels?: number;
983
1377
  };
984
1378
  /**
985
1379
  * The scopes that mint a token for this audience, all of ONE resource. An entry may begin with
@@ -999,7 +1393,10 @@ export interface ProviderOAuth2AudienceSelection {
999
1393
  readonly audience: ProviderRestOAuth2Audience;
1000
1394
  readonly primary: boolean;
1001
1395
  }
1002
- /** Whether `host` is admitted by one audience's hosts: an exact value, or `<labels><suffix>` for a declared suffix. */
1396
+ /**
1397
+ * Whether `host` is admitted by one audience's hosts: an exact value, or `<labels><suffix>` for a declared suffix, with
1398
+ * no more labels than `hostSuffixMaxLabels` allows (#3640).
1399
+ */
1003
1400
  export declare function providerOAuth2AudienceAdmitsHost(audience: ProviderRestOAuth2Audience, host: string): boolean;
1004
1401
  /**
1005
1402
  * The ONE audience whose hosts admit `host`, or `undefined` when none does. The declaration guarantees at most one
@@ -1096,6 +1493,74 @@ export declare enum ProviderOAuth2PkceSupport {
1096
1493
  /** The vendor refuses PKCE from a confidential client (Slack): neither is sent. */
1097
1494
  NOT_ACCEPTED = "NOT_ACCEPTED"
1098
1495
  }
1496
+ /**
1497
+ * How this application's client authenticates to a vendor's token and revocation endpoints. See
1498
+ * {@link ProviderRestOAuth2.tokenEndpointAuthMethod}.
1499
+ *
1500
+ * The values are the names the "OAuth Token Endpoint Authentication Methods" registry gives these two methods, which
1501
+ * is what OpenID Connect and dynamic client registration call `token_endpoint_auth_method`, so a vendor's discovery
1502
+ * document (`token_endpoint_auth_methods_supported`) can be compared with a declaration by eye.
1503
+ * spec-cite: RFC 7591 §2 — `token_endpoint_auth_method`: "client_secret_post": "The client uses the HTTP POST parameters
1504
+ * as defined in OAuth 2.0, Section 2.3.1."; "client_secret_basic": "The client uses HTTP Basic as defined in OAuth 2.0,
1505
+ * Section 2.3.1." Fetched 2026-11-06.
1506
+ *
1507
+ * Narrower than `OAuth2_TokenEndpoint_AuthMethod` on purpose: that vocabulary also names `client_secret_jwt` and
1508
+ * `none`, which nothing here performs. `private_key_jwt` joined with #3402, performed by the client-credentials grant
1509
+ * only; the connected-account exchange refuses it at startup. A member nothing honours would
1510
+ * be a declaration that looks like a client-authentication choice and changes nothing, so each method joins this
1511
+ * vocabulary in the change that implements it.
1512
+ */
1513
+ export declare enum ProviderOAuth2ClientAuthenticationMethod {
1514
+ /**
1515
+ * `client_id` and `client_secret` as parameters of the request body: the engine's behaviour when nothing is declared,
1516
+ * and what most vendors' own documentation shows. RFC 6749 calls it "NOT RECOMMENDED", which is why a vendor that
1517
+ * documents Basic declares {@link CLIENT_SECRET_BASIC} rather than being met with this.
1518
+ * spec-cite: RFC 6749 §2.3.1 — sending the two parameters in the body "is NOT RECOMMENDED and SHOULD be limited to
1519
+ * clients unable to directly utilize the HTTP Basic authentication scheme". Fetched 2026-11-06.
1520
+ */
1521
+ CLIENT_SECRET_POST = "client_secret_post",
1522
+ /**
1523
+ * `Authorization: Basic base64(client_id ":" client_secret)`, each half form-urlencoded BEFORE base64, and neither in
1524
+ * the body: one credential on one channel. Every authorization server must accept it (Zoom, Notion and Intuit document
1525
+ * it as their client authentication).
1526
+ * spec-cite: RFC 6749 §2.3.1 — "The client identifier is encoded using the "application/x-www-form-urlencoded"
1527
+ * encoding algorithm per Appendix B", and so is the password, before both become the Basic user-id and password; "The
1528
+ * authorization server MUST support the HTTP Basic authentication scheme". Appendix B encodes UTF-8 first, so `£` is
1529
+ * `%C2%A3` and a space is `+`. Fetched 2026-11-06.
1530
+ */
1531
+ CLIENT_SECRET_BASIC = "client_secret_basic",
1532
+ /**
1533
+ * A JWT the client signs with its own private key, sent as `client_assertion` (#3402). Performed by the
1534
+ * client-credentials grant only ({@link ProviderRestClientCredentials.privateKeyJwt}); the connected-account exchange
1535
+ * does not sign assertions, so `oauth2.tokenEndpointAuthMethod` refuses it at startup.
1536
+ * spec-cite: OpenID Connect Core 1.0 §9 — `private_key_jwt`: "Clients that have registered a public key sign a JWT
1537
+ * using that key." Fetched 2026-11-09 from https://openid.net/specs/openid-connect-core-1_0.html.
1538
+ */
1539
+ PRIVATE_KEY_JWT = "private_key_jwt"
1540
+ }
1541
+ /**
1542
+ * How the parameters of a request to a vendor's token or revocation endpoint are written in its body. See
1543
+ * {@link ProviderRestOAuth2.tokenRequestBodyEncoding} and {@link ProviderRestOAuth2.revocationRequestBodyEncoding}.
1544
+ *
1545
+ * The parameters are the same in both encodings (`grant_type`, `code`, `refresh_token`, `token`, …); only their
1546
+ * serialization and the `Content-Type` change.
1547
+ */
1548
+ export declare enum ProviderOAuth2RequestBodyEncoding {
1549
+ /**
1550
+ * `application/x-www-form-urlencoded`, which RFC 6749 and RFC 7009 specify: the default for both requests.
1551
+ * spec-cite: RFC 6749 §4.1.3 (the client makes the token request "by sending the following parameters using the
1552
+ * "application/x-www-form-urlencoded" format"; §6 does the same for a refresh), and RFC 7009 §2.1 (the revocation
1553
+ * request uses "the "application/x-www-form-urlencoded" format in the HTTP request entity-body"). Fetched 2026-11-06.
1554
+ */
1555
+ FORM = "FORM",
1556
+ /**
1557
+ * `application/json`, an object whose members are the same parameters as string values. A deviation some vendors
1558
+ * document: Notion's token endpoint takes only JSON, and Intuit's own client library revokes with a JSON body.
1559
+ * spec-cite: https://developers.notion.com/docs/authorization — the token request's `Content-Type: application/json`,
1560
+ * fetched 2026-11-06.
1561
+ */
1562
+ JSON = "JSON"
1563
+ }
1099
1564
  /**
1100
1565
  * A `scope` list encoding that deviates from RFC 6749's space-delimited list, in either direction: the token answer
1101
1566
  * ({@link ProviderRestOAuth2.grantedScopesDelimiter}) or the authorize request ({@link ProviderRestOAuth2.authorizeScopesDelimiter}).
@@ -1112,6 +1577,40 @@ export declare enum ProviderOAuth2ScopeListDelimiter {
1112
1577
  */
1113
1578
  COMMA = "COMMA"
1114
1579
  }
1580
+ /**
1581
+ * What makes a client-authentication declaration unusable (see {@link ProviderRestOAuth2.tokenEndpointAuthMethod}):
1582
+ *
1583
+ * - a method or an encoding outside its vocabulary. The request builder tests for the declared member, so an unknown
1584
+ * value (a misspelling in a module written without types) would silently fall back to the default and send the
1585
+ * secret the way the vendor said not to;
1586
+ * - a revocation encoding with no revocation URL, a setting that changes nothing.
1587
+ */
1588
+ export declare function describeProviderOAuth2ClientAuthenticationDefects(oauth2: Pick<ProviderRestOAuth2, 'tokenEndpointAuthMethod' | 'tokenRequestBodyEncoding' | 'revocationRequestBodyEncoding' | 'rfc7009RevocationUrl'>): string[];
1589
+ /** A refresh refusal the vendor documents as a dead refresh token. See {@link ProviderRestOAuth2.refreshRefusals}. */
1590
+ export interface ProviderOAuth2RefreshRefusal {
1591
+ /** The OAuth2 `error` code of the refusal. */
1592
+ readonly error: string;
1593
+ /** The `error_description` the refusal must carry, exactly, when the code alone is not specific to a dead token. */
1594
+ readonly errorDescription?: string;
1595
+ }
1596
+ /**
1597
+ * What makes a token-request declaration unusable (follow-up to #3392), checked where the provider registry composes:
1598
+ *
1599
+ * - a {@link ProviderRestOAuth2.codeExchangeParameters} name that is not a plain identifier, that names a parameter the
1600
+ * engine writes itself, or whose value is empty;
1601
+ * - a {@link ProviderRestOAuth2.refreshRefusals} entry whose code is not code-shaped (it would never match), is
1602
+ * `invalid_grant` (already a refusal, so the entry says nothing) or `invalid_client` (the application's registration,
1603
+ * never one person's grant), or whose description is empty.
1604
+ */
1605
+ export declare function describeProviderOAuth2TokenRequestDefects(oauth2: Pick<ProviderRestOAuth2, 'codeExchangeParameters' | 'refreshRefusals'>): string[];
1606
+ /**
1607
+ * Whether a refresh's refusal is one the provider declares as a dead refresh token ({@link ProviderRestOAuth2.refreshRefusals}).
1608
+ * The one reading, shared by the refresh and its tests.
1609
+ */
1610
+ export declare function providerOAuth2RefreshRefusalIsDeclared(oauth2: Pick<ProviderRestOAuth2, 'refreshRefusals'>, answer: {
1611
+ readonly error: string;
1612
+ readonly errorDescription?: string;
1613
+ }): boolean;
1115
1614
  /**
1116
1615
  * The granted scopes a token answer's `scope` string names, read with the vendor's declared delimiter: whitespace (RFC
1117
1616
  * 6749 §5.1) when none is declared, a comma for {@link ProviderOAuth2ScopeListDelimiter.COMMA}.
@@ -1161,13 +1660,104 @@ export declare enum ProviderOAuth2ReportedFactSource {
1161
1660
  * answer names the account only inside the ID token (#2660).
1162
1661
  * spec-cite: OpenID Connect Core 1.0 §3.1.3.7, item 6.
1163
1662
  */
1164
- ID_TOKEN_CLAIMS = "id_token_claims"
1663
+ ID_TOKEN_CLAIMS = "id_token_claims",
1664
+ /**
1665
+ * A query parameter of the AUTHORIZATION REDIRECT that brings the person back with the code (#3397): Intuit's
1666
+ * `realmId`, the QuickBooks company the person chose on the consent screen. `responsePath` is the parameter's name,
1667
+ * one segment. Read by the callback leg, kept with the attempt until the completion leg redeems the code, and recorded
1668
+ * only from that exchange: a refresh has no redirect, so it never restates such a fact and the stored value stands.
1669
+ *
1670
+ * The weakest of the three sources, and restricted for it. The redirect reaches this application through the
1671
+ * browser of the person who authorized, so that person could change the value before it arrives, exactly as they
1672
+ * could type an answer. Such a fact is therefore admitted only where a wrong value harms nobody but the person who
1673
+ * forged it: as a PATH value ({@link ProviderOAuth2ReportedAccountFact.pathSegmentPattern}) of a vendor that checks it
1674
+ * against the token on every call (Intuit answers a realm the token was not granted for with an authorization fault).
1675
+ * It may never be an `origin`, fill a base URL host, or route inbound deliveries, since each of those decides where a
1676
+ * credential goes or which organization receives a message; `describeProviderOAuth2ReportedFactUseDefects` refuses it.
1677
+ */
1678
+ AUTHORIZATION_REDIRECT_QUERY = "authorization_redirect_query",
1679
+ /**
1680
+ * The JSON answer of the vendor's USERINFO endpoint ({@link ProviderRestOAuth2.userInfoUrl}, #3397), fetched with the
1681
+ * access token the authorization just minted: DocuSign's `accounts[…].account_id` and `base_uri`. As trustworthy as
1682
+ * the token answer (the vendor answers over TLS, to a request the person's own token authorized), so it may be placed
1683
+ * wherever a token fact may. Read at the authorization-code exchange only: a refresh does not call the endpoint, so it
1684
+ * never restates such a fact and the stored value stands until the person connects again.
1685
+ */
1686
+ USERINFO_RESPONSE = "userinfo_response"
1687
+ }
1688
+ /** What reading a {@link ProviderOAuth2ReportedAccountFact.responsePath} out of a vendor's answer found (#3397). */
1689
+ export declare enum ProviderReportedFactReadOutcome {
1690
+ /** A non-empty string or a finite number stood at the path; `value` is its text. */
1691
+ FOUND = "FOUND",
1692
+ /** Nothing usable stood at the path: a missing key, a `null`, an object, an empty string. The fact is not reported. */
1693
+ ABSENT = "ABSENT",
1694
+ /**
1695
+ * A selection segment (`accounts[is_default=true]`) met an array in which NOT EXACTLY ONE element matched. The vendor's
1696
+ * answer does not name one element, so no fact is read from it: guessing the first would address a request to an
1697
+ * account nobody chose. `matched` is how many did.
1698
+ */
1699
+ SELECTION_NOT_SINGLE = "SELECTION_NOT_SINGLE"
1700
+ }
1701
+ /** The result of {@link readProviderReportedFactValue}. */
1702
+ export type ProviderReportedFactRead = {
1703
+ readonly outcome: ProviderReportedFactReadOutcome.FOUND;
1704
+ readonly value: string;
1705
+ } | {
1706
+ readonly outcome: ProviderReportedFactReadOutcome.ABSENT;
1707
+ } | {
1708
+ readonly outcome: ProviderReportedFactReadOutcome.SELECTION_NOT_SINGLE;
1709
+ readonly segment: string;
1710
+ readonly matched: number;
1711
+ };
1712
+ /** One step of a parsed {@link ProviderOAuth2ReportedAccountFact.responsePath}. */
1713
+ export interface ProviderReportedFactPathSegment {
1714
+ /** The own property read at this step. */
1715
+ readonly key: string;
1716
+ /** Present on a SELECTION step: the property is an array, and the one element whose `field` reads as `equals` is kept. */
1717
+ readonly select?: {
1718
+ readonly field: string;
1719
+ readonly equals: string;
1720
+ };
1165
1721
  }
1722
+ /**
1723
+ * The steps of a reported fact's `responsePath`, or `undefined` when it is not one (#3397). A path is dot-separated;
1724
+ * each step is an own property name, optionally followed by ONE selection `[field=value]` that picks, out of the array
1725
+ * at that property, the single element whose `field` reads as `value`: `accounts[is_default=true].account_id`.
1726
+ *
1727
+ * Names, fields and values may not contain `.`, `[`, `]` or `=`; a value may be empty. Kept this small on purpose: it
1728
+ * addresses one element by one stated condition, which is what a vendor documenting "the default account" means, and
1729
+ * nothing that would need a query language.
1730
+ */
1731
+ export declare function parseProviderReportedFactPath(path: string): readonly ProviderReportedFactPathSegment[] | undefined;
1732
+ /**
1733
+ * The value at a reported fact's `responsePath` inside a vendor's JSON answer (#3397): a token answer, the claims of
1734
+ * its ID token, or a userinfo answer. Own properties only (the answer is a vendor's JSON, and `constructor` is not a
1735
+ * fact). A FOUND value is a non-empty string or a finite number's decimal text.
1736
+ *
1737
+ * A selection step keeps the ONE array element whose field reads as the stated value (a string as itself, a number as
1738
+ * its decimal text, a boolean as `true` / `false`, so DocuSign's `is_default: true` matches `[is_default=true]` whether
1739
+ * it is sent as a boolean or a string). Zero or several matches is {@link ProviderReportedFactReadOutcome.SELECTION_NOT_SINGLE}:
1740
+ * two facts selected with the same condition (`accounts[is_default=true].account_id` and `….base_uri`) are therefore
1741
+ * read from the same element or not at all. Anything else that does not lead to a scalar (a missing key, a property
1742
+ * that is not an array where a selection is written, an unparsable path) is ABSENT.
1743
+ */
1744
+ export declare function readProviderReportedFactValue(payload: unknown, path: string): ProviderReportedFactRead;
1745
+ /**
1746
+ * The query parameters an authorization redirect carries for the OAuth protocol itself, never as an account fact: a
1747
+ * redirect-sourced fact named like one would read the code, the state or the error as the account.
1748
+ * spec-cite: RFC 6749 §4.1.2 (`code`, `state`) and §4.1.2.1 (`error`, `error_description`, `error_uri`); RFC 9207 §2
1749
+ * (`iss`).
1750
+ * semantic-literal-boundary: the OAuth protocol's own redirect parameter names, compared with a module's declaration.
1751
+ */
1752
+ export declare const PROVIDER_OAUTH2_REDIRECT_PROTOCOL_PARAMETERS: readonly string[];
1166
1753
  /** One vendor-reported account fact. See {@link ProviderRestOAuth2.reportedAccountFacts}. */
1167
1754
  export interface ProviderOAuth2ReportedAccountFact {
1168
1755
  /**
1169
- * Dot-path inside the fact's {@link source}: the token endpoint's JSON answer by default, or the claims of its
1170
- * `id_token`. A string or finite number there is the value.
1756
+ * Dot-path inside the fact's {@link source}: the token endpoint's JSON answer by default, the claims of its
1757
+ * `id_token`, or the userinfo endpoint's JSON answer. A string or finite number there is the value. A step may select
1758
+ * ONE element of an array by a stated condition, `accounts[is_default=true].account_id` (#3397); zero or several
1759
+ * matching elements report nothing and refuse the connect ({@link readProviderReportedFactValue}). For
1760
+ * {@link ProviderOAuth2ReportedFactSource.AUTHORIZATION_REDIRECT_QUERY}, the redirect's query parameter name.
1171
1761
  */
1172
1762
  readonly responsePath: string;
1173
1763
  /** Where {@link responsePath} is read. Omitted, the token endpoint's JSON answer. */
@@ -1179,6 +1769,27 @@ export interface ProviderOAuth2ReportedAccountFact {
1179
1769
  readonly origin?: Extract<ProviderOAuth2EndpointHostAllowance, {
1180
1770
  readonly kind: ProviderOAuth2EndpointHostAllowanceKind.DECLARED_VALUES;
1181
1771
  }>;
1772
+ /**
1773
+ * Present when operations may place the value as ONE PATH SEGMENT (#3397): QuickBooks' `/v3/company/{realmId}/…`. An
1774
+ * operation names the fact on a PATH parameter ({@link ProviderRestOperationParameter.connectionFact}) and the caller
1775
+ * never supplies it. A JavaScript regular expression the WHOLE value must match (it is anchored for you), checked when
1776
+ * the connection is made (a value that does not match, or no value, refuses the connect) and again on every call, as
1777
+ * an origin is. A value that matches is still refused when it is `.` or `..` or holds a `/`, so it can never move the
1778
+ * request to another path. Keep it as narrow as the vendor documents: `\d+` for a QuickBooks company id.
1779
+ *
1780
+ * Without this a vendor-reported fact fills nothing: what the vendor reported is not an answer the person gave, and
1781
+ * only a declared shape keeps a value that would otherwise go unexamined out of a URL.
1782
+ */
1783
+ readonly pathSegmentPattern?: string;
1784
+ /**
1785
+ * `true` when the value may fill the base URL's `{<this fact's name>}` HOST placeholder (#3397), admitted against the
1786
+ * REST contract's `baseUrlHostAllowances` entry for that name on every call: the same allowance, and the same
1787
+ * resolver, that admit a host a person typed at connect. Needs a `DECLARED_VALUES` allowance under the fact's name.
1788
+ * Never filled into an OAuth2 endpoint (authorize, token, revocation): those carry the client secret, and only the
1789
+ * person's answers or the deployment fill them. An `origin` is the other way to address the account; a fact is one
1790
+ * or the other.
1791
+ */
1792
+ readonly fillsBaseUrlHost?: true;
1182
1793
  /**
1183
1794
  * `true` when the inbound receiver routes the vendor's deliveries to an organization by this fact: Slack's
1184
1795
  * `team.id`, the workspace an event comes from (#2660). Such a fact may name ONE organization only, so an
@@ -1252,6 +1863,37 @@ export declare function providerOAuth2IdTokenRequirementHolds(requirement: Provi
1252
1863
  * the behavior is entirely unspecified").
1253
1864
  */
1254
1865
  export declare function describeProviderOAuth2IdTokenRequirementDefects(oauth2: Pick<ProviderRestOAuth2, 'idTokenRequirements' | 'scopes' | 'reportedAccountFacts' | 'idTokenIssuer'>): string[];
1866
+ /**
1867
+ * Whether a vendor-reported value may stand as one path segment under its fact's declared
1868
+ * {@link ProviderOAuth2ReportedAccountFact.pathSegmentPattern} (#3397): the WHOLE value matches the pattern, and it is
1869
+ * not empty, not a dot-segment and holds no `/`, whatever the pattern admits. The one admission, applied where the value
1870
+ * is captured (the authorization) and where it is spent (every call). A pattern that does not compile admits nothing.
1871
+ */
1872
+ export declare function providerReportedPathSegmentAdmits(pattern: string, value: string): boolean;
1873
+ /** The REST-contract slice {@link describeProviderOAuth2ReportedFactUseDefects} reads. */
1874
+ export interface ProviderReportedFactUseContract {
1875
+ readonly baseUrl?: string;
1876
+ readonly baseUrlConnectionFact?: string;
1877
+ readonly baseUrlHostAllowances?: Readonly<Record<string, ProviderOAuth2EndpointHostAllowance>>;
1878
+ readonly oauth2?: Pick<ProviderRestOAuth2, 'reportedAccountFacts' | 'connectionParameters'> & Partial<Pick<ProviderRestOAuth2, 'tokenUrl' | 'userInfoUrl'>>;
1879
+ }
1880
+ /**
1881
+ * What makes the declared USES of a connection's vendor-reported facts unsafe or unusable (#3397), checked at boot with
1882
+ * the contract's other declarations:
1883
+ *
1884
+ * - a fact read from the authorization redirect is something the person who authorized could change, so it may not be
1885
+ * an `origin`, fill a base URL host, or route inbound deliveries, and its name must be one query parameter that is
1886
+ * not the OAuth protocol's own (`code`, `state`, …);
1887
+ * - a `pathSegmentPattern` must compile, admit no empty value, and not sit beside an `origin`;
1888
+ * - a `fillsBaseUrlHost` fact must name a `{placeholder}` of the base URL that a `DECLARED_VALUES` allowance governs,
1889
+ * must not also be an `origin` or something the person is asked at connect, and cannot share a contract with
1890
+ * `baseUrlConnectionFact`, which already decides where calls go;
1891
+ * - every other `responsePath` must parse ({@link parseProviderReportedFactPath});
1892
+ * - a fact read from the userinfo answer needs a `userInfoUrl`, a `userInfoUrl` needs a fact that reads it (a request
1893
+ * carrying the person's token that nothing uses is not made), and the userinfo endpoint may carry only the
1894
+ * host-forming placeholders the token endpoint carries, in the same position (it is the same authorization server).
1895
+ */
1896
+ export declare function describeProviderOAuth2ReportedFactUseDefects(contract: ProviderReportedFactUseContract): string[];
1255
1897
  /** The values a whole-scope placeholder admits. See {@link ProviderRestOAuth2.scopeValueAllowances}. */
1256
1898
  export interface ProviderOAuth2ScopeValueAllowance {
1257
1899
  readonly values: readonly string[];
@@ -1421,6 +2063,24 @@ export type ProviderOAuth2EndpointHostAllowance = {
1421
2063
  * so `evilmy.salesforce.com` never matches `.my.salesforce.com`.
1422
2064
  */
1423
2065
  readonly hostSuffixes?: readonly string[];
2066
+ /**
2067
+ * The MOST DNS labels an admitted host may carry before its suffix (#3640); absent, any number. Applies to every
2068
+ * entry of `hostSuffixes`, and only to them (an exact `values` entry is admitted as written).
2069
+ *
2070
+ * Declared only where the VENDOR documents the shape. Shopify does: a store host is exactly one label under
2071
+ * `.myshopify.com` ("Confirm the `shop` parameter matches `^[a-zA-Z0-9][a-zA-Z0-9\-]*\.myshopify\.com$`"), so it
2072
+ * declares `1`. Without the bound, a person answering `shop` with the full domain they see, `acme.myshopify.com`,
2073
+ * composed `acme.myshopify.com.myshopify.com`: a host under the suffix that does not exist, which failed every
2074
+ * request on DNS. (That answer is now also stripped to its handle, see `stripProviderHostAnswerTemplateSuffix`; the
2075
+ * bound is what refuses `acme.evil.myshopify.com`, naming the expected form.)
2076
+ * spec-cite: https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/authorization-code-grant —
2077
+ * the `shop` parameter check, as quoted in the Shopify module's header (fetched 2026-11-10).
2078
+ *
2079
+ * Every place a suffix admits a host reads it, through `providerHostIsUnderSuffix`: the answer filled into an endpoint
2080
+ * or base URL, the token-endpoint host description, the declined-value check. A positive integer, with at least one
2081
+ * suffix (`describeProviderOAuth2EndpointHostAllowanceDefects`).
2082
+ */
2083
+ readonly hostSuffixMaxLabels?: number;
1424
2084
  /**
1425
2085
  * ORIGIN only: an admitted value must ALSO have the same origin as the deployment's configured base
1426
2086
  * URL (the REST contract's `baseUrl` / `baseUrlEnvVarName`) (#991 review F4).
@@ -1526,6 +2186,59 @@ export declare enum ProviderOAuth2EndpointTemplateDefect {
1526
2186
  * hyphen; 63 octets per label).
1527
2187
  */
1528
2188
  export declare const PROVIDER_OAUTH2_DNS_NAME_PATTERN: RegExp;
2189
+ /**
2190
+ * Whether `host` is `<labels><suffix>`: it ends with the dotted `suffix`, what precedes the suffix is a DNS name (never
2191
+ * empty, so the bare suffix is not admitted), and that DNS name carries at most `maxLabels` labels (#3640).
2192
+ *
2193
+ * THE one suffix test. Every place a declared host suffix admits a host — an answer filled into an endpoint or a base URL,
2194
+ * an audience selecting a request host, the token-endpoint host description, a declined value a suffix would still admit —
2195
+ * asks this function, so a label bound declared once holds at all of them. `maxLabels` absent means unbounded, which is
2196
+ * what every suffix meant before the bound existed. A bound that is not a positive integer admits nothing (fail closed):
2197
+ * the declaration checks refuse it where a person is reading a diff, and this keeps the runtime closed regardless.
2198
+ *
2199
+ * Case-insensitive; the caller need not lower-case.
2200
+ */
2201
+ export declare function providerHostIsUnderSuffix(host: string, suffix: string, maxLabels?: number): boolean;
2202
+ /** One declared host suffix with its label bound, as {@link providerHostSuffixesOverlap} compares them. */
2203
+ export interface ProviderHostSuffixBound {
2204
+ readonly suffix: string;
2205
+ /** At most this many DNS labels before the suffix; absent, unbounded. See {@link providerHostIsUnderSuffix}. */
2206
+ readonly maxLabels?: number;
2207
+ }
2208
+ /**
2209
+ * Whether some host is admitted under BOTH suffixes (#3640). Two suffixes can only share a host when one ends with the
2210
+ * other (`.a.b` and `.b`): a host under the longer one carries the extra labels (`.a`) before the shorter one, so the
2211
+ * shorter one's bound must leave room for one label of its own plus those. Equal suffixes always share a host.
2212
+ */
2213
+ export declare function providerHostSuffixesOverlap(left: ProviderHostSuffixBound, right: ProviderHostSuffixBound): boolean;
2214
+ /**
2215
+ * The bare answer for a placeholder that OPENS a host, when the person typed the whole host (#3640).
2216
+ *
2217
+ * `https://{shop}.myshopify.com` asks for the store's handle, and the person connecting sees `acme.myshopify.com` in the
2218
+ * vendor's own screens. Filled verbatim, that answer dials `acme.myshopify.com.myshopify.com`: a host the suffix admits
2219
+ * (it ends in `.myshopify.com`) that does not exist, so every request fails on DNS naming a host rather than the answer.
2220
+ * The literal text the template writes after the placeholder (`literalHostSuffix`) is therefore stripped ONCE from an
2221
+ * answer that ends with it: `acme.myshopify.com` → `acme`. Nothing else is rewritten; the result is then admitted like
2222
+ * any other answer, so an answer with too many labels left (`acme.evil.myshopify.com` → `acme.evil` under a one-label
2223
+ * bound) is still refused.
2224
+ *
2225
+ * Generic rather than per vendor, because it is a property of the TEMPLATE: every provider whose host opens with a
2226
+ * placeholder (Shopify, Zendesk) asks for the part before a literal the vendor shows people with it attached. Done where
2227
+ * the template is filled, not in a connect prompt, so the OAuth start, the stored-answer re-admission and every
2228
+ * operation's base URL read the same answer the same way, whichever screen or flag supplied it. A host that literally
2229
+ * repeated its suffix is never a real vendor host, so nothing a person could mean is lost.
2230
+ */
2231
+ export declare function stripProviderHostAnswerTemplateSuffix(value: string, literalHostSuffix: string | undefined): string;
2232
+ /**
2233
+ * The words a refusal uses for the answer a bounded suffix expects (#3640): `a single DNS label before .myshopify.com
2234
+ * (the 'acme' of acme.myshopify.com)`. Shared so every refusal names the same form.
2235
+ */
2236
+ export declare function describeProviderHostSuffixExpectedForm(suffix: string, maxLabels: number | undefined, answerExcludesSuffix: boolean): string;
2237
+ /**
2238
+ * Why a declared label bound on host suffixes cannot be honoured (#3640): it is not a positive integer, or there is no
2239
+ * suffix for it to bound. Shared by the endpoint allowances and the audiences, so both refuse the same shapes.
2240
+ */
2241
+ export declare function describeProviderHostSuffixMaxLabelsDefects(maxLabels: number | undefined, hostSuffixes: readonly string[] | undefined): string[];
1529
2242
  /**
1530
2243
  * Why a DECLARED host allowance cannot be applied where its placeholder sits — the position rules the
1531
2244
  * transposer enforces at GENERATION time, so a broken declaration refuses the batch instead of being