@superdurable/connectors-sdk 0.1.1 → 0.3.0

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.
@@ -1,14 +1,336 @@
1
1
  import {
2
2
  SecretString
3
- } from "../chunk-KD5DYYSU.js";
3
+ } from "../chunk-QDMBAWXD.js";
4
+ import "../chunk-BQGYGCOQ.js";
4
5
  import {
5
6
  ReauthorizationRequiredError
6
- } from "../chunk-SOXYPIMO.js";
7
+ } from "../chunk-IHG6ADQG.js";
7
8
 
8
9
  // src/oauthtoken/index.ts
9
10
  import { createPrivateKey, sign } from "crypto";
11
+
12
+ // src/oauthtoken/manifest_refresh.ts
13
+ var ManifestCredentialRefreshDriver = class {
14
+ #providerName;
15
+ #auth;
16
+ #credentialFields;
17
+ #settings;
18
+ #fetch;
19
+ #now;
20
+ /** Bind validated auth data, pinned settings, a caller-owned transport, and a clock. */
21
+ constructor(options) {
22
+ if (!options?.providerName || !options.auth || !options.credentialFields) throw new TypeError("manifest refresh configuration is required");
23
+ this.#providerName = options.providerName;
24
+ this.#auth = options.auth;
25
+ this.#credentialFields = options.credentialFields;
26
+ this.#settings = options.settings ?? {};
27
+ this.#fetch = options.fetch ?? fetch;
28
+ this.#now = options.now ?? (() => /* @__PURE__ */ new Date());
29
+ }
30
+ /** Check declared access fields against the host's single credential expiry. */
31
+ refreshRequired(state) {
32
+ const grants = this.#grants(this.#selectedMethod(state.credentials));
33
+ if (!grants.length) return false;
34
+ const accessFields = grants.flatMap((grant) => grant.mappings.filter((mapping) => mapping.source === "access_token").map((mapping) => manifestText(mapping.credential)));
35
+ const hasAccessToken = accessFields.length > 0 && accessFields.every((name) => Boolean(this.#credentialValue(state.credentials, name)));
36
+ return isRefreshRequired(hasAccessToken, state.expiresAt, state.now, "keep");
37
+ }
38
+ /** Send each active grant and return one complete credential with the earliest expiry. */
39
+ async refresh(state, signal) {
40
+ let credentials = state.credentials;
41
+ const method = this.#selectedMethod(credentials);
42
+ const grants = this.#grants(method);
43
+ if (!grants.length) throw new ReauthorizationRequiredError();
44
+ for (const grant of grants) this.#validateGrant(grant);
45
+ const expiries = [];
46
+ for (const grant of grants) {
47
+ if (grant.refreshField && !this.#credentialValue(credentials, grant.refreshField)) continue;
48
+ const token = await this.#exchange(grant, credentials, method, signal);
49
+ const requiredScopes = this.#scopes(grant.scopes).filter((scope) => grant.kind !== "refresh" || scope !== "offline_access");
50
+ if (grant.exactScopes ? !hasDeclaredScopes(token.scope, requiredScopes, grant.scopeSeparators, true) : token.scope.trim() !== "" && !hasDeclaredScopes(token.scope, requiredScopes, grant.scopeSeparators, false)) throw new ReauthorizationRequiredError();
51
+ const updates = {};
52
+ for (const mapping of grant.mappings) {
53
+ const destination = manifestText(mapping.credential);
54
+ const value = token.mappedCredentialValue(manifestText(mapping.source));
55
+ if (value === void 0 || value === "" && destination === grant.refreshField) continue;
56
+ const property = this.#credentialFields[destination];
57
+ if (!property) throw new TypeError("manifest credential field is not mapped");
58
+ updates[property] = this.#isSecret(method, destination) ? new SecretString(value) : value;
59
+ }
60
+ for (const copy of grant.copies) {
61
+ const destination = manifestText(copy.credential);
62
+ const property = this.#credentialFields[destination];
63
+ if (!property) throw new TypeError("manifest credential field is not mapped");
64
+ const value = this.#credentialValue(credentials, manifestText(copy.fromCredential));
65
+ updates[property] = this.#isSecret(method, destination) ? new SecretString(value) : value;
66
+ }
67
+ credentials = { ...credentials, ...updates };
68
+ const lifetime = token.expiresInMs > 0 ? token.expiresInMs : parseDurationMs(grant.missingExpiry);
69
+ expiries.push(lifetime > 0 ? this.#now().getTime() + lifetime : 0);
70
+ }
71
+ if (!expiries.length) throw new ReauthorizationRequiredError();
72
+ return { credentials, expiresAt: new Date(Math.min(...expiries)) };
73
+ }
74
+ #selectedMethod(credentials) {
75
+ const methods = manifestList(this.#auth.methods);
76
+ if (!methods.length) return this.#auth;
77
+ const selected = manifestText(credentials.authMethodId);
78
+ const method = methods.map(manifestObject).find((item) => item.id === (selected || manifestText(this.#auth.defaultMethod)));
79
+ if (!method) throw new ReauthorizationRequiredError();
80
+ return method;
81
+ }
82
+ #grants(method) {
83
+ if (method.oauth2) {
84
+ const oauth = manifestObject(method.oauth2);
85
+ const refresh = method.oauth2 && manifestObject(oauth.refresh);
86
+ let declared = manifestList(refresh?.grants).map(manifestObject);
87
+ if (!declared.length) {
88
+ const mapped = manifestList(oauth.credentialMappings).map(manifestObject).find((mapping) => mapping.source === "refresh_token");
89
+ if (!mapped) return [];
90
+ declared = [{ refreshTokenCredential: mapped.credential, credentialMappings: oauth.credentialMappings }];
91
+ }
92
+ return declared.map((declaration) => ({
93
+ kind: "refresh",
94
+ endpoint: oauth.tokenEndpoint,
95
+ authentication: oauth.tokenEndpointAuthMethod ?? "client_secret_post",
96
+ encoding: manifestText(oauth.tokenRequestEncoding) || "form",
97
+ clientID: manifestText(oauth.clientIDCredential),
98
+ clientSecret: manifestText(oauth.clientSecretCredential),
99
+ refreshField: manifestText(declaration.refreshTokenCredential),
100
+ keyCredential: "",
101
+ assertion: {},
102
+ mappings: manifestList(declaration.credentialMappings).map(manifestObject),
103
+ copies: [],
104
+ scopes: declaration.grantedScopes === "userScopes" ? oauth.userScopes : refresh?.requiredScopes ?? oauth.scopes,
105
+ requestScopes: [],
106
+ terminalCodes: strings(refresh?.terminalErrorCodes, ["invalid_grant"]),
107
+ acceptedTokenTypes: strings(declaration.acceptedTokenTypes ?? refresh?.acceptedTokenTypes, ["Bearer"]),
108
+ exactScopes: declaration.exactScopes === true || declaration.exactScopes === void 0 && refresh?.exactScopes === true,
109
+ scopeSeparators: refresh?.scopeSeparators === "whitespaceAndComma" ? "whitespaceAndComma" : "whitespace",
110
+ allowsPublicClient: refresh?.allowsPublicClient === true,
111
+ acceptsMissingTokenType: declaration.acceptsMissingTokenType === void 0 ? refresh?.acceptsMissingTokenType === true : declaration.acceptsMissingTokenType === true,
112
+ acceptsMissingExpiresIn: refresh?.missingExpiry !== void 0,
113
+ missingExpiry: missingExpiryLifetime(refresh?.missingExpiry),
114
+ additionalParameters: stringRecord(refresh?.additionalParameters)
115
+ }));
116
+ }
117
+ if (method.clientCredentials) {
118
+ const client = manifestObject(method.clientCredentials);
119
+ return [{
120
+ kind: "client_credentials",
121
+ endpoint: client.tokenEndpoint,
122
+ authentication: client.tokenEndpointAuthMethod ?? "client_secret_post",
123
+ encoding: manifestText(client.tokenRequestEncoding) || "form",
124
+ clientID: manifestText(client.clientIDCredential),
125
+ clientSecret: manifestText(client.clientSecretCredential),
126
+ refreshField: "",
127
+ keyCredential: "",
128
+ assertion: {},
129
+ mappings: manifestList(client.credentialMappings).map(manifestObject),
130
+ copies: [],
131
+ scopes: client.scopes,
132
+ requestScopes: client.scopes,
133
+ terminalCodes: strings(client.terminalErrorCodes, ["invalid_client"]),
134
+ acceptedTokenTypes: strings(client.acceptedTokenTypes, ["Bearer"]),
135
+ exactScopes: false,
136
+ scopeSeparators: "whitespace",
137
+ allowsPublicClient: false,
138
+ acceptsMissingTokenType: client.acceptsMissingTokenType === true,
139
+ acceptsMissingExpiresIn: client.missingExpiry !== void 0,
140
+ missingExpiry: missingExpiryLifetime(client.missingExpiry),
141
+ additionalParameters: stringRecord(client.additionalParameters)
142
+ }];
143
+ }
144
+ const serviceAccount = manifestObject(method.serviceAccount);
145
+ if (!serviceAccount.jwtBearer) return [];
146
+ const bearer = manifestObject(serviceAccount.jwtBearer);
147
+ return [{
148
+ kind: "jwt_bearer",
149
+ endpoint: bearer.tokenEndpoint,
150
+ authentication: "",
151
+ encoding: "form",
152
+ clientID: "",
153
+ clientSecret: "",
154
+ refreshField: "",
155
+ keyCredential: manifestText(bearer.keyCredential),
156
+ assertion: bearer,
157
+ mappings: manifestList(bearer.credentialMappings).map(manifestObject),
158
+ copies: manifestList(bearer.credentialCopies).map(manifestObject),
159
+ scopes: bearer.scopes,
160
+ requestScopes: [],
161
+ terminalCodes: strings(bearer.terminalErrorCodes, ["invalid_grant"]),
162
+ acceptedTokenTypes: strings(bearer.acceptedTokenTypes, ["Bearer"]),
163
+ exactScopes: false,
164
+ scopeSeparators: "whitespace",
165
+ allowsPublicClient: false,
166
+ acceptsMissingTokenType: bearer.acceptsMissingTokenType === true,
167
+ acceptsMissingExpiresIn: bearer.missingExpiry !== void 0,
168
+ missingExpiry: missingExpiryLifetime(bearer.missingExpiry),
169
+ additionalParameters: {}
170
+ }];
171
+ }
172
+ async #exchange(grant, credentials, method, signal) {
173
+ let key = {};
174
+ if (grant.kind === "jwt_bearer") {
175
+ try {
176
+ key = manifestObject(JSON.parse(this.#credentialValue(credentials, grant.keyCredential)));
177
+ } catch {
178
+ throw new ReauthorizationRequiredError();
179
+ }
180
+ }
181
+ const endpoint = new TokenEndpoint({
182
+ providerName: this.#providerName,
183
+ url: this.#resolveSource(grant.endpoint, credentials, key),
184
+ terminalErrorCodes: grant.terminalCodes,
185
+ acceptedTokenTypes: grant.acceptedTokenTypes,
186
+ acceptsMissingTokenType: grant.acceptsMissingTokenType,
187
+ acceptsMissingExpiresIn: grant.acceptsMissingExpiresIn,
188
+ usesJSONRequestBody: grant.encoding === "json",
189
+ additionalRequestParameters: grant.additionalParameters,
190
+ credentialMappingPaths: grant.mappings.map((mapping) => manifestText(mapping.source)),
191
+ fetch: this.#fetch
192
+ });
193
+ if (grant.kind === "jwt_bearer") {
194
+ const assertion = grant.assertion;
195
+ const lifetimeMs = parseDurationMs(manifestText(assertion.lifetime));
196
+ let signed;
197
+ try {
198
+ signed = signJWTBearerAssertion({
199
+ issuer: this.#resolveSource(assertion.issuer, credentials, key),
200
+ audience: this.#resolveSource(assertion.audience, credentials, key),
201
+ ...assertion.subject ? { subject: this.#resolveSource(assertion.subject, credentials, key) } : {},
202
+ scope: this.#scopes(assertion.scopes).join(" "),
203
+ issuedAt: this.#now(),
204
+ lifetimeMs
205
+ }, parseRSAPrivateKeyPEM(this.#resolveSource(assertion.privateKey, credentials, key)));
206
+ } catch {
207
+ throw new ReauthorizationRequiredError();
208
+ }
209
+ return await endpoint.exchangeJWTBearerAssertion(signed, signal);
210
+ }
211
+ const secret = this.#credentialValue(credentials, grant.clientSecret);
212
+ if (!secret && !grant.allowsPublicClient && (grant.kind === "refresh" || this.#isRequired(method, grant.clientSecret)))
213
+ throw new ReauthorizationRequiredError();
214
+ const authenticationMethod = secret || grant.kind === "client_credentials" ? this.#resolveSource(grant.authentication, credentials, key) : "none";
215
+ const client = { id: this.#credentialValue(credentials, grant.clientID), secret: new SecretString(secret), authenticationMethod };
216
+ if (grant.kind === "refresh") {
217
+ return endpoint.exchangeRefreshToken(client, new SecretString(this.#credentialValue(credentials, grant.refreshField)), signal);
218
+ }
219
+ return endpoint.exchangeClientCredentials(client, this.#scopes(grant.requestScopes), signal);
220
+ }
221
+ #credentialValue(credentials, name) {
222
+ const property = this.#credentialFields[name];
223
+ if (!property) return "";
224
+ const value = credentials[property];
225
+ return value instanceof SecretString ? value.reveal() : typeof value === "string" ? value : "";
226
+ }
227
+ #isSecret(method, name) {
228
+ return manifestList(method.fields).map(manifestObject).some((field) => field.name === name && field.type === "secretString");
229
+ }
230
+ #isRequired(method, name) {
231
+ const field = manifestList(method.fields).map(manifestObject).find((candidate) => candidate.name === name);
232
+ return field ? field.required === true : true;
233
+ }
234
+ #validateGrant(grant) {
235
+ for (const mapping of grant.mappings) {
236
+ if (!manifestText(mapping.source) || !this.#credentialFields[manifestText(mapping.credential)])
237
+ throw new TypeError("manifest credential field is not mapped");
238
+ }
239
+ for (const copy of grant.copies) {
240
+ if (!this.#credentialFields[manifestText(copy.credential)] || !this.#credentialFields[manifestText(copy.fromCredential)])
241
+ throw new TypeError("manifest credential field is not mapped");
242
+ }
243
+ if (grant.missingExpiry && parseDurationMs(grant.missingExpiry) <= 0)
244
+ throw new TypeError("manifest grant duration must be positive");
245
+ if (grant.kind === "jwt_bearer" && parseDurationMs(manifestText(grant.assertion.lifetime)) < 1)
246
+ throw new TypeError("manifest JWT assertion lifetime must be at least one millisecond");
247
+ }
248
+ #scopes(source) {
249
+ if (Array.isArray(source)) return source.filter((value) => typeof value === "string");
250
+ const fromSetting = manifestObject(source).fromSetting;
251
+ return typeof fromSetting === "string" ? (this.#settings[fromSetting] ?? "").split(/\s+/).filter(Boolean) : [];
252
+ }
253
+ #resolveSource(source, credentials, key) {
254
+ if (typeof source === "string") return source;
255
+ const valueSource = manifestObject(source);
256
+ if (typeof valueSource.value === "string") return valueSource.value;
257
+ if (typeof valueSource.template === "string") return valueSource.template.replace(/\{([^{}]+)\}/g, (_, name) => pathEscape(this.#credentialFields[name] ? this.#credentialValue(credentials, name) : this.#settings[name] ?? ""));
258
+ let value = "";
259
+ if (typeof valueSource.fromCredential === "string") value = this.#credentialValue(credentials, valueSource.fromCredential);
260
+ else if (typeof valueSource.fromSetting === "string") value = this.#settings[valueSource.fromSetting] ?? "";
261
+ else if (typeof valueSource.fromKey === "string") value = manifestText(key[valueSource.fromKey]);
262
+ else throw new ReauthorizationRequiredError();
263
+ if (valueSource.values) {
264
+ const translated = manifestObject(valueSource.values)[value];
265
+ if (typeof translated !== "string") throw new ReauthorizationRequiredError();
266
+ return translated;
267
+ }
268
+ if (!value) throw new ReauthorizationRequiredError();
269
+ return value;
270
+ }
271
+ };
272
+ function manifestObject(value) {
273
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
274
+ }
275
+ function manifestList(value) {
276
+ return Array.isArray(value) ? value : [];
277
+ }
278
+ function manifestText(value) {
279
+ return typeof value === "string" ? value : "";
280
+ }
281
+ function strings(value, fallback) {
282
+ const found = manifestList(value).filter((item) => typeof item === "string");
283
+ return found.length ? found : fallback;
284
+ }
285
+ function stringRecord(value) {
286
+ return Object.fromEntries(Object.entries(manifestObject(value)).filter((entry) => typeof entry[1] === "string"));
287
+ }
288
+ function missingExpiryLifetime(value) {
289
+ return manifestText(manifestObject(value).lifetime);
290
+ }
291
+ function hasDeclaredScopes(returnedScopes, requiredScopes, separators, exact) {
292
+ const granted = new Set(returnedScopes.split(separators === "whitespaceAndComma" ? /[ ,]+/ : /\s+/).filter(Boolean));
293
+ const required = new Set(requiredScopes);
294
+ return (!exact || granted.size === required.size) && [...required].every((scope) => granted.has(scope));
295
+ }
296
+ function pathEscape(value) {
297
+ return encodeURIComponent(value).replace(/[!'()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`).replace(/%(?:2B|26|3D|3A|40|24)/g, (escaped) => ({ "%2B": "+", "%26": "&", "%3D": "=", "%3A": ":", "%40": "@", "%24": "$" })[escaped] ?? escaped);
298
+ }
299
+ function parseDurationMs(value) {
300
+ if (!value) return 0;
301
+ if (value === "0" || value === "+0" || value === "-0") return 0;
302
+ const sign2 = value[0] === "-" ? -1n : 1n;
303
+ const magnitude = value[0] === "+" || value[0] === "-" ? value.slice(1) : value;
304
+ const parts = magnitude.matchAll(/((?:\d+(?:\.\d*)?|\.\d+))(ns|us|µs|μs|ms|s|m|h)/g);
305
+ const nanoseconds = {
306
+ ns: 1n,
307
+ us: 1000n,
308
+ "\xB5s": 1000n,
309
+ "\u03BCs": 1000n,
310
+ ms: 1000000n,
311
+ s: 1000000000n,
312
+ m: 60000000000n,
313
+ h: 3600000000000n
314
+ };
315
+ let duration = 0n;
316
+ let position = 0;
317
+ for (const part of parts) {
318
+ if (part.index !== position) throw new TypeError("manifest grant duration is invalid");
319
+ const [whole, fraction = ""] = part[1].split(".");
320
+ const unit = nanoseconds[part[2]];
321
+ duration += BigInt(whole || "0") * unit;
322
+ if (fraction) duration += BigInt(fraction) * unit / 10n ** BigInt(fraction.length);
323
+ position += part[0].length;
324
+ }
325
+ if (position !== magnitude.length || duration === 0n || duration > (sign2 < 0n ? 9223372036854775808n : 9223372036854775807n))
326
+ throw new TypeError("manifest grant duration is invalid");
327
+ return Number(sign2 * duration) / 1e6;
328
+ }
329
+
330
+ // src/oauthtoken/index.ts
10
331
  var RefreshSkewMs = 3e5;
11
332
  var TokenResponse = class {
333
+ #mappedCredentialFields;
12
334
  /** Replacement access token. */
13
335
  accessToken;
14
336
  /** Rotated refresh token, empty when the provider retained it. */
@@ -32,11 +354,16 @@ var TokenResponse = class {
32
354
  this.scope = fields.scope;
33
355
  this.tokenType = fields.tokenType;
34
356
  this.retainedFields = fields.retainedFields;
357
+ this.#mappedCredentialFields = fields.mappedCredentialFields ?? {};
35
358
  }
36
359
  /** Keep the previous refresh token when no rotated value was returned. */
37
360
  nextRefreshToken(prior) {
38
361
  return this.refreshToken.reveal() ? this.refreshToken : prior;
39
362
  }
363
+ /** Reveal only one declared credential mapping at the provider boundary. */
364
+ mappedCredentialValue(path) {
365
+ return this.#mappedCredentialFields[path]?.reveal();
366
+ }
40
367
  };
41
368
  var TokenEndpoint = class {
42
369
  #options;
@@ -136,13 +463,26 @@ var TokenEndpoint = class {
136
463
  if (typeof accessToken !== "string" || !accessToken || !hasAcceptedExpiry || !hasAcceptedType) throw new Error(`${options.providerName} token response omitted required fields`);
137
464
  const retainedFields = {};
138
465
  for (const field of options.retainedResponseFields ?? []) if (Object.hasOwn(decoded, field)) retainedFields[field] = decoded[field];
466
+ const mappedCredentialFields = {};
467
+ for (const path of options.credentialMappingPaths ?? []) {
468
+ const value = responsePathValue(decoded, path);
469
+ if (value !== void 0) mappedCredentialFields[path] = new SecretString(value);
470
+ }
139
471
  const refreshToken = typeof decoded.refresh_token === "string" ? decoded.refresh_token : "";
140
472
  const refreshTokenExpiry = typeof decoded.refresh_token_expires_in === "number" && decoded.refresh_token_expires_in > 0 ? decoded.refresh_token_expires_in : 0;
141
- return new TokenResponse({ accessToken: new SecretString(accessToken), refreshToken: new SecretString(refreshToken), expiresInMs: (expiresIn ?? 0) * 1e3, refreshTokenExpiresInMs: refreshTokenExpiry * 1e3, scope: typeof decoded.scope === "string" ? decoded.scope : "", tokenType: typeof tokenType === "string" ? tokenType : "", retainedFields });
473
+ return new TokenResponse({ accessToken: new SecretString(accessToken), refreshToken: new SecretString(refreshToken), expiresInMs: (expiresIn ?? 0) * 1e3, refreshTokenExpiresInMs: refreshTokenExpiry * 1e3, scope: typeof decoded.scope === "string" ? decoded.scope : "", tokenType: typeof tokenType === "string" ? tokenType : "", retainedFields, mappedCredentialFields });
142
474
  }
143
475
  };
144
476
  var grantParameters = /* @__PURE__ */ new Set(["grant_type", "refresh_token", "client_id", "client_secret", "assertion", "client_assertion", "client_assertion_type"]);
145
477
  var standardResponseFields = /* @__PURE__ */ new Set(["access_token", "refresh_token", "expires_in", "refresh_token_expires_in", "scope", "token_type", "error", "error_description", "error_uri", "id_token"]);
478
+ function responsePathValue(response, path) {
479
+ let value = response;
480
+ for (const member of path.split(".")) {
481
+ if (!value || typeof value !== "object" || Array.isArray(value) || !Object.hasOwn(value, member)) return void 0;
482
+ value = value[member];
483
+ }
484
+ return typeof value === "string" ? value : void 0;
485
+ }
146
486
  function validateEndpointURL(value, allowsPlainHTTP, providerName) {
147
487
  let parsed;
148
488
  try {
@@ -222,6 +562,7 @@ function signJWTBearerAssertion(assertion, privateKey) {
222
562
  return new SecretString(`${unsigned}.${signature}`);
223
563
  }
224
564
  export {
565
+ ManifestCredentialRefreshDriver,
225
566
  RefreshSkewMs,
226
567
  TokenEndpoint,
227
568
  TokenResponse,
@@ -1,4 +1,5 @@
1
- import { O as OperationRef, f as BranchDefinition, Z as RetryPolicy, C as Call, M as MutationAttempt, Q as QueryAttempt } from './credentials-vrvW0d4j.js';
1
+ import { C as Call } from './credentials-JqG97kJ5.js';
2
+ import { O as OperationRef, e as BranchDefinition, J as RetryPolicy, M as MutationAttempt, Q as QueryAttempt } from './types-D6De3sry.js';
2
3
 
3
4
  /** Execute options owned by one operation; durations are milliseconds. */
4
5
  interface StepDefaults {
@@ -1,7 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  import { inspect } from 'node:util';
3
3
  import { S3Client } from '@aws-sdk/client-s3';
4
- import { t as CredentialProvider$1, C as Call, W as RefreshingCredentialProvider$1, X as RejectedCredentialRefreshingProvider, u as CredentialRefreshDriver } from '../credentials-vrvW0d4j.js';
4
+ import { c as CredentialProvider$1, C as Call, R as RefreshingCredentialProvider$1, h as RejectedCredentialRefreshingProvider, d as CredentialRefreshDriver } from '../credentials-JqG97kJ5.js';
5
+ import '../types-D6De3sry.js';
5
6
  import '@superdurable/dex';
6
7
 
7
8
  /** Current secret-free project configuration schema shared with Dex Web. */
@@ -1,13 +1,15 @@
1
+ import {
2
+ OperationRefSchema
3
+ } from "../chunk-BQGYGCOQ.js";
1
4
  import {
2
5
  BodyTooLargeError,
3
6
  isLocalStorageHost,
4
7
  isPrivateBrokerHost,
5
8
  readBoundedBody
6
- } from "../chunk-2IDXFKMY.js";
9
+ } from "../chunk-UWB627CI.js";
7
10
  import {
8
- OperationRefSchema,
9
11
  ReauthorizationRequiredError
10
- } from "../chunk-SOXYPIMO.js";
12
+ } from "../chunk-IHG6ADQG.js";
11
13
 
12
14
  // src/projectconfig/model.ts
13
15
  import { z } from "zod";
@@ -22,8 +22,21 @@ declare class ProviderTransportError extends Error {
22
22
  /** True only when the request failed before a usable connection could send HTTP bytes. */
23
23
  constructor(isProvablyNotSent: boolean);
24
24
  }
25
- /** Public-only HTTPS or explicitly owned private-network HTTP. */
26
- type AddressPolicy = 'publicHTTPS' | 'privateNetworkHTTP';
25
+ /** Provider emitted no response byte within the configured stall timeout. */
26
+ declare class ProviderStalledError extends Error {
27
+ /** Construct a sanitized no-byte timeout failure. */
28
+ constructor();
29
+ }
30
+ /** Public HTTPS, owned private HTTP, or caller-owned unrestricted transport. */
31
+ type AddressPolicy = 'publicHTTPS' | 'privateNetworkHTTP' | 'unrestricted';
32
+ /** Caller-owned transport for a provider exchange; implementations must not replay redirects.
33
+ * A client may return a complete body without calling onResponseChunk. If it calls the callback,
34
+ * it must feed every received chunk in order before returning and honor false by stopping the response.
35
+ */
36
+ interface ProviderHTTPClient {
37
+ /** Send one request and return its bounded response. */
38
+ send(request: ProviderRequest): Promise<ProviderResponse>;
39
+ }
27
40
  /** One bounded provider request; response bodies and URL values never enter errors. */
28
41
  interface ProviderRequest {
29
42
  /** Absolute provider URL. */ url: string;
@@ -31,9 +44,14 @@ interface ProviderRequest {
31
44
  /** Request headers, including provider credentials when needed. */ headers?: Record<string, string>;
32
45
  /** Caller-encoded request body. */ body?: Uint8Array;
33
46
  /** Maximum response body size in bytes. */ maxResponseBytes: number;
47
+ /** On a response limit, destroy the connection (default) or drain to EOF before rejecting. Drain requires a positive timeoutMs, positive stallTimeoutMs, or signal. */ onBodyLimit?: 'destroy' | 'drain';
48
+ /** Optional separate prefix limit for non-2xx error bodies; when set, its truncation takes precedence over drain. */ maxErrorResponseBytes?: number;
49
+ /** Classify a non-2xx status even when its diagnostic body fails to read. */ ignoreErrorBodyReadFailure?: boolean;
34
50
  /** Network policy checked against all resolved addresses. */ addressPolicy: AddressPolicy;
35
51
  /** Request deadline in milliseconds. */ timeoutMs?: number;
36
52
  /** Caller cancellation. */ signal?: AbortSignal;
53
+ /** Observe chunks in arrival order; returning false stops a completed event stream. */ onResponseChunk?: (chunk: Uint8Array, status: number, headers: http.IncomingHttpHeaders) => boolean | void;
54
+ /** Abort a response when no byte arrives for this many milliseconds. */ stallTimeoutMs?: number;
37
55
  }
38
56
  /** Bounded response; redirects remain visible as 3xx responses. */
39
57
  interface ProviderResponse {
@@ -54,4 +72,4 @@ declare function readErrorTokens(body: Uint8Array, pointers: readonly string[]):
54
72
  /** Send once using pinned, policy-checked DNS results and no redirect replay. */
55
73
  declare function sendProviderRequest(request: ProviderRequest): Promise<ProviderResponse>;
56
74
 
57
- export { type AddressPolicy, AddressRefusal, BodyTooLargeError, MaxErrorBodyBytes, MaxRetryAfterDelayMs, type ProviderRequest, type ProviderResponse, ProviderTransportError, isHeaderSafeCredential, parseRetryAfter, readBoundedBody, readErrorTokens, sendProviderRequest, validateBaseURL };
75
+ export { type AddressPolicy, AddressRefusal, BodyTooLargeError, MaxErrorBodyBytes, MaxRetryAfterDelayMs, type ProviderHTTPClient, type ProviderRequest, type ProviderResponse, ProviderStalledError, ProviderTransportError, isHeaderSafeCredential, parseRetryAfter, readBoundedBody, readErrorTokens, sendProviderRequest, validateBaseURL };
@@ -3,6 +3,7 @@ import {
3
3
  BodyTooLargeError,
4
4
  MaxErrorBodyBytes,
5
5
  MaxRetryAfterDelayMs,
6
+ ProviderStalledError,
6
7
  ProviderTransportError,
7
8
  isHeaderSafeCredential,
8
9
  parseRetryAfter,
@@ -10,12 +11,13 @@ import {
10
11
  readErrorTokens,
11
12
  sendProviderRequest,
12
13
  validateBaseURL
13
- } from "../chunk-2IDXFKMY.js";
14
+ } from "../chunk-UWB627CI.js";
14
15
  export {
15
16
  AddressRefusal,
16
17
  BodyTooLargeError,
17
18
  MaxErrorBodyBytes,
18
19
  MaxRetryAfterDelayMs,
20
+ ProviderStalledError,
19
21
  ProviderTransportError,
20
22
  isHeaderSafeCredential,
21
23
  parseRetryAfter,
@@ -0,0 +1,42 @@
1
+ import { C as Call, S as SecretString } from './credentials-JqG97kJ5.js';
2
+ import { ProviderHTTPClient } from './providerhttp/index.js';
3
+ import { Q as QueryAttempt } from './types-D6De3sry.js';
4
+ import { b as Query, Q as QueryDefinition } from './operation-CASLOg86.js';
5
+ import { T as TextGenerationRequest, q as TextGenerationResponse, W as WireFormat } from './wire_format-stEVruMs.js';
6
+
7
+ /** Configuration for one generateText Query; durations are milliseconds. */
8
+ interface TextGenerationQueryConfig {
9
+ /** Generated connector operation definition and sync Step defaults. */ definition: QueryDefinition;
10
+ /** Provider-specific adapter. */ wireFormat: WireFormat;
11
+ /** Validated provider base URL. */ baseURL: string;
12
+ /** Default model; blank requires a request model. */ connectionModel?: string;
13
+ /** HTTP exchange deadline. */ requestTimeoutMs: number;
14
+ /** Optional caller-owned HTTP transport, as with Go's HTTPClient. It must not replay redirects. */ httpClient?: ProviderHTTPClient;
15
+ /** Resolve private credential once per validated attempt. */ resolveCredential: (call: Call) => string | SecretString | Promise<string | SecretString>;
16
+ /** Maximum complete body or event stream bytes. */ maxResponseBytes: number;
17
+ /** Maximum one SSE event bytes; required for streaming. */ maxStreamEventBytes?: number;
18
+ }
19
+ /** Shared provider-neutral text generation Query; concurrent invocations own isolated state.
20
+ *
21
+ * Each invocation resolves the model, validates portable fields in order, applies model rules,
22
+ * resolves one credential, encodes the request, dispatches once, and classifies the response.
23
+ * Validation or configuration failure in the first five stages selects defect with zero requests.
24
+ * A silent request records a nil Dex heartbeat every five seconds, clearing a caller checkpoint.
25
+ * Stream text can include retried, blocked, or invalid attempts; only the final Result is authoritative.
26
+ * Sync Execute durability prevents a long local attempt from dispatching the billed call again.
27
+ * Structured numbers are bounded to 256 literal characters and exponent magnitude 400.
28
+ */
29
+ declare class TextGenerationQuery implements Query<TextGenerationRequest, TextGenerationResponse> {
30
+ #private;
31
+ /** Validate configuration before a provider request can be made. */
32
+ constructor(config: TextGenerationQueryConfig);
33
+ /** Return the connector's generateText definition. */ definition(): QueryDefinition;
34
+ /** Return optional request features declared by the provider. */ requestFeatures(): Readonly<WireFormat['features']>;
35
+ /** Return the provider model ID rule. */ modelIDRule(): WireFormat['modelIDRule'];
36
+ /** Validate in fixed order, dispatch once, and classify a branch or safe retry without exposing secrets. */
37
+ invoke(call: Call, request: TextGenerationRequest): Promise<QueryAttempt<TextGenerationResponse>>;
38
+ }
39
+ /** Construct a generateText Query, rejecting invalid definitions, wire formats, models, timeouts, and size limits before dispatch. */
40
+ declare function newTextGenerationQuery(config: TextGenerationQueryConfig): TextGenerationQuery;
41
+
42
+ export { TextGenerationQuery as T, type TextGenerationQueryConfig as a, newTextGenerationQuery as n };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { C as Call, a as CallID } from '../credentials-vrvW0d4j.js';
2
+ import { C as CallID } from '../types-D6De3sry.js';
3
+ import { C as Call } from '../credentials-JqG97kJ5.js';
3
4
  import '@superdurable/dex';
4
5
 
5
6
  /** The current attempt has no permission to send another provider write. */
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  CallIDSchema
3
- } from "../chunk-SOXYPIMO.js";
3
+ } from "../chunk-BQGYGCOQ.js";
4
+ import "../chunk-IHG6ADQG.js";
4
5
 
5
6
  // src/singledispatch/index.ts
6
7
  import { z } from "zod";
@@ -0,0 +1,15 @@
1
+ import { T as TextGenerationRequest } from '../wire_format-stEVruMs.js';
2
+ export { B as BlockedBranchID, C as CredentialHeader, D as DecodedResponse, a as DefectBranchID, E as EncodeRequestInput, b as EncodedRequest, c as ErrorOutcome, d as ErrorRule, F as FinishReason, G as GeneratedBranchID, I as InvalidResponseBranchID, M as Message, e as MessageRole, f as ModelIDRule, g as ModelRequestRules, P as ProviderRejectedBranchID, h as ProviderReportedError, R as ReasoningEffort, i as RequestFeatures, j as ResponsePart, S as ServerSentEvent, k as StreamDecodeState, l as StructuredOutput, m as StructuredOutputMode, n as StructuredOutputRules, o as TemperaturePolicy, p as TextGenerationOperationID, q as TextGenerationResponse, r as TruncatedBranchID, U as Usage, W as WireFormat, s as blockedOutcome, t as classifyError, u as invalidResponseOutcome, v as providerRejectedOutcome, w as quotaExhaustedOutcome, x as resolveModel, y as retryOutcome, z as temperatureAccepted, A as temperatureNotAccepted, H as temperatureRange, J as textGenerationBranchDefinitions, K as validateModelID, L as validatePortableRequest, N as validateStructuredOutputRules, O as validateWireFormat } from '../wire_format-stEVruMs.js';
3
+ export { T as TextGenerationQuery, a as TextGenerationQueryConfig, n as newTextGenerationQuery } from '../query-CeUaW9xU.js';
4
+ import '../types-D6De3sry.js';
5
+ import 'zod';
6
+ import '../credentials-JqG97kJ5.js';
7
+ import '@superdurable/dex';
8
+ import '../providerhttp/index.js';
9
+ import 'node:http';
10
+ import '../operation-CASLOg86.js';
11
+
12
+ /** Remove whitespace and one JSON Markdown fence, then validate the original schema. */
13
+ declare function extractStructuredAnswer(request: TextGenerationRequest, generatedText: string): string;
14
+
15
+ export { TextGenerationRequest, extractStructuredAnswer };