@layervai/qurl 3.1.0 → 4.0.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.
Files changed (152) hide show
  1. package/README.md +259 -46
  2. package/dist/cjs/client-connectors.d.ts +44 -0
  3. package/dist/cjs/client-connectors.js +189 -0
  4. package/dist/cjs/client-delegated.d.ts +30 -0
  5. package/dist/cjs/client-delegated.js +312 -0
  6. package/dist/cjs/client-errors.d.ts +44 -0
  7. package/dist/cjs/client-errors.js +219 -0
  8. package/dist/cjs/client-http-types.d.ts +37 -0
  9. package/dist/cjs/client-http-types.js +2 -0
  10. package/dist/cjs/client-input-fields.d.ts +53 -0
  11. package/dist/cjs/client-input-fields.js +262 -0
  12. package/dist/cjs/client-limits.d.ts +36 -0
  13. package/dist/cjs/client-limits.js +61 -0
  14. package/dist/cjs/client-management.d.ts +12 -0
  15. package/dist/cjs/client-management.js +119 -0
  16. package/dist/cjs/client-paths.d.ts +21 -0
  17. package/dist/cjs/client-paths.js +143 -0
  18. package/dist/cjs/client-portals.d.ts +82 -0
  19. package/dist/cjs/client-portals.js +244 -0
  20. package/dist/cjs/client-resources.d.ts +29 -0
  21. package/dist/cjs/client-resources.js +331 -0
  22. package/dist/cjs/client-validation.d.ts +35 -0
  23. package/dist/cjs/client-validation.js +326 -0
  24. package/dist/cjs/client.d.ts +45 -75
  25. package/dist/cjs/client.js +621 -2448
  26. package/dist/cjs/errors.d.ts +9 -1
  27. package/dist/cjs/errors.js +10 -1
  28. package/dist/cjs/index.d.ts +4 -2
  29. package/dist/cjs/index.js +4 -1
  30. package/dist/cjs/response-metadata.d.ts +13 -0
  31. package/dist/cjs/response-metadata.js +38 -0
  32. package/dist/cjs/share.js +2 -0
  33. package/dist/cjs/target-path.d.ts +2 -0
  34. package/dist/cjs/target-path.js +34 -0
  35. package/dist/cjs/types.d.ts +83 -7
  36. package/dist/cjs/version.d.ts +1 -1
  37. package/dist/cjs/version.js +1 -1
  38. package/dist/esm/client-connectors.d.ts +44 -0
  39. package/dist/esm/client-connectors.js +180 -0
  40. package/dist/esm/client-delegated.d.ts +30 -0
  41. package/dist/esm/client-delegated.js +295 -0
  42. package/dist/esm/client-errors.d.ts +44 -0
  43. package/dist/esm/client-errors.js +206 -0
  44. package/dist/esm/client-http-types.d.ts +37 -0
  45. package/dist/esm/client-http-types.js +1 -0
  46. package/dist/esm/client-input-fields.d.ts +53 -0
  47. package/dist/esm/client-input-fields.js +258 -0
  48. package/dist/esm/client-limits.d.ts +36 -0
  49. package/dist/esm/client-limits.js +58 -0
  50. package/dist/esm/client-management.d.ts +12 -0
  51. package/dist/esm/client-management.js +113 -0
  52. package/dist/esm/client-paths.d.ts +21 -0
  53. package/dist/esm/client-paths.js +138 -0
  54. package/dist/esm/client-portals.d.ts +82 -0
  55. package/dist/esm/client-portals.js +236 -0
  56. package/dist/esm/client-resources.d.ts +29 -0
  57. package/dist/esm/client-resources.js +323 -0
  58. package/dist/esm/client-validation.d.ts +35 -0
  59. package/dist/esm/client-validation.js +302 -0
  60. package/dist/esm/client.d.ts +45 -76
  61. package/dist/esm/client.js +318 -2147
  62. package/dist/esm/crid.d.ts +0 -1
  63. package/dist/esm/crid.js +0 -1
  64. package/dist/esm/errors.d.ts +9 -2
  65. package/dist/esm/errors.js +9 -1
  66. package/dist/esm/index.d.ts +4 -3
  67. package/dist/esm/index.js +2 -2
  68. package/dist/esm/internal.d.ts +0 -1
  69. package/dist/esm/internal.js +0 -1
  70. package/dist/esm/node/agent-runtime.d.ts +0 -1
  71. package/dist/esm/node/agent-runtime.js +0 -1
  72. package/dist/esm/node/agent-state.d.ts +0 -1
  73. package/dist/esm/node/agent-state.js +0 -1
  74. package/dist/esm/node/agent-transport.d.ts +0 -1
  75. package/dist/esm/node/agent-transport.js +0 -1
  76. package/dist/esm/node/deployment.d.ts +0 -1
  77. package/dist/esm/node/deployment.js +0 -1
  78. package/dist/esm/node/file-agent-state.d.ts +0 -1
  79. package/dist/esm/node/file-agent-state.js +0 -1
  80. package/dist/esm/node/native-udp.d.ts +0 -1
  81. package/dist/esm/node/native-udp.js +0 -1
  82. package/dist/esm/node/nhp-wire.d.ts +0 -1
  83. package/dist/esm/node/nhp-wire.js +0 -1
  84. package/dist/esm/node/portal-opener.d.ts +0 -1
  85. package/dist/esm/node/portal-opener.js +0 -1
  86. package/dist/esm/node/provider.d.ts +0 -1
  87. package/dist/esm/node/provider.js +0 -1
  88. package/dist/esm/node/qv2.d.ts +0 -1
  89. package/dist/esm/node/qv2.js +0 -1
  90. package/dist/esm/node/relay.d.ts +0 -1
  91. package/dist/esm/node/relay.js +0 -1
  92. package/dist/esm/node/sealed-agent-state.d.ts +0 -1
  93. package/dist/esm/node/sealed-agent-state.js +0 -1
  94. package/dist/esm/node/strict-json.d.ts +0 -1
  95. package/dist/esm/node/strict-json.js +0 -1
  96. package/dist/esm/node.d.ts +0 -1
  97. package/dist/esm/node.js +0 -1
  98. package/dist/esm/response-metadata.d.ts +13 -0
  99. package/dist/esm/response-metadata.js +33 -0
  100. package/dist/esm/share.d.ts +0 -1
  101. package/dist/esm/share.js +2 -1
  102. package/dist/esm/target-path.d.ts +2 -0
  103. package/dist/esm/target-path.js +30 -0
  104. package/dist/esm/types.d.ts +83 -8
  105. package/dist/esm/types.js +0 -1
  106. package/dist/esm/version.d.ts +1 -2
  107. package/dist/esm/version.js +1 -2
  108. package/package.json +9 -7
  109. package/dist/esm/client.d.ts.map +0 -1
  110. package/dist/esm/client.js.map +0 -1
  111. package/dist/esm/crid.d.ts.map +0 -1
  112. package/dist/esm/crid.js.map +0 -1
  113. package/dist/esm/errors.d.ts.map +0 -1
  114. package/dist/esm/errors.js.map +0 -1
  115. package/dist/esm/index.d.ts.map +0 -1
  116. package/dist/esm/index.js.map +0 -1
  117. package/dist/esm/internal.d.ts.map +0 -1
  118. package/dist/esm/internal.js.map +0 -1
  119. package/dist/esm/node/agent-runtime.d.ts.map +0 -1
  120. package/dist/esm/node/agent-runtime.js.map +0 -1
  121. package/dist/esm/node/agent-state.d.ts.map +0 -1
  122. package/dist/esm/node/agent-state.js.map +0 -1
  123. package/dist/esm/node/agent-transport.d.ts.map +0 -1
  124. package/dist/esm/node/agent-transport.js.map +0 -1
  125. package/dist/esm/node/deployment.d.ts.map +0 -1
  126. package/dist/esm/node/deployment.js.map +0 -1
  127. package/dist/esm/node/file-agent-state.d.ts.map +0 -1
  128. package/dist/esm/node/file-agent-state.js.map +0 -1
  129. package/dist/esm/node/native-udp.d.ts.map +0 -1
  130. package/dist/esm/node/native-udp.js.map +0 -1
  131. package/dist/esm/node/nhp-wire.d.ts.map +0 -1
  132. package/dist/esm/node/nhp-wire.js.map +0 -1
  133. package/dist/esm/node/portal-opener.d.ts.map +0 -1
  134. package/dist/esm/node/portal-opener.js.map +0 -1
  135. package/dist/esm/node/provider.d.ts.map +0 -1
  136. package/dist/esm/node/provider.js.map +0 -1
  137. package/dist/esm/node/qv2.d.ts.map +0 -1
  138. package/dist/esm/node/qv2.js.map +0 -1
  139. package/dist/esm/node/relay.d.ts.map +0 -1
  140. package/dist/esm/node/relay.js.map +0 -1
  141. package/dist/esm/node/sealed-agent-state.d.ts.map +0 -1
  142. package/dist/esm/node/sealed-agent-state.js.map +0 -1
  143. package/dist/esm/node/strict-json.d.ts.map +0 -1
  144. package/dist/esm/node/strict-json.js.map +0 -1
  145. package/dist/esm/node.d.ts.map +0 -1
  146. package/dist/esm/node.js.map +0 -1
  147. package/dist/esm/share.d.ts.map +0 -1
  148. package/dist/esm/share.js.map +0 -1
  149. package/dist/esm/types.d.ts.map +0 -1
  150. package/dist/esm/types.js.map +0 -1
  151. package/dist/esm/version.d.ts.map +0 -1
  152. package/dist/esm/version.js.map +0 -1
@@ -1,50 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConnectorResource = exports.ProtectedResource = exports.QURLClient = exports.SIGNED_FRAGMENT_RE = void 0;
4
- exports.isApiKeyRequestScope = isApiKeyRequestScope;
5
- const crid_js_1 = require("./crid.js");
3
+ exports.ConnectorResource = exports.ProtectedResource = exports.SIGNED_FRAGMENT_RE = exports.isApiKeyRequestScope = exports.QURLClient = void 0;
6
4
  const errors_js_1 = require("./errors.js");
5
+ const response_metadata_js_1 = require("./response-metadata.js");
7
6
  const internal_js_1 = require("./internal.js");
8
- const share_js_1 = require("./share.js");
9
7
  const version_js_1 = require("./version.js");
8
+ const share_js_1 = require("./share.js");
9
+ const client_limits_js_1 = require("./client-limits.js");
10
+ const client_errors_js_1 = require("./client-errors.js");
11
+ const client_validation_js_1 = require("./client-validation.js");
12
+ const client_input_fields_js_1 = require("./client-input-fields.js");
13
+ const client_resources_js_1 = require("./client-resources.js");
14
+ const client_portals_js_1 = require("./client-portals.js");
15
+ const client_connectors_js_1 = require("./client-connectors.js");
16
+ const client_paths_js_1 = require("./client-paths.js");
17
+ const client_delegated_js_1 = require("./client-delegated.js");
18
+ const client_management_js_1 = require("./client-management.js");
10
19
  const DEFAULT_BASE_URL = "https://api.layerv.ai";
11
20
  const DEFAULT_MAX_RETRIES = 3;
12
21
  const DEFAULT_TIMEOUT = 30_000;
13
- // Match qurl-go c4f2f98's exact API response cap. Current qurl-service list
14
- // routes cap pages at 100 items. A caller can still exceed this cap with a
15
- // large, heavily escaped webhook-delivery page, so list callers must request a
16
- // smaller page if the service reports the body-limit error. This cap is fixed:
17
- // do not add an override or widen one SDK independently from the other.
18
- // The limit is enforced from Content-Length when usable and again while
19
- // consuming the response stream.
20
- const MAX_RESPONSE_BODY_BYTES = 1 << 20;
21
- const MAX_ERROR_SNIPPET_BYTES = 512;
22
- // UTF-8 uses at least one byte per UTF-16 code unit. Reading more source units
23
- // cannot add a byte that fits in a 512-byte retained diagnostic.
24
- const MAX_ERROR_SNIPPET_SOURCE_CODE_UNITS = MAX_ERROR_SNIPPET_BYTES;
25
- const MAX_INVALID_FIELD_ENTRIES = 100;
26
- const MAX_DEBUG_BODY_KEYS = 100;
27
- // Bound each retained diagnostic collection as well as each string. Without
28
- // this aggregate cap, 100 valid 512-byte keys and values could turn a small
29
- // server error into a large caller-visible object. This is a diagnostic budget,
30
- // not part of the API wire contract.
31
- const MAX_DIAGNOSTIC_COLLECTION_BYTES = 8 << 10;
32
- const TEXT_ENCODER = new TextEncoder();
33
- const ERROR_SNIPPET_ELLIPSIS = "...";
34
- const ERROR_SNIPPET_ELLIPSIS_BYTES = 3;
35
- // RFC 8259 JSON exchanged between systems must be valid UTF-8. A non-fatal
36
- // decoder would replace malformed bytes with U+FFFD before JSON.parse and hide
37
- // the producer's exact contract violation.
38
- const TEXT_DECODER = new TextDecoder("utf-8", { fatal: true });
39
- const RETRY_BASE_DELAY_MS = 500;
40
- // Bounds local exponential backoff (NOT server-asserted Retry-After —
41
- // see `RETRY_AFTER_HARD_CAP_MS` for that).
42
- const RETRY_MAX_DELAY_MS = 30_000;
43
- // Hard cap on server-asserted Retry-After. Guards against `Retry-After`
44
- // values >2^31-1 ms overflowing Node's setTimeout (silently truncated to
45
- // 1ms — turns a "wait a month" directive into a hot retry loop).
46
- const RETRY_AFTER_HARD_CAP_MS = 60 * 60 * 1000;
47
- const RETRY_AFTER_PARSE_LIMIT_S = RETRY_AFTER_HARD_CAP_MS / 1000;
48
22
  const RETRYABLE_STATUS = new Set([429, 502, 503, 504]);
49
23
  // Keep mutating status retries 429-only until the service-side idempotency
50
24
  // cache contract is proven across 5xx, TTL, and replay-window edge cases.
@@ -54,12 +28,11 @@ const REDIRECT_RESPONSE_STATUSES = new Set([300, 301, 302, 303, 305, 307, 308]);
54
28
  const NO_RETRYABLE_STATUSES = new Set();
55
29
  const IDEMPOTENCY_KEY_METHODS = new Set(["POST", "PATCH"]);
56
30
  const MUTATING_RETRY_METHODS = new Set(["POST", "PATCH"]);
57
- const MAX_IDEMPOTENCY_KEY = 256;
58
31
  const IDEMPOTENCY_KEY_VALUE_RE = /^[\x21-\x7e](?:[\x20-\x7e]*[\x21-\x7e])?$/;
59
32
  const UUID_HEX = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
60
33
  class ResponseBodyTooLargeError extends Error {
61
- constructor() {
62
- super(`Response body exceeds ${MAX_RESPONSE_BODY_BYTES}-byte limit`);
34
+ constructor(limit) {
35
+ super(`Response body exceeds ${limit}-byte limit`);
63
36
  this.name = "ResponseBodyTooLargeError";
64
37
  }
65
38
  }
@@ -78,288 +51,22 @@ class ResponseBodyMaterializationError extends Error {
78
51
  const NO_PASSTHROUGH_STATUSES = [];
79
52
  const BATCH_PASSTHROUGH_STATUSES = [400];
80
53
  const DELEGATED_BATCH_PASSTHROUGH_STATUSES = [304];
81
- /** Allowlist of known query-param keys for the `list()` endpoint. */
82
- const LIST_PARAM_KEYS = [
83
- "limit",
84
- "cursor",
85
- "status",
86
- "q",
87
- "sort",
88
- "created_after",
89
- "created_before",
90
- "expires_before",
91
- "expires_after",
92
- ];
93
- const REQUEST_OPTION_KEYS = ["idempotencyKey"];
94
- const DELEGATED_BATCH_INPUT_KEYS = [
95
- "mint_capability",
96
- "grants",
97
- ];
98
- const DELEGATED_BATCH_GRANT_KEYS = [
99
- "expires_in",
100
- "label",
101
- "one_time_use",
102
- "max_sessions",
103
- "session_duration",
104
- "access_policy",
105
- ];
106
- const DELEGATED_BATCH_CREATE_OPTION_KEYS = [
107
- "idempotencyKey",
108
- ];
109
- const DELEGATED_BATCH_GET_OPTION_KEYS = [
110
- "etag",
111
- ];
112
- // Compile-time witness: `Exclude<keyof X, (typeof KEYS)[number]>` is
113
- // `never` iff KEYS lists every key of X. Paired with the
114
- // `satisfies readonly (keyof X)[]` clause on the array (which catches
115
- // the other direction), this gives bidirectional drift detection on
116
- // the *_KEYS allowlists at zero runtime cost.
117
- function assertExhaustive(_) {
118
- /* type-only check */
119
- }
120
- assertExhaustive(true);
121
- assertExhaustive(true);
122
- assertExhaustive(true);
123
- assertExhaustive(true);
124
- assertExhaustive(true);
125
- assertExhaustive(true);
126
- const CREATE_FIELD_KEYS = [
127
- "private",
128
- "type",
129
- "target_url",
130
- "expires_in",
131
- "one_time_use",
132
- "max_sessions",
133
- "session_duration",
134
- "label",
135
- "access_policy",
136
- "custom_domain",
137
- ];
138
- assertExhaustive(true);
139
- /**
140
- * Fields accepted by `update()`. The empty-input pre-flight check in
141
- * {@link QURLClient.update} iterates this const — so adding a new field to
142
- * {@link UpdateInput} without listing it here will fail the
143
- * paired `assertExhaustive` witness at compile time rather than silently
144
- * sneaking through an empty-input request.
145
- */
146
- const UPDATE_FIELD_KEYS = [
147
- "extend_by",
148
- "expires_at",
149
- "description",
150
- "tags",
151
- ];
152
- assertExhaustive(true);
153
- /**
154
- * Fields accepted by `mintLink()`. Iterated by the null-stripping
155
- * normalization loop in {@link QURLClient.mintLink} so unknown keys
156
- * from untyped-JS callers don't leak through to the wire body.
157
- * Symmetric with `LIST_PARAM_KEYS` / `UPDATE_FIELD_KEYS`.
158
- */
159
- const MINT_FIELD_KEYS = [
160
- "expires_in",
161
- "expires_at",
162
- "label",
163
- "one_time_use",
164
- "max_sessions",
165
- "session_duration",
166
- "access_policy",
167
- ];
168
- assertExhaustive(true);
169
- const RESOURCE_LIST_PARAM_KEYS = [
170
- "cursor",
171
- "limit",
172
- "alias",
173
- "slug",
174
- "status",
175
- "type",
176
- ];
177
- assertExhaustive(true);
178
- const CREATE_RESOURCE_FIELD_KEYS = [
179
- "private",
180
- "consumer_access",
181
- "type",
182
- "target_url",
183
- "description",
184
- "tags",
185
- "custom_domain",
186
- "alias",
187
- "slug",
188
- "find_or_create",
189
- ];
190
- assertExhaustive(true);
191
- const UPDATE_RESOURCE_FIELD_KEYS = [
192
- "allowed_device_keys_add",
193
- "allowed_device_keys_remove",
194
- "consumer_access",
195
- "expected_consumer_access_version",
196
- "description",
197
- "tags",
198
- "custom_domain",
199
- "preserve_host",
200
- "alias",
201
- ];
202
- assertExhaustive(true);
203
- const CREATE_QURL_FOR_RESOURCE_FIELD_KEYS = [
204
- "expires_in",
205
- "one_time_use",
206
- "max_sessions",
207
- "session_duration",
208
- "label",
209
- "access_policy",
210
- "target_path",
211
- ];
212
- const SHARE_RESOURCE_OPTION_KEYS = [
213
- "ttlSeconds",
214
- ];
215
- assertExhaustive(true);
216
- assertExhaustive(true);
217
- /**
218
- * Options accepted by the portal-verb `protectUrl` / `createPortal`
219
- * surface. These are camelCase caller-facing options (the portal verbs are
220
- * the idiomatic layer); the build helpers map them onto the same wire
221
- * fields the REST-shaped methods send.
222
- */
223
- const PROTECT_URL_OPTION_KEYS = [
224
- "private",
225
- "consumerAccess",
226
- "description",
227
- "tags",
228
- "customDomain",
229
- "alias",
230
- ];
231
- assertExhaustive(true);
232
- const CREATE_PORTAL_OPTION_KEYS = [
233
- "validFor",
234
- "label",
235
- "oneTimeUse",
236
- "maxSessions",
237
- "sessionDuration",
238
- "targetPath",
239
- ];
240
- assertExhaustive(true);
241
- const UPDATE_RESOURCE_QURL_FIELD_KEYS = [
242
- "extend_by",
243
- "expires_at",
244
- "label",
245
- "access_policy",
246
- "max_sessions",
247
- "session_duration",
248
- ];
249
- assertExhaustive(true);
250
- const UPDATE_CUSTOMER_FIELD_KEYS = [
251
- "spending_cap_cents",
252
- ];
253
- assertExhaustive(true);
254
- const CREATE_BILLING_CHECKOUT_FIELD_KEYS = [
255
- "plan",
256
- ];
257
- assertExhaustive(true);
258
- const REGISTER_DOMAIN_FIELD_KEYS = [
259
- "domain",
260
- ];
261
- assertExhaustive(true);
262
- const CREATE_WEBHOOK_FIELD_KEYS = [
263
- "url",
264
- "events",
265
- "description",
266
- ];
267
- assertExhaustive(true);
268
- const UPDATE_WEBHOOK_FIELD_KEYS = [
269
- "url",
270
- "events",
271
- "description",
272
- "status",
273
- ];
274
- assertExhaustive(true);
275
- const CREATE_API_KEY_FIELD_KEYS = [
276
- "kind",
277
- "name",
278
- "scopes",
279
- "target",
280
- "claims",
281
- "expires_in",
282
- ];
283
- assertExhaustive(true);
284
- const UPDATE_API_KEY_FIELD_KEYS = [
285
- "name",
286
- "scopes",
287
- ];
288
- assertExhaustive(true);
289
- const REDEEM_ACCESS_CODE_FIELD_KEYS = [
290
- "code",
291
- "honeypot",
292
- "elapsed_ms",
293
- ];
294
- assertExhaustive(true);
295
- const CREATE_ACCESS_CODE_FIELD_KEYS = [
296
- "resource_id",
297
- "name",
298
- "max_uses",
299
- "expires_at",
300
- ];
301
- assertExhaustive(true);
302
- const CONNECTOR_INSTALLATION_LIST_PARAM_KEYS = [
303
- "cursor",
304
- "limit",
305
- ];
306
- assertExhaustive(true);
307
- const BILLING_INVOICE_LIST_PARAM_KEYS = [
308
- "limit",
309
- "cursor",
310
- ];
311
- assertExhaustive(true);
312
- const DOMAIN_LIST_PARAM_KEYS = [
313
- "limit",
314
- "cursor",
315
- ];
316
- assertExhaustive(true);
317
- const WEBHOOK_LIST_PARAM_KEYS = [
318
- "limit",
319
- "cursor",
320
- ];
321
- assertExhaustive(true);
322
- const WEBHOOK_DELIVERY_LIST_PARAM_KEYS = [
323
- "limit",
324
- "cursor",
325
- ];
326
- assertExhaustive(true);
327
- const API_KEY_LIST_PARAM_KEYS = [
328
- "limit",
329
- "cursor",
330
- "status",
331
- ];
332
- assertExhaustive(true);
333
- /**
334
- * Construct a {@link ValidationError} for a client-side pre-flight check.
335
- * Uses `status: 0` (matching {@link NetworkError}/{@link TimeoutError}) and
336
- * `code: "client_validation"` so catch-by-class still works and callers can
337
- * tell the error originated inside the SDK rather than from the API.
338
- */
339
- function clientValidationError(detail) {
340
- return new errors_js_1.ValidationError({
341
- status: 0,
342
- code: errors_js_1.ERROR_CODE_CLIENT_VALIDATION,
343
- title: "Invalid Argument",
344
- detail,
345
- });
346
- }
347
54
  function validateRequestOptions(options) {
348
55
  if (options === undefined) {
349
56
  return;
350
57
  }
351
58
  if (typeof options !== "object" || options === null || Array.isArray(options)) {
352
- throw clientValidationError(`request options: must be an object (got ${options === null ? "null" : describeShape(options)})`);
59
+ throw (0, client_errors_js_1.clientValidationError)(`request options: must be an object (got ${options === null ? "null" : (0, client_validation_js_1.describeShape)(options)})`);
353
60
  }
354
- requireNoUnknownFields(options, REQUEST_OPTION_KEYS, "request options");
61
+ (0, client_validation_js_1.requireNoUnknownFields)(options, client_input_fields_js_1.REQUEST_OPTION_KEYS, "request options");
355
62
  const key = options.idempotencyKey;
356
63
  if (key !== undefined) {
357
64
  if (typeof key !== "string" || key.length === 0) {
358
- throw clientValidationError("idempotencyKey: must be a non-empty string");
65
+ throw (0, client_errors_js_1.clientValidationError)("idempotencyKey: must be a non-empty string");
359
66
  }
360
- requireMaxLength(key, "idempotencyKey", MAX_IDEMPOTENCY_KEY);
67
+ (0, client_validation_js_1.requireMaxLength)(key, "idempotencyKey", client_limits_js_1.MAX_IDEMPOTENCY_KEY);
361
68
  if (!IDEMPOTENCY_KEY_VALUE_RE.test(key)) {
362
- throw clientValidationError("idempotencyKey: must contain only printable ASCII characters and must not start or end with spaces");
69
+ throw (0, client_errors_js_1.clientValidationError)("idempotencyKey: must contain only printable ASCII characters and must not start or end with spaces");
363
70
  }
364
71
  }
365
72
  }
@@ -415,171 +122,6 @@ function fillRandomBytes(bytes) {
415
122
  }
416
123
  throw new errors_js_1.RuntimeError("globalThis.crypto.getRandomValues is required to generate Idempotency-Key");
417
124
  }
418
- /**
419
- * Construct a {@link ValidationError} for the case where the API returned
420
- * a response that parsed as JSON but didn't match the expected schema
421
- * shape (e.g. 400 with a body that isn't a {@link BatchCreateOutput}).
422
- *
423
- * Distinct from {@link clientValidationError} so callers can `.code`-branch
424
- * between "I passed bad input locally" (`"client_validation"`) and "the
425
- * server returned a body I can't interpret" (`"unexpected_response"`).
426
- * Uses `status: 0` by default because a logical shape mismatch is not tied to
427
- * the HTTP classification. Callers that enforce an observed HTTP contract
428
- * pass that status explicitly.
429
- *
430
- * Threads `request_id` through to {@link QURLError.requestId} when the
431
- * server-side correlation ID is available — operators debugging
432
- * "unexpected response" tickets need it on the *error* path too, not
433
- * just on success/passthrough returns.
434
- */
435
- function unexpectedResponseError(detail, options = {}) {
436
- // Every detail passed here is an SDK-authored contract message whose dynamic
437
- // inputs were validated by the caller-side shape guards. Do not pass raw
438
- // server strings to this helper. Server diagnostics use boundedErrorSnippet.
439
- const safeRequestId = boundedErrorIdentifier(options.requestId);
440
- return new errors_js_1.ValidationError({
441
- status: options.status ?? 0,
442
- code: errors_js_1.ERROR_CODE_UNEXPECTED_RESPONSE,
443
- title: "Unexpected Response",
444
- detail,
445
- request_id: safeRequestId,
446
- });
447
- }
448
- function withErrorCause(error, cause) {
449
- // Match the native Error `cause` descriptor. In particular, do not leak a
450
- // transport object through Object.keys(), object spread, or JSON logging.
451
- Object.defineProperty(error, "cause", {
452
- value: cause,
453
- writable: true,
454
- configurable: true,
455
- });
456
- return error;
457
- }
458
- function isIndependentAbort(error, requestSignal) {
459
- return (error instanceof Error &&
460
- (error.name === "AbortError" || error.name === "TimeoutError") &&
461
- !requestSignal.aborted);
462
- }
463
- function isSdkTimeout(error, requestSignal) {
464
- return (error instanceof Error &&
465
- requestSignal.aborted &&
466
- (error.name === "TimeoutError" || error.name === "AbortError"));
467
- }
468
- /**
469
- * Normalize a server-provided problem title/detail into a single-line,
470
- * UTF-8-safe snippet. Error messages must stay bounded even when a proxy or
471
- * compromised upstream returns very large structured problem fields.
472
- */
473
- function normalizedErrorSnippet(value) {
474
- if (typeof value !== "string")
475
- return undefined;
476
- // Bound source work before the Unicode regular expressions allocate
477
- // normalized copies. The response body cap is much larger than one retained
478
- // diagnostic, so processing the full server string would waste memory.
479
- const normalized = value
480
- .slice(0, MAX_ERROR_SNIPPET_SOURCE_CODE_UNITS)
481
- // Strip control characters plus bidi override/isolate controls that can
482
- // visually reorder diagnostics. Preserve other formatting characters
483
- // such as ZWJ/ZWNJ, which are required by legitimate scripts and emoji.
484
- .replace(/[\p{Cc}\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/gu, " ")
485
- .trim()
486
- .replace(/\s+/g, " ");
487
- return normalized === "" ? undefined : normalized;
488
- }
489
- function boundedErrorSnippet(value) {
490
- const sourceTruncated = typeof value === "string" && value.length > MAX_ERROR_SNIPPET_SOURCE_CODE_UNITS;
491
- const normalized = normalizedErrorSnippet(value);
492
- if (normalized === undefined)
493
- return undefined;
494
- const contentLimit = sourceTruncated
495
- ? MAX_ERROR_SNIPPET_BYTES - ERROR_SNIPPET_ELLIPSIS_BYTES
496
- : MAX_ERROR_SNIPPET_BYTES;
497
- // UTF-8 uses at most three bytes per UTF-16 code unit. Most diagnostics are
498
- // short ASCII, so avoid an allocation only to prove they fit.
499
- if (normalized.length * 3 <= contentLimit) {
500
- return sourceTruncated ? `${normalized}${ERROR_SNIPPET_ELLIPSIS}` : normalized;
501
- }
502
- const encoded = TEXT_ENCODER.encode(normalized);
503
- if (encoded.byteLength <= contentLimit) {
504
- return sourceTruncated ? `${normalized}${ERROR_SNIPPET_ELLIPSIS}` : normalized;
505
- }
506
- let end = MAX_ERROR_SNIPPET_BYTES - ERROR_SNIPPET_ELLIPSIS_BYTES;
507
- // Do not split a multi-byte UTF-8 sequence at the cap.
508
- while (end > 0 && (encoded[end] & 0xc0) === 0x80)
509
- end--;
510
- return `${TEXT_DECODER.decode(encoded.subarray(0, end))}${ERROR_SNIPPET_ELLIPSIS}`;
511
- }
512
- /** Keep server identifiers useful as exact machine-readable discriminants. */
513
- function boundedErrorIdentifier(value) {
514
- if (typeof value !== "string" || value.length > MAX_ERROR_SNIPPET_SOURCE_CODE_UNITS) {
515
- return undefined;
516
- }
517
- const normalized = normalizedErrorSnippet(value);
518
- if (normalized === undefined ||
519
- normalized !== value ||
520
- TEXT_ENCODER.encode(value).byteLength > MAX_ERROR_SNIPPET_BYTES) {
521
- return undefined;
522
- }
523
- return value;
524
- }
525
- /** Collect at most the first N enumerable own string keys without a full key-array copy. */
526
- function firstEnumerableOwnKeys(value, limit) {
527
- const keys = [];
528
- for (const key in value) {
529
- if (!Object.prototype.hasOwnProperty.call(value, key))
530
- continue;
531
- keys.push(key);
532
- if (keys.length === limit)
533
- break;
534
- }
535
- return keys;
536
- }
537
- /** Bound the structured per-field diagnostics exposed on ValidationError. */
538
- function boundedInvalidFields(value) {
539
- if (typeof value !== "object" || value === null || Array.isArray(value))
540
- return undefined;
541
- const entries = [];
542
- const retainedKeys = new Set();
543
- let retainedBytes = 0;
544
- // Bound inspected entries as well as retained entries. A hostile envelope
545
- // whose values all have the wrong type must not make diagnostics scan every
546
- // key in the response body.
547
- for (const rawKey of firstEnumerableOwnKeys(value, MAX_INVALID_FIELD_ENTRIES)) {
548
- const rawValue = value[rawKey];
549
- const key = boundedErrorSnippet(rawKey);
550
- const fieldDetail = boundedErrorSnippet(rawValue);
551
- if (key !== undefined && fieldDetail !== undefined && !retainedKeys.has(key)) {
552
- const entryBytes = TEXT_ENCODER.encode(key).byteLength + TEXT_ENCODER.encode(fieldDetail).byteLength;
553
- // A later, smaller entry can still fit even when this one cannot.
554
- if (retainedBytes + entryBytes > MAX_DIAGNOSTIC_COLLECTION_BYTES)
555
- continue;
556
- retainedKeys.add(key);
557
- entries.push([key, fieldDetail]);
558
- retainedBytes += entryBytes;
559
- }
560
- }
561
- return entries.length > 0 ? Object.fromEntries(entries) : undefined;
562
- }
563
- /** Bound server-controlled object keys before forwarding them to a debug sink. */
564
- function boundedObjectKeys(value) {
565
- // Stop before normalization so a hostile envelope cannot force work over
566
- // every enumerable own key. Some early keys may normalize away; bounded
567
- // diagnostics are more important here than filling every available slot.
568
- const retained = [];
569
- let retainedBytes = 0;
570
- for (const rawKey of firstEnumerableOwnKeys(value, MAX_DEBUG_BODY_KEYS)) {
571
- const key = boundedErrorSnippet(rawKey);
572
- if (key === undefined)
573
- continue;
574
- const keyBytes = TEXT_ENCODER.encode(key).byteLength;
575
- // A later, smaller key can still fit even when this one cannot.
576
- if (retainedBytes + keyBytes > MAX_DIAGNOSTIC_COLLECTION_BYTES)
577
- continue;
578
- retained.push(key);
579
- retainedBytes += keyBytes;
580
- }
581
- return retained;
582
- }
583
125
  async function cancelResponseBody(body) {
584
126
  if (!body)
585
127
  return;
@@ -590,24 +132,27 @@ async function cancelResponseBody(body) {
590
132
  // Best-effort resource cleanup must not replace the deterministic SDK error.
591
133
  }
592
134
  }
593
- function materializedResponseBodyExceedsLimit(value) {
594
- if (value.length > MAX_RESPONSE_BODY_BYTES)
135
+ function materializedResponseBodyExceedsLimit(value, limit) {
136
+ if (value.length > limit)
595
137
  return true;
596
138
  // Encoding creates another buffer. UTF-8 uses at most three bytes per
597
139
  // UTF-16 code unit, so shorter strings are provably safe without that copy.
598
- return (value.length * 3 > MAX_RESPONSE_BODY_BYTES &&
599
- TEXT_ENCODER.encode(value).byteLength > MAX_RESPONSE_BODY_BYTES);
140
+ return value.length * 3 > limit && client_errors_js_1.TEXT_ENCODER.encode(value).byteLength > limit;
600
141
  }
601
- function contentLengthExceedsLimit(response) {
142
+ function contentLengthExceedsLimit(response, limit) {
602
143
  // 204/205 never carry content, and a 304 Content-Length describes the
603
144
  // selected representation rather than a response body. Do not reject those
604
145
  // statuses for metadata about bytes that fetch will not deliver.
605
146
  if (response.status === 204 || response.status === 205 || response.status === 304)
606
147
  return false;
148
+ // Encoded Content-Length describes wire bytes; fetch exposes decoded bytes.
149
+ const encoding = response.headers.get("content-encoding");
150
+ if (encoding && encoding.trim().toLowerCase() !== "identity")
151
+ return false;
607
152
  const value = response.headers.get("content-length");
608
153
  if (value === null || !/^\d+$/.test(value.trim()))
609
154
  return false;
610
- return Number(value) > MAX_RESPONSE_BODY_BYTES;
155
+ return Number(value) > limit;
611
156
  }
612
157
  /** Detect a followed redirect from fetch implementations that expose only the final URL. */
613
158
  function responseUrlState(responseUrl, requestUrl) {
@@ -635,16 +180,16 @@ function responseUrlState(responseUrl, requestUrl) {
635
180
  * optimization only; streaming byte accounting remains authoritative because
636
181
  * that header may be absent or false.
637
182
  */
638
- async function readBoundedResponseBody(response) {
183
+ async function readBoundedResponseBody(response, limit) {
639
184
  // `headers.get` is part of the documented injected-fetch contract. Detect a
640
185
  // malformed local shim before it can be mistaken for a retryable body-stream
641
186
  // failure and replay an otherwise deterministic GET programming error.
642
187
  if (typeof response.headers?.get !== "function") {
643
188
  throw new ResponseBodyMaterializationError("Response-like fetch must provide headers.get(name)");
644
189
  }
645
- if (contentLengthExceedsLimit(response)) {
190
+ if (contentLengthExceedsLimit(response, limit)) {
646
191
  await cancelResponseBody(response.body);
647
- throw new ResponseBodyTooLargeError();
192
+ throw new ResponseBodyTooLargeError(limit);
648
193
  }
649
194
  const body = response.body;
650
195
  if (body === null)
@@ -666,8 +211,8 @@ async function readBoundedResponseBody(response) {
666
211
  }
667
212
  }
668
213
  const text = typeof materializedText === "string" ? materializedText : "";
669
- if (materializedResponseBodyExceedsLimit(text)) {
670
- throw new ResponseBodyTooLargeError();
214
+ if (materializedResponseBodyExceedsLimit(text, limit)) {
215
+ throw new ResponseBodyTooLargeError(limit);
671
216
  }
672
217
  if (text !== "" ||
673
218
  response.status === 204 ||
@@ -695,8 +240,8 @@ async function readBoundedResponseBody(response) {
695
240
  }
696
241
  if (serialized === undefined)
697
242
  return "";
698
- if (materializedResponseBodyExceedsLimit(serialized)) {
699
- throw new ResponseBodyTooLargeError();
243
+ if (materializedResponseBodyExceedsLimit(serialized, limit)) {
244
+ throw new ResponseBodyTooLargeError(limit);
700
245
  }
701
246
  return serialized;
702
247
  }
@@ -712,14 +257,14 @@ async function readBoundedResponseBody(response) {
712
257
  throw new ResponseBodyMaterializationError("Response-like fetch body stream must yield Uint8Array chunks");
713
258
  }
714
259
  const nextTotal = total + value.byteLength;
715
- if (nextTotal > MAX_RESPONSE_BODY_BYTES) {
260
+ if (nextTotal > limit) {
716
261
  try {
717
262
  await reader.cancel();
718
263
  }
719
264
  catch {
720
265
  // Preserve the body-limit failure even if stream cancellation fails.
721
266
  }
722
- throw new ResponseBodyTooLargeError();
267
+ throw new ResponseBodyTooLargeError(limit);
723
268
  }
724
269
  chunks.push(value);
725
270
  total = nextTotal;
@@ -741,139 +286,12 @@ async function readBoundedResponseBody(response) {
741
286
  }
742
287
  }
743
288
  try {
744
- return TEXT_DECODER.decode(bytes);
289
+ return client_errors_js_1.TEXT_DECODER.decode(bytes);
745
290
  }
746
291
  catch {
747
292
  throw new InvalidResponseEncodingError();
748
293
  }
749
294
  }
750
- // ---- Spec-derived validation helpers ------------------------------------
751
- // These mirror constraints documented on each request schema in
752
- // `openapi.yaml` so obvious mistakes fail fast instead of round-tripping
753
- // to the API and coming back as a generic 400.
754
- const MAX_TARGET_URL = 2048;
755
- const MAX_LABEL = 500;
756
- const MAX_BATCH_ITEMS = 100;
757
- const MAX_DESCRIPTION = 500;
758
- const MAX_CUSTOM_DOMAIN = 253;
759
- const MAX_MAX_SESSIONS = 1000;
760
- const MAX_API_KEY_NAME = 100;
761
- const API_KEY_REQUEST_SCOPE_VALUES = [
762
- "qurl:read",
763
- "qurl:write",
764
- "qurl:resolve",
765
- "qurl:agent",
766
- ];
767
- assertExhaustive(true);
768
- const API_KEY_REQUEST_SCOPES = new Set(API_KEY_REQUEST_SCOPE_VALUES);
769
- /** Narrow a response scope before reusing it in a create/update request. */
770
- function isApiKeyRequestScope(scope) {
771
- return API_KEY_REQUEST_SCOPES.has(scope);
772
- }
773
- const CREDENTIAL_TARGET_VALUES = [
774
- "agent",
775
- "connector",
776
- ];
777
- assertExhaustive(true);
778
- const CREDENTIAL_TARGETS = new Set(CREDENTIAL_TARGET_VALUES);
779
- const CREDENTIAL_CLAIM_TYPE_VALUES = [
780
- "connector",
781
- ];
782
- assertExhaustive(true);
783
- const CREDENTIAL_CLAIM_TYPES = new Set(CREDENTIAL_CLAIM_TYPE_VALUES);
784
- // Mirrors CreateApiKeyRequest.expires_in in the service OpenAPI. Composite
785
- // durations are deliberately invalid on this endpoint. The 24h cap permits
786
- // 1d, but no positive whole-week duration.
787
- const CREDENTIAL_DURATION_PATTERN = /^([0-9]+)([smhdw])$/;
788
- const CREDENTIAL_DURATION_MULTIPLIERS = {
789
- s: 1n,
790
- m: 60n,
791
- h: 3600n,
792
- d: 86400n,
793
- w: 604800n,
794
- };
795
- const MAX_ENROLLMENT_TOKEN_TTL_SECONDS = 86400n;
796
- const MAX_ALIAS = 64;
797
- const MAX_SLUG = 64;
798
- const MAX_TARGET_PATH = 2048;
799
- const MAX_TAGS = 10;
800
- const MAX_TAG_LENGTH = 50;
801
- const MAX_AUTO_PAGINATION_PAGES = 10_000;
802
- const MIN_DELEGATED_BATCH_IDEMPOTENCY_KEY = 32;
803
- const MAX_DELEGATED_MINT_CAPABILITY = 8192;
804
- const MAX_DELEGATED_ETAG = 96;
805
- const MAX_DELEGATED_BATCH_ITEMS = 100;
806
- const DELEGATED_BATCH_ID_PATTERN = /^dqb_[A-Za-z0-9_-]{22}$/;
807
- const DELEGATED_QURL_ID_PATTERN = /^q_[0-9a-f]{11}$/;
808
- const STRONG_ETAG_PATTERN = /^"[\x21\x23-\x7e]+"$/;
809
- const UTC_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
810
- // Keep these identity contracts aligned with qurl-go and the public API.
811
- const CONNECTOR_SLUG_PATTERN = /^[a-z][a-z0-9-]{1,62}[a-z0-9]$/;
812
- // Current service schemas intentionally share this grammar, but keep the
813
- // mutable alias contract separate from the immutable lookup identity.
814
- const CONNECTOR_ALIAS_PATTERN = /^[a-z][a-z0-9-]{1,62}[a-z0-9]$/;
815
- const CONNECTOR_RESOURCE_ID_PATTERN = /^[A-Za-z0-9_-]{122}$/;
816
- const CONNECTOR_RESOURCE_ID_DER_PREFIX = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE";
817
- const CONNECTOR_ROUTING_ID_PATTERN = /^c-[a-z2-7]{51}[aq]$/;
818
- // CreateQurlRequest.target_url pattern is loose (just a URI) but
819
- // UpdateQurlRequest.tags pattern is specific — enforce it here.
820
- const TAG_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9 _-]*$/;
821
- function requireMaxLength(value, field, max) {
822
- if (value === undefined)
823
- return;
824
- // Untyped-JS safety: a non-string `value` would silently pass since
825
- // `(42).length === undefined > max` is `false` and the bad value would
826
- // sail through to the wire. Match the typeof guard pattern used by
827
- // requireValidTags / requireValidTargetUrl.
828
- if (typeof value !== "string") {
829
- throw clientValidationError(`${field}: must be a string (got ${value === null ? "null" : typeof value})`);
830
- }
831
- if (value.length > max) {
832
- throw clientValidationError(`${field}: must be ${max} characters or fewer (got ${value.length})`);
833
- }
834
- }
835
- function requireMaxUtf8Bytes(value, field, max) {
836
- if (value === undefined)
837
- return;
838
- if (typeof value !== "string") {
839
- throw clientValidationError(`${field}: must be a string (got ${value === null ? "null" : typeof value})`);
840
- }
841
- const bytes = TEXT_ENCODER.encode(value).byteLength;
842
- if (bytes > max) {
843
- throw clientValidationError(`${field}: must be ${max} UTF-8 bytes or fewer (got ${bytes})`);
844
- }
845
- }
846
- function requireNonEmptyIfPresent(value, field) {
847
- // Reject `""` so uninitialized form state doesn't reach the wire.
848
- if (value === "") {
849
- throw clientValidationError(`${field}: must not be an empty string`);
850
- }
851
- }
852
- function requireMaxSessionsInRange(value, field = "max_sessions") {
853
- if (value === undefined)
854
- return;
855
- if (!Number.isInteger(value) || value < 0 || value > MAX_MAX_SESSIONS) {
856
- throw clientValidationError(`${field}: must be an integer between 0 and ${MAX_MAX_SESSIONS} (got ${value})`);
857
- }
858
- }
859
- function requireBooleanIfPresent(value, field) {
860
- if (value === undefined)
861
- return;
862
- if (typeof value !== "boolean") {
863
- throw clientValidationError(`${field}: must be a boolean (got ${describeShape(value)})`);
864
- }
865
- }
866
- function requireListLimitInRange(value, methodName) {
867
- if (value === undefined || value === null)
868
- return;
869
- // All current paginated OpenAPI query params share 1..100. If a future
870
- // endpoint diverges, add a per-endpoint bound rather than changing this
871
- // shared guard silently.
872
- if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 100) {
873
- const rendered = typeof value === "string" ? JSON.stringify(value) : String(value);
874
- throw clientValidationError(`${methodName}: limit: must be an integer between 1 and 100 (got ${rendered})`);
875
- }
876
- }
877
295
  function appendQuery(path, input, keys, methodName) {
878
296
  validateQueryInput(input, keys, methodName);
879
297
  const params = new URLSearchParams();
@@ -887,9 +305,9 @@ function appendQuery(path, input, keys, methodName) {
887
305
  return query ? `${path}?${query}` : path;
888
306
  }
889
307
  function validateQueryInput(input, keys, methodName) {
890
- requireNoUnknownFields(input, keys, methodName);
308
+ (0, client_validation_js_1.requireNoUnknownFields)(input, keys, methodName);
891
309
  if (keys.includes("limit")) {
892
- requireListLimitInRange(input.limit, methodName);
310
+ (0, client_validation_js_1.requireListLimitInRange)(input.limit, methodName);
893
311
  }
894
312
  for (const key of keys) {
895
313
  const value = input[key];
@@ -898,7 +316,7 @@ function validateQueryInput(input, keys, methodName) {
898
316
  if (key === "limit")
899
317
  continue;
900
318
  if (typeof value !== "string") {
901
- throw clientValidationError(`${methodName}: ${key}: must be a string (got ${describeShape(value)})`);
319
+ throw (0, client_errors_js_1.clientValidationError)(`${methodName}: ${key}: must be a string (got ${(0, client_validation_js_1.describeShape)(value)})`);
902
320
  }
903
321
  }
904
322
  }
@@ -909,1330 +327,17 @@ function validateListAllInput(input, keys, methodName) {
909
327
  // each request revalidates the threaded cursor/filter set before dispatch.
910
328
  validateQueryInput(input, keys, methodName);
911
329
  }
330
+ function withEnvelopeMetadata(value, meta) {
331
+ return (0, response_metadata_js_1.withResponseMetadata)(value, (0, client_errors_js_1.boundedErrorIdentifier)(meta?.request_id));
332
+ }
912
333
  function pageFromMeta(out, meta) {
913
- return {
334
+ return withEnvelopeMetadata({
914
335
  ...out,
915
336
  next_cursor: meta?.next_cursor,
916
337
  has_more: meta?.has_more ?? Boolean(meta?.next_cursor),
917
338
  request_id: meta?.request_id,
918
339
  page_size: meta?.page_size,
919
- };
920
- }
921
- // Best-effort defence-in-depth against today's access-token grammar. The
922
- // service remains authoritative because resource IDs and credentials are both
923
- // opaque and future credential formats may not retain the `at_` prefix. The
924
- // delimiter requirement cannot collide with current base64url public keys,
925
- // standard-base64 keys, or base32 CRIDs.
926
- const PATH_EMBEDDED_ACCESS_TOKEN_RE = /[?&#=:\s]at_/i;
927
- const PATH_PREFIXED_ACCESS_TOKEN_RE = /\/at_/i;
928
- const QURL_LINK_URL_RE = /^(?:https?:\/\/)?(?:[a-z0-9-]+\.)*qurl\.link(?::\d{1,5})?(?:\/|[?#])/i;
929
- // ponytail: today's 22-character token suffix only improves URL-paste diagnostics;
930
- // other lengths are still rejected as URLs without echoing the input.
931
- const URL_EMBEDDED_ACCESS_TOKEN_RE = /[?&#=:/]at_[a-z0-9_-]{22}(?:$|[?&#:/])/i;
932
- // A host without a slash can be a legitimate domain identifier. Require the
933
- // path boundary so shared validation does not break the domain namespace.
934
- const PATH_SCHEMELESS_URL_RE = /^[a-z0-9-]+(?:\.[a-z0-9-]+)+(?::\d{1,5})?\//i;
935
- const QURL_DISPLAY_ID_PREFIX_RE = /^q_/i;
936
- const MAX_PATH_ID_DECODE_PASSES = 3;
937
- const MAX_PATH_ID_LENGTH = 4096;
938
- function isUrlLikePathId(value) {
939
- return /^https?:\/\//i.test(value) || PATH_SCHEMELESS_URL_RE.test(value);
940
- }
941
- const REVOKE_INDIVIDUAL_QURL_RECOVERY = "revoke individual tokens with revokeResourceQurl(crid, qurlId)";
942
- const DELETE_RESOURCE_RECOVERY = "pass a CRID returned by the API; to revoke one qURL, call revokeResourceQurl(crid, qurlId)";
943
- const RESOURCE_OR_QURL_ID_PATH_OPTIONS = {
944
- requireCridUnlessQurlDisplayId: true,
945
- };
946
- const QURL_ID_PATH_OPTIONS = {
947
- rejectBareAccessToken: true,
948
- accessTokenRecovery: "pass the qURL display ID, not its access token",
949
- };
950
- /**
951
- * Validates that a path-parameter argument is a non-empty string. Some callers
952
- * pass resource/qURL display IDs, while newer endpoints also pass domains,
953
- * session IDs, webhook IDs, and API-key IDs, so this intentionally enforces
954
- * only basic shape plus transport-safety rules and leaves endpoint-specific
955
- * identifier grammar to the service.
956
- */
957
- function validatePathId(id, method, field = "id", options = {}) {
958
- if (options.requireCridUnlessQurlDisplayId && typeof id === "string" && !id.startsWith("q_")) {
959
- requireResourceCrid(id, method, field);
960
- return;
961
- }
962
- // `.trim()` catches whitespace-only and padded IDs before they round-trip as
963
- // `%20...%20` paths that the server can only reject with a less useful 404.
964
- // The pre-flight error is more actionable than the 404.
965
- if (typeof id !== "string") {
966
- const observedType = id === null ? "null" : typeof id;
967
- throw clientValidationError(`${method}: ${field} is required (got ${observedType})`);
968
- }
969
- if (id.trim() === "") {
970
- throw clientValidationError(`${method}: ${field} is required`);
971
- }
972
- if (id.trim() !== id) {
973
- throw clientValidationError(`${method}: ${field} must not include leading or trailing whitespace`);
974
- }
975
- if (id.length > MAX_PATH_ID_LENGTH) {
976
- throw clientValidationError(`${method}: ${field} must be ${MAX_PATH_ID_LENGTH} characters or fewer`);
977
- }
978
- const decodedIds = decodedPathIdForms(id);
979
- // URL resolvers and intermediaries can normalize raw or pre-encoded dot
980
- // segments before routing. Reject every decoded form we inspected so an
981
- // item operation cannot be retargeted to a collection endpoint. Embedded
982
- // `a/../b` remains an opaque single segment because the later URL builder
983
- // percent-encodes its slash.
984
- if (decodedIds.some((value) => value === "." || value === "..")) {
985
- throw clientValidationError(`${method}: ${field} is an invalid URL path segment`);
986
- }
987
- // `/` is deliberately outside the general delimiter class so a URL path
988
- // such as `/at_a_glance` is not misread as a credential. For non-URL input,
989
- // `/at_` remains a strong pasted-path signal. Fragment, query, header, and
990
- // pasted `Bearer at_...` / `token:at_...` forms are also strong signals.
991
- // Bare `at_...` values are rejected only for resource/qURL identifiers:
992
- // those current formats have fixed non-colliding prefixes, while domain,
993
- // webhook, session, and API-key IDs are deliberately opaque/open contracts.
994
- // Full URLs and delimiter/path-prefixed credentials remain rejected in
995
- // every namespace.
996
- const containsUrl = decodedIds.some(isUrlLikePathId);
997
- const containsAccessToken = decodedIds.some((value) => {
998
- const valueIsUrl = isUrlLikePathId(value);
999
- return ((QURL_LINK_URL_RE.test(value) &&
1000
- (PATH_EMBEDDED_ACCESS_TOKEN_RE.test(value) || PATH_PREFIXED_ACCESS_TOKEN_RE.test(value))) ||
1001
- (valueIsUrl
1002
- ? URL_EMBEDDED_ACCESS_TOKEN_RE.test(value)
1003
- : PATH_EMBEDDED_ACCESS_TOKEN_RE.test(value) || PATH_PREFIXED_ACCESS_TOKEN_RE.test(value)) ||
1004
- (options.rejectBareAccessToken === true && value.slice(0, 3).toLowerCase() === "at_"));
1005
- });
1006
- if (containsAccessToken) {
1007
- const recovery = options.accessTokenRecovery ?? "pass the identifier returned by the API";
1008
- throw clientValidationError(`${method}: ${field} must not contain an access token; ${recovery}`);
1009
- }
1010
- // A full target/qURL is a common argument mix-up, but it is distinct from
1011
- // passing an access-token credential. Diagnose it accurately without
1012
- // echoing the URL, which may itself contain sensitive query parameters.
1013
- if (containsUrl) {
1014
- throw clientValidationError(`${method}: ${field} must be an identifier, not a URL`);
1015
- }
1016
- if (options.rejectQurlDisplayId === true &&
1017
- decodedIds.some((value) => QURL_DISPLAY_ID_PREFIX_RE.test(value))) {
1018
- throw clientValidationError(`${method}: ${field} must not be a qURL display ID; ${REVOKE_INDIVIDUAL_QURL_RECOVERY}`);
1019
- }
1020
- }
1021
- function decodedPathIdForms(id) {
1022
- // Probe repeatedly encoded input too. Callers sometimes pre-encode a copied
1023
- // link before passing it through a URL builder that encodes it again. Three
1024
- // passes cover the common accidental cases without allowing hostile input
1025
- // to create unbounded work. More deeply encoded or malformed-percent input
1026
- // deliberately fails open to the authoritative service after safe segment
1027
- // encoding; this heuristic is not an identifier parser.
1028
- const decodedIds = [id];
1029
- let decodedId = id;
1030
- for (let pass = 0; pass < MAX_PATH_ID_DECODE_PASSES; pass += 1) {
1031
- try {
1032
- const next = decodeURIComponent(decodedId);
1033
- if (next === decodedId)
1034
- break;
1035
- decodedIds.push(next);
1036
- decodedId = next;
1037
- }
1038
- catch {
1039
- // Malformed percent escapes remain opaque and are left to the service.
1040
- break;
1041
- }
1042
- }
1043
- return decodedIds;
1044
- }
1045
- function requireConnectorSlug(slug, method) {
1046
- if (typeof slug !== "string" || !CONNECTOR_SLUG_PATTERN.test(slug)) {
1047
- throw clientValidationError(`${method}: slug must be 3-64 lowercase alphanumeric or hyphen characters, start with a letter, and end alphanumeric`);
1048
- }
1049
- }
1050
- function classifyConnectorMutationFailure(operation, error) {
1051
- if (!(error instanceof errors_js_1.QURLError)) {
1052
- throw new errors_js_1.ConnectorResourceOutcomeUnknownError(new errors_js_1.RuntimeError("Unexpected failure after Connector mutation dispatch", { cause: error }));
1053
- }
1054
- // Most authoritative 4xx responses prove that the mutation was rejected.
1055
- // bootstrap_key_consumed is the exception: the service returns it from the
1056
- // consume phase after the resource may have been bound or found.
1057
- // Callers perform argument/runtime preflight outside their mutation try
1058
- // blocks; every other surfaced failure therefore follows dispatch or a
1059
- // nominal success whose resource contract could not be consumed, so callers
1060
- // must reconcile before retrying. Ensure's exact 201 + valid resource but
1061
- // missing found_existing is handled after this classifier: the row proves
1062
- // the selected resource, while only its required metadata is unavailable.
1063
- if (operation === "ensure" && error.status === 409 && error.code === "bootstrap_key_consumed") {
1064
- throw new errors_js_1.ConnectorResourceOutcomeUnknownError(error);
1065
- }
1066
- if (error.status >= 400 && error.status < 500) {
1067
- throw error;
1068
- }
1069
- throw new errors_js_1.ConnectorResourceOutcomeUnknownError(error);
1070
- }
1071
- function decodeCanonicalBase64Url(value) {
1072
- if (!CONNECTOR_RESOURCE_ID_PATTERN.test(value) ||
1073
- !value.startsWith(CONNECTOR_RESOURCE_ID_DER_PREFIX)) {
1074
- return undefined;
1075
- }
1076
- try {
1077
- const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "==";
1078
- const binary = atob(padded);
1079
- const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
1080
- const canonical = btoa(String.fromCharCode(...bytes))
1081
- .replace(/\+/g, "-")
1082
- .replace(/\//g, "_")
1083
- .replace(/=+$/, "");
1084
- return canonical === value ? bytes : undefined;
1085
- }
1086
- catch {
1087
- return undefined;
1088
- }
1089
- }
1090
- function requireConnectorSubtleCrypto(method) {
1091
- if (!globalThis.crypto?.subtle) {
1092
- throw new errors_js_1.RuntimeError(`${method}: requires the Web Crypto SubtleCrypto API`);
1093
- }
1094
- }
1095
- function requireResourceCrid(crid, method, field = "id") {
1096
- if ((0, crid_js_1.parseCrid)(crid))
1097
- return;
1098
- // Keep actionable paste errors without inspecting valid CRIDs twice.
1099
- validatePathId(crid, method, field, {
1100
- rejectBareAccessToken: true,
1101
- rejectQurlDisplayId: true,
1102
- accessTokenRecovery: method === "delete" || method === "deleteResource"
1103
- ? DELETE_RESOURCE_RECOVERY
1104
- : "pass a CRID returned by the API",
1105
- });
1106
- throw clientValidationError(`${method}: requires a valid CRID`);
1107
- }
1108
- async function importsAsConnectorPublicKey(der) {
1109
- try {
1110
- const keyData = new ArrayBuffer(der.byteLength);
1111
- new Uint8Array(keyData).set(der);
1112
- await globalThis.crypto.subtle.importKey("spki", keyData, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
1113
- return true;
1114
- }
1115
- catch {
1116
- return false;
1117
- }
1118
- }
1119
- function containsControlCharacter(value) {
1120
- return Array.from(value).some((character) => {
1121
- const codePoint = character.codePointAt(0);
1122
- return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);
1123
- });
1124
- }
1125
- async function verifyResourceIdentity(resource, method, expectedCrid) {
1126
- if (!resource || typeof resource.resource_id !== "string") {
1127
- throw unexpectedResponseError(`${method}: response has missing or invalid resource_id`);
1128
- }
1129
- const resourceKey = decodeCanonicalBase64Url(resource.resource_id);
1130
- requireConnectorSubtleCrypto(method);
1131
- if (!resourceKey || !(await importsAsConnectorPublicKey(resourceKey))) {
1132
- throw unexpectedResponseError(`${method}: response has missing or invalid resource_id`);
1133
- }
1134
- if (expectedCrid !== undefined && resource.crid !== expectedCrid) {
1135
- throw unexpectedResponseError(`${method}: response crid does not match the request`);
1136
- }
1137
- if (!(await (0, crid_js_1.cridKeyMatches)(resource.crid, resourceKey))) {
1138
- throw unexpectedResponseError(`${method}: response has missing, invalid, or public-key-mismatched crid`);
1139
- }
1140
- }
1141
- async function parseConnectorResource(client, value, method, expectation = {}) {
1142
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
1143
- throw unexpectedResponseError(`${method}: response is missing connector resource data`);
1144
- }
1145
- const resource = value;
1146
- await verifyResourceIdentity(resource, method, expectation.crid);
1147
- if (typeof resource.connector_routing_id !== "string" ||
1148
- !CONNECTOR_ROUTING_ID_PATTERN.test(resource.connector_routing_id)) {
1149
- throw unexpectedResponseError(`${method}: response has missing or invalid connector_routing_id`);
1150
- }
1151
- if (typeof resource.knock_resource_id !== "string" ||
1152
- resource.knock_resource_id.trim() === "" ||
1153
- resource.knock_resource_id.trim() !== resource.knock_resource_id ||
1154
- containsControlCharacter(resource.knock_resource_id)) {
1155
- throw unexpectedResponseError(`${method}: response has missing or invalid knock_resource_id`);
1156
- }
1157
- if (resource.resource_id === resource.knock_resource_id ||
1158
- resource.connector_routing_id === resource.knock_resource_id) {
1159
- throw unexpectedResponseError(`${method}: response cross-wires resource identity, routing, and admission values`);
1160
- }
1161
- if (resource.type !== "tunnel") {
1162
- throw unexpectedResponseError(`${method}: response resource type is not tunnel`);
1163
- }
1164
- if (typeof resource.slug !== "string" || !CONNECTOR_SLUG_PATTERN.test(resource.slug)) {
1165
- throw unexpectedResponseError(`${method}: response has missing or invalid slug`);
1166
- }
1167
- if (expectation.slug !== undefined && resource.slug !== expectation.slug) {
1168
- throw unexpectedResponseError(`${method}: response slug does not match the request`);
1169
- }
1170
- if (resource.alias !== undefined &&
1171
- resource.alias !== null &&
1172
- (typeof resource.alias !== "string" || !CONNECTOR_ALIAS_PATTERN.test(resource.alias))) {
1173
- // The public API intentionally gives mutable aliases and immutable slugs the
1174
- // same canonical wire grammar (domain aliasPattern/slugPattern). Keeping
1175
- // response validation aligned avoids accepting a row the service itself
1176
- // could not create or update.
1177
- throw unexpectedResponseError(`${method}: response has invalid alias`);
1178
- }
1179
- if (resource.status === "revoked") {
1180
- if (expectation.revokedIsLifecycleError !== true) {
1181
- throw unexpectedResponseError(`${method}: active-only operation returned a revoked connector resource`);
1182
- }
1183
- throw new errors_js_1.QURLError({
1184
- status: 0,
1185
- code: errors_js_1.ERROR_CODE_CONNECTOR_RESOURCE_REVOKED,
1186
- title: "Connector Resource Revoked",
1187
- detail: `${method}: qURL Connector resource is revoked`,
1188
- });
1189
- }
1190
- if (resource.status !== "active") {
1191
- throw unexpectedResponseError(`${method}: response has invalid resource status`);
1192
- }
1193
- return new ConnectorResource(client, resource, CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN);
1194
- }
1195
- function requireValidTags(tags) {
1196
- // null tolerance for untyped-JS callers — treated as "don't touch."
1197
- // Duplicates pass through to the server (authoritative on dedupe policy).
1198
- if (tags === undefined || tags === null)
1199
- return;
1200
- if (!Array.isArray(tags)) {
1201
- throw clientValidationError(`tags: must be an array of strings (got ${typeof tags})`);
1202
- }
1203
- if (tags.length > MAX_TAGS) {
1204
- throw clientValidationError(`tags: max ${MAX_TAGS} items allowed (got ${tags.length})`);
1205
- }
1206
- const errors = [];
1207
- for (let i = 0; i < tags.length; i++) {
1208
- const tag = tags[i];
1209
- // Per-element type guard for untyped-JS callers. Without this, a
1210
- // non-string element would silently pass (`.length` on a number is
1211
- // `undefined`; `TAG_PATTERN.test(42)` coerces to `"42"`) or TypeError
1212
- // (`null.length`).
1213
- if (typeof tag !== "string") {
1214
- errors.push(`tags[${i}]: must be a string (got ${tag === null ? "null" : typeof tag})`);
1215
- continue;
1216
- }
1217
- if (tag.length < 1 || tag.length > MAX_TAG_LENGTH) {
1218
- errors.push(`tags[${i}]: must be 1-${MAX_TAG_LENGTH} characters (got ${tag.length})`);
1219
- continue;
1220
- }
1221
- if (!TAG_PATTERN.test(tag)) {
1222
- errors.push(`tags[${i}]: must start with an alphanumeric and contain only letters, numbers, spaces, underscores, or hyphens`);
1223
- }
1224
- }
1225
- if (errors.length > 0) {
1226
- throw clientValidationError(errors.join("; "));
1227
- }
1228
- }
1229
- function describeShape(value) {
1230
- if (value === null)
1231
- return "null";
1232
- if (Array.isArray(value))
1233
- return "array";
1234
- return typeof value;
1235
- }
1236
- const ACCESS_POLICY_LIST_FIELDS = [
1237
- "ip_allowlist",
1238
- "ip_denylist",
1239
- "geo_allowlist",
1240
- "geo_denylist",
1241
- ];
1242
- const ACCESS_POLICY_STRING_FIELDS = [
1243
- "user_agent_allow_regex",
1244
- "user_agent_deny_regex",
1245
- ];
1246
- const AI_AGENT_POLICY_LIST_FIELDS = [
1247
- "deny_categories",
1248
- "allow_categories",
1249
- ];
1250
- function requireValidAccessPolicy(policy) {
1251
- // null/undefined → "don't touch". Server is authoritative on CIDR /
1252
- // ISO-3166 / regex grammar; this guard catches only the shape errors
1253
- // an untyped-JS caller is likely to make (e.g. `geo_allowlist: "US"`
1254
- // instead of `["US"]`) so they get a structured ValidationError
1255
- // instead of a server 400 they have to debug.
1256
- if (policy === undefined || policy === null)
1257
- return;
1258
- if (typeof policy !== "object" || Array.isArray(policy)) {
1259
- throw clientValidationError(`access_policy: must be an object (got ${describeShape(policy)})`);
1260
- }
1261
- for (const field of ACCESS_POLICY_LIST_FIELDS) {
1262
- const value = policy[field];
1263
- if (value === undefined)
1264
- continue;
1265
- if (!Array.isArray(value)) {
1266
- throw clientValidationError(`access_policy.${field}: must be an array (got ${describeShape(value)})`);
1267
- }
1268
- }
1269
- for (const field of ACCESS_POLICY_STRING_FIELDS) {
1270
- const value = policy[field];
1271
- if (value === undefined)
1272
- continue;
1273
- if (typeof value !== "string") {
1274
- throw clientValidationError(`access_policy.${field}: must be a string (got ${describeShape(value)})`);
1275
- }
1276
- }
1277
- const aip = policy.ai_agent_policy;
1278
- if (aip !== undefined) {
1279
- if (aip === null || typeof aip !== "object" || Array.isArray(aip)) {
1280
- throw clientValidationError(`access_policy.ai_agent_policy: must be an object (got ${describeShape(aip)})`);
1281
- }
1282
- if (aip.block_all !== undefined && typeof aip.block_all !== "boolean") {
1283
- throw clientValidationError(`access_policy.ai_agent_policy.block_all: must be a boolean (got ${describeShape(aip.block_all)})`);
1284
- }
1285
- for (const field of AI_AGENT_POLICY_LIST_FIELDS) {
1286
- const value = aip[field];
1287
- if (value === undefined)
1288
- continue;
1289
- if (!Array.isArray(value)) {
1290
- throw clientValidationError(`access_policy.ai_agent_policy.${field}: must be an array (got ${describeShape(value)})`);
1291
- }
1292
- }
1293
- }
1294
- }
1295
- function validateCreateDelegatedQurlBatchInput(input) {
1296
- if (typeof input !== "object" || input === null || Array.isArray(input)) {
1297
- throw clientValidationError(`createDelegatedQurlBatch: input must be an object (got ${describeShape(input)})`);
1298
- }
1299
- const typed = input;
1300
- requireNoUnknownFields(typed, DELEGATED_BATCH_INPUT_KEYS, "createDelegatedQurlBatch");
1301
- if (typeof typed.mint_capability !== "string" ||
1302
- typed.mint_capability.length < 1 ||
1303
- typed.mint_capability.length > MAX_DELEGATED_MINT_CAPABILITY) {
1304
- throw clientValidationError(`createDelegatedQurlBatch: mint_capability must be a string of 1-${MAX_DELEGATED_MINT_CAPABILITY} characters`);
1305
- }
1306
- if (!Array.isArray(typed.grants) ||
1307
- typed.grants.length < 1 ||
1308
- typed.grants.length > MAX_DELEGATED_BATCH_ITEMS) {
1309
- throw clientValidationError(`createDelegatedQurlBatch: grants must contain 1-${MAX_DELEGATED_BATCH_ITEMS} items`);
1310
- }
1311
- for (const [index, grant] of typed.grants.entries()) {
1312
- const field = `createDelegatedQurlBatch: grants[${index}]`;
1313
- if (typeof grant !== "object" || grant === null || Array.isArray(grant)) {
1314
- throw clientValidationError(`${field}: must be an object (got ${describeShape(grant)})`);
1315
- }
1316
- requireNoUnknownFields(grant, DELEGATED_BATCH_GRANT_KEYS, field);
1317
- requireMaxUtf8Bytes(grant.label, `${field}.label`, MAX_LABEL);
1318
- requireBooleanIfPresent(grant.one_time_use, `${field}.one_time_use`);
1319
- requireMaxSessionsInRange(grant.max_sessions, `${field}.max_sessions`);
1320
- for (const durationField of ["expires_in", "session_duration"]) {
1321
- const value = grant[durationField];
1322
- if (value !== undefined && (typeof value !== "string" || value.length === 0)) {
1323
- throw clientValidationError(`${field}.${durationField}: must be a non-empty string`);
1324
- }
1325
- }
1326
- if (grant.access_policy !== undefined) {
1327
- requireValidAccessPolicy(grant.access_policy);
1328
- }
1329
- }
1330
- }
1331
- function validateDelegatedBatchCreateOptions(options) {
1332
- if (typeof options !== "object" || options === null || Array.isArray(options)) {
1333
- throw clientValidationError("createDelegatedQurlBatch: options are required");
1334
- }
1335
- requireNoUnknownFields(options, DELEGATED_BATCH_CREATE_OPTION_KEYS, "createDelegatedQurlBatch options");
1336
- const key = options.idempotencyKey;
1337
- if (typeof key !== "string" ||
1338
- key.length < MIN_DELEGATED_BATCH_IDEMPOTENCY_KEY ||
1339
- key.length > MAX_IDEMPOTENCY_KEY ||
1340
- !/^[!-~]+$/.test(key)) {
1341
- throw clientValidationError(`createDelegatedQurlBatch: idempotencyKey must contain ${MIN_DELEGATED_BATCH_IDEMPOTENCY_KEY}-${MAX_IDEMPOTENCY_KEY} visible ASCII characters`);
1342
- }
1343
- }
1344
- function validateGetDelegatedQurlBatchOptions(options) {
1345
- if (options === undefined)
1346
- return;
1347
- if (typeof options !== "object" || options === null || Array.isArray(options)) {
1348
- throw clientValidationError(`getDelegatedQurlBatch: options must be an object (got ${describeShape(options)})`);
1349
- }
1350
- requireNoUnknownFields(options, DELEGATED_BATCH_GET_OPTION_KEYS, "getDelegatedQurlBatch options");
1351
- const etag = options.etag;
1352
- if (etag !== undefined &&
1353
- (typeof etag !== "string" ||
1354
- etag.length > MAX_DELEGATED_ETAG ||
1355
- !STRONG_ETAG_PATTERN.test(etag))) {
1356
- throw clientValidationError("getDelegatedQurlBatch: etag must be a valid strong ETag");
1357
- }
1358
- }
1359
- function delegatedResponseError(status, detail, requestId) {
1360
- return unexpectedResponseError(`Delegated qURL batch ${detail}`, { status, requestId });
1361
- }
1362
- function delegatedResponseObject(value, status, label, requestId) {
1363
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
1364
- throw delegatedResponseError(status, `${label} is not an object`, requestId);
1365
- }
1366
- return value;
1367
- }
1368
- function delegatedEnvelope(envelope, status) {
1369
- if (typeof envelope.meta !== "object" || envelope.meta === null || Array.isArray(envelope.meta)) {
1370
- throw delegatedResponseError(status, "response meta is not an object");
1371
- }
1372
- const meta = envelope.meta;
1373
- if (typeof meta.request_id !== "string") {
1374
- throw delegatedResponseError(status, "response meta has invalid request_id");
1375
- }
1376
- const data = delegatedResponseObject(envelope.data, status, "response data", meta.request_id);
1377
- return { data, requestId: meta.request_id };
1378
- }
1379
- function requiredDelegatedHeader(envelope, name, status, requestId) {
1380
- const headers = envelope.__http_headers;
1381
- if (!headers || typeof headers.get !== "function") {
1382
- throw delegatedResponseError(status, "response headers are unavailable", requestId);
1383
- }
1384
- const value = headers.get(name);
1385
- if (value === null) {
1386
- throw delegatedResponseError(status, `response is missing ${name}`, requestId);
1387
- }
1388
- return value;
1389
- }
1390
- function hasDelegatedNoStore(value) {
1391
- if (value === null)
1392
- return false;
1393
- const directives = new Set(value.split(",").map((directive) => directive.trim().split("=", 1)[0].toLowerCase()));
1394
- return directives.has("private") && directives.has("no-store");
1395
- }
1396
- function delegatedResponseHeaders(envelope, status, requestId) {
1397
- const cacheControl = envelope.__http_headers?.get("Cache-Control") ?? null;
1398
- if (cacheControl !== null) {
1399
- if (!hasDelegatedNoStore(cacheControl)) {
1400
- throw delegatedResponseError(status, "response has invalid Cache-Control", requestId);
1401
- }
1402
- }
1403
- else if (status !== 304) {
1404
- requiredDelegatedHeader(envelope, "Cache-Control", status, requestId);
1405
- }
1406
- const etag = envelope.__http_headers?.get("ETag") ?? undefined;
1407
- if (etag !== undefined && (etag.length > MAX_DELEGATED_ETAG || !STRONG_ETAG_PATTERN.test(etag))) {
1408
- throw delegatedResponseError(status, "response has invalid strong ETag", requestId);
1409
- }
1410
- const rawRetryAfter = envelope.__http_headers?.get("Retry-After") ?? "";
1411
- const trimmed = rawRetryAfter.trim();
1412
- const seconds = /^\d+$/.test(trimmed)
1413
- ? Number(trimmed)
1414
- : /^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/.test(trimmed)
1415
- ? Math.ceil((Date.parse(trimmed) - Date.now()) / 1000)
1416
- : NaN;
1417
- const retryAfter = Number.isSafeInteger(seconds) && seconds > 0 && seconds <= RETRY_AFTER_PARSE_LIMIT_S
1418
- ? seconds
1419
- : undefined;
1420
- return { etag, retryAfter };
1421
- }
1422
- function isUtcTimestamp(value) {
1423
- return (typeof value === "string" &&
1424
- UTC_TIMESTAMP_PATTERN.test(value) &&
1425
- Number.isFinite(Date.parse(value)));
1426
- }
1427
- function delegatedBatchBase(data, status, requestId, expectedBatchId) {
1428
- if (typeof data.batch_id !== "string" ||
1429
- !DELEGATED_BATCH_ID_PATTERN.test(data.batch_id) ||
1430
- (expectedBatchId !== undefined && data.batch_id !== expectedBatchId)) {
1431
- throw delegatedResponseError(status, "response has invalid batch_id", requestId);
1432
- }
1433
- if (!Number.isInteger(data.item_count) ||
1434
- Number(data.item_count) < 1 ||
1435
- Number(data.item_count) > MAX_DELEGATED_BATCH_ITEMS) {
1436
- throw delegatedResponseError(status, "response has invalid item_count", requestId);
1437
- }
1438
- if (!isUtcTimestamp(data.submitted_at)) {
1439
- throw delegatedResponseError(status, "response has invalid submitted_at", requestId);
1440
- }
1441
- return {
1442
- batchId: data.batch_id,
1443
- itemCount: Number(data.item_count),
1444
- submittedAt: data.submitted_at,
1445
- };
1446
- }
1447
- function validateDelegatedLocation(value, baseUrl, batchId, status, requestId) {
1448
- let location;
1449
- try {
1450
- const base = new URL(`${baseUrl}/`);
1451
- const relative = value.startsWith("/v1/delegated-qurl-batches/")
1452
- ? `${base.pathname.replace(/\/$/, "")}${value}`
1453
- : value;
1454
- location = new URL(relative, base);
1455
- }
1456
- catch {
1457
- throw delegatedResponseError(status, "response has invalid Location", requestId);
1458
- }
1459
- const expected = new URL(`${baseUrl}/v1/delegated-qurl-batches/${batchId}`);
1460
- if (location.username !== "" ||
1461
- location.password !== "" ||
1462
- location.search !== "" ||
1463
- location.hash !== "" ||
1464
- location.href !== expected.href) {
1465
- throw delegatedResponseError(status, "response has invalid Location", requestId);
1466
- }
1467
- return location.href;
1468
- }
1469
- function validateDelegatedQurlLink(value) {
1470
- if (typeof value !== "string")
1471
- return false;
1472
- try {
1473
- const link = new URL(value);
1474
- return (link.protocol === "https:" &&
1475
- link.username === "" &&
1476
- link.password === "" &&
1477
- link.hash.length > 1);
1478
- }
1479
- catch {
1480
- return false;
1481
- }
1482
- }
1483
- function validateDelegatedQurl(value, expectedQurlId, status, requestId) {
1484
- const data = delegatedResponseObject(value, status, "response data", requestId);
1485
- if (data.qurl_id !== expectedQurlId) {
1486
- throw delegatedResponseError(status, "response has invalid qurl_id", requestId);
1487
- }
1488
- if (data.status !== "active" &&
1489
- data.status !== "consumed" &&
1490
- data.status !== "expired" &&
1491
- data.status !== "revoked") {
1492
- throw delegatedResponseError(status, "response has invalid status", requestId);
1493
- }
1494
- if (!isUtcTimestamp(data.expires_at) ||
1495
- typeof data.one_time_use !== "boolean" ||
1496
- !Number.isInteger(data.max_sessions) ||
1497
- Number(data.max_sessions) < 0 ||
1498
- !Number.isInteger(data.session_duration) ||
1499
- Number(data.session_duration) <= 0 ||
1500
- (data.label !== undefined && typeof data.label !== "string") ||
1501
- (data.access_policy !== undefined &&
1502
- (typeof data.access_policy !== "object" ||
1503
- data.access_policy === null ||
1504
- Array.isArray(data.access_policy)))) {
1505
- throw delegatedResponseError(status, "response has invalid qURL metadata", requestId);
1506
- }
1507
- return { ...data, request_id: requestId };
1508
- }
1509
- function validateDelegatedBatchResults(value, itemCount, batchStatus, status, requestId) {
1510
- if (!Array.isArray(value) || value.length !== itemCount) {
1511
- throw delegatedResponseError(status, "terminal response has invalid results", requestId);
1512
- }
1513
- let succeeded = 0;
1514
- const qurlIds = new Set();
1515
- const links = new Set();
1516
- const results = value.map((raw, index) => {
1517
- const item = delegatedResponseObject(raw, status, `result ${index}`, requestId);
1518
- if (item.index !== index) {
1519
- throw delegatedResponseError(status, `result ${index} has invalid index`, requestId);
1520
- }
1521
- if (item.status === "succeeded") {
1522
- if (item.error !== undefined && item.error !== null) {
1523
- throw delegatedResponseError(status, `result ${index} has conflicting fields`, requestId);
1524
- }
1525
- const qurl = delegatedResponseObject(item.qurl, status, `result ${index} qurl`, requestId);
1526
- if (typeof qurl.qurl_id !== "string" ||
1527
- !DELEGATED_QURL_ID_PATTERN.test(qurl.qurl_id) ||
1528
- !validateDelegatedQurlLink(qurl.qurl_link) ||
1529
- !isUtcTimestamp(qurl.expires_at) ||
1530
- qurlIds.has(qurl.qurl_id) ||
1531
- links.has(qurl.qurl_link)) {
1532
- throw delegatedResponseError(status, `result ${index} has invalid qurl`, requestId);
1533
- }
1534
- qurlIds.add(qurl.qurl_id);
1535
- links.add(qurl.qurl_link);
1536
- succeeded++;
1537
- return {
1538
- index,
1539
- status: "succeeded",
1540
- qurl: {
1541
- qurl_id: qurl.qurl_id,
1542
- qurl_link: qurl.qurl_link,
1543
- expires_at: qurl.expires_at,
1544
- },
1545
- };
1546
- }
1547
- if (item.status !== "failed" || (item.qurl !== undefined && item.qurl !== null)) {
1548
- throw delegatedResponseError(status, `result ${index} has invalid status`, requestId);
1549
- }
1550
- const error = delegatedResponseObject(item.error, status, `result ${index} error`, requestId);
1551
- if (typeof error.code !== "string" ||
1552
- error.code.length === 0 ||
1553
- typeof error.message !== "string") {
1554
- throw delegatedResponseError(status, `result ${index} has invalid error`, requestId);
1555
- }
1556
- return {
1557
- index,
1558
- status: "failed",
1559
- error: { code: error.code, message: error.message },
1560
- };
1561
- });
1562
- if ((batchStatus === "succeeded" && succeeded !== itemCount) ||
1563
- (batchStatus === "failed" && succeeded !== 0) ||
1564
- (batchStatus === "partially_failed" && (succeeded === 0 || succeeded === itemCount))) {
1565
- throw delegatedResponseError(status, "terminal status does not match results", requestId);
1566
- }
1567
- return results;
1568
- }
1569
- function classifyDelegatedBatchCreateFailure(error, batchId) {
1570
- if (!(error instanceof errors_js_1.QURLError)) {
1571
- throw new errors_js_1.DelegatedBatchOutcomeUnknownError(new errors_js_1.RuntimeError("Unexpected failure after delegated batch dispatch", { cause: error }), batchId);
1572
- }
1573
- if ((error.status >= 400 && error.status < 500 && error.status !== 408) || error.status === 501) {
1574
- throw error;
1575
- }
1576
- throw new errors_js_1.DelegatedBatchOutcomeUnknownError(error, batchId);
1577
- }
1578
- // `format: uri` in the OpenAPI spec allows schemes the SDK doesn't
1579
- // usefully support (`ftp://`, `file://`, `javascript:`, …). This is a
1580
- // cheap client-side sanity check — the server is still the
1581
- // authoritative validator (e.g. it rejects localhost, cloud metadata,
1582
- // and private-range hosts; the SDK doesn't need to duplicate that).
1583
- const ALLOWED_URL_SCHEMES = ["http://", "https://"];
1584
- function requireValidHttpUrl(value, field) {
1585
- // Three checks in priority order: type guard, scheme, length.
1586
- // Stop at the first failure so a non-string URL doesn't
1587
- // produce both "must be a string" AND "must be ≤ 2048 characters"
1588
- // (the length check on a non-string would also typeof-fail and
1589
- // duplicate the type message). The collect-all loop in
1590
- // validateCreateInput runs each FIELD's validator independently — within
1591
- // a single URL field itself we want fail-fast.
1592
- if (typeof value !== "string") {
1593
- throw clientValidationError(`${field}: must be a string (got ${value === null ? "null" : typeof value})`);
1594
- }
1595
- if (!ALLOWED_URL_SCHEMES.some((scheme) => value.startsWith(scheme))) {
1596
- // Truncate to keep the error message compact and avoid
1597
- // pathologically long schemes from filling logs.
1598
- const repr = JSON.stringify(value).slice(0, 40);
1599
- throw clientValidationError(`${field}: must start with http:// or https:// (got ${repr})`);
1600
- }
1601
- if (value.length > MAX_TARGET_URL) {
1602
- throw clientValidationError(`${field}: must be ${MAX_TARGET_URL} characters or fewer (got ${value.length})`);
1603
- }
1604
- }
1605
- function requireValidTargetUrl(target_url) {
1606
- requireValidHttpUrl(target_url, "target_url");
1607
- }
1608
- function validateCreateInput(input) {
1609
- // Untyped-JS guard: surface as ValidationError instead of raw TypeError.
1610
- if (typeof input !== "object" || input === null) {
1611
- throw clientValidationError(`create: input must be an object (got ${input === null ? "null" : typeof input})`);
1612
- }
1613
- // Collect-all: run every validator and aggregate the messages so a
1614
- // caller fixing multiple bad fields sees them all at once instead
1615
- // of fix-re-run-repeat. Matches the documented batchCreate UX
1616
- // ("All failures are collected into a single ValidationError")
1617
- // and mirrors the requireValidTags collect-all pattern.
1618
- const errors = [];
1619
- const collect = (fn) => {
1620
- try {
1621
- fn();
1622
- }
1623
- catch (err) {
1624
- if (err instanceof errors_js_1.ValidationError) {
1625
- errors.push(err.detail);
1626
- return;
1627
- }
1628
- throw err;
1629
- }
1630
- };
1631
- collect(() => requireNoUnknownFields(input, CREATE_FIELD_KEYS, "create"));
1632
- const resourceType = input.type;
1633
- if (resourceType !== undefined && typeof resourceType !== "string") {
1634
- errors.push(`type: must be a string (got ${resourceType === null ? "null" : typeof resourceType})`);
1635
- }
1636
- collect(() => {
1637
- if (input.target_url === undefined) {
1638
- if (resourceType === undefined || resourceType === "url") {
1639
- throw clientValidationError("target_url: is required for url qURLs");
1640
- }
1641
- return;
1642
- }
1643
- requireValidTargetUrl(input.target_url);
1644
- });
1645
- collect(() => requireMaxLength(input.label, "label", MAX_LABEL));
1646
- collect(() => requireNonEmptyIfPresent(input.label, "label"));
1647
- collect(() => requireMaxLength(input.custom_domain, "custom_domain", MAX_CUSTOM_DOMAIN));
1648
- collect(() => requireNonEmptyIfPresent(input.custom_domain, "custom_domain"));
1649
- collect(() => requireBooleanIfPresent(input.private, "private"));
1650
- collect(() => requireBooleanIfPresent(input.one_time_use, "one_time_use"));
1651
- collect(() => requireMaxSessionsInRange(input.max_sessions));
1652
- collect(() => requireNonEmptyIfPresent(input.expires_in, "expires_in"));
1653
- collect(() => requireNonEmptyIfPresent(input.session_duration, "session_duration"));
1654
- collect(() => requireValidAccessPolicy(input.access_policy));
1655
- if (errors.length > 0) {
1656
- // Inner `; ` is contractual (paired with batchCreate's outer ` | `).
1657
- // Future field-level validators must not aggregate via `; ` internally.
1658
- throw clientValidationError(errors.join("; "));
1659
- }
1660
- // Intentional omission: duration *grammar* (`expires_in`,
1661
- // `session_duration`) is server-authoritative. Empty strings are
1662
- // rejected up front (see collect() calls above) but unit/whitespace
1663
- // edge cases ("24hh", "1w 3d") surface as a clean server 400 — a
1664
- // client-side regex would just create a drift surface against the
1665
- // server's rego/duration parser.
1666
- //
1667
- // `access_policy` contents (CIDRs, ISO-3166 codes, regex patterns)
1668
- // are pass-through for the same reason. The shape-guard above only
1669
- // catches structural mistakes (string-instead-of-array on list fields)
1670
- // an untyped-JS caller is likely to make.
1671
- }
1672
- function validateQurlTokenOptions(input) {
1673
- if (input === undefined)
1674
- return;
1675
- requireMaxLength(input.label, "label", MAX_LABEL);
1676
- requireNonEmptyIfPresent(input.label, "label");
1677
- requireNonEmptyIfPresent(input.expires_in, "expires_in");
1678
- requireNonEmptyIfPresent(input.session_duration, "session_duration");
1679
- // Mirror the service-documented size cap, but keep host-safety grammar
1680
- // (single leading slash, no scheme/host/backslash/etc.) and the tunnel-only
1681
- // gate server-authoritative (`invalid_target_path`); a client-side regex
1682
- // would create a drift surface. Keep max-before-empty consistent with label;
1683
- // empty strings pass the length check and are rejected by the next guard.
1684
- requireMaxUtf8Bytes(input.target_path, "target_path", MAX_TARGET_PATH);
1685
- requireNonEmptyIfPresent(input.target_path, "target_path");
1686
- requireBooleanIfPresent(input.one_time_use, "one_time_use");
1687
- requireMaxSessionsInRange(input.max_sessions);
1688
- requireValidAccessPolicy(input.access_policy);
1689
- }
1690
- function validateResourceQurlUpdateInput(input) {
1691
- requireMaxLength(input.label, "label", MAX_LABEL);
1692
- requireNonEmptyIfPresent(input.label, "label");
1693
- requireNonEmptyIfPresent(input.extend_by, "extend_by");
1694
- requireNonEmptyIfPresent(input.expires_at, "expires_at");
1695
- requireNonEmptyIfPresent(input.session_duration, "session_duration");
1696
- requireMaxSessionsInRange(input.max_sessions);
1697
- requireValidAccessPolicy(input.access_policy);
1698
- if (input.extend_by !== undefined && input.expires_at !== undefined) {
1699
- throw clientValidationError("updateResourceQurl: `extend_by` and `expires_at` are mutually exclusive — provide at most one");
1700
- }
1701
- }
1702
- function requireObjectInput(input, method) {
1703
- if (typeof input !== "object" || input === null || Array.isArray(input)) {
1704
- throw clientValidationError(`${method}: input must be an object (got ${input === null ? "null" : typeof input})`);
1705
- }
1706
- }
1707
- function requireNonEmptyStringField(input, field, method) {
1708
- const value = input[field];
1709
- if (typeof value !== "string" || value.length === 0) {
1710
- throw clientValidationError(`${method}: ${field} must be a non-empty string`);
1711
- }
1712
- }
1713
- function requireNonEmptyArrayField(input, field, method) {
1714
- const value = input[field];
1715
- if (!Array.isArray(value) || value.length === 0) {
1716
- throw clientValidationError(`${method}: ${field} must be a non-empty array`);
1717
- }
1718
- }
1719
- function requireStringArrayElements(value, field, method) {
1720
- if (!Array.isArray(value)) {
1721
- throw clientValidationError(`${method}: ${field} must be a non-empty array`);
1722
- }
1723
- value.forEach((entry, index) => {
1724
- if (typeof entry !== "string" || entry.length === 0) {
1725
- throw clientValidationError(`${method}: ${field}[${index}] must be a non-empty string`);
1726
- }
1727
- });
1728
- }
1729
- function requireAtLeastOneField(input, fields, method) {
1730
- if (!fields.some((field) => input[field] !== undefined)) {
1731
- throw clientValidationError(`${method}: at least one field (${fields.join(", ")}) must be provided`);
1732
- }
1733
- }
1734
- function requireNoUnknownFields(input, fields, method) {
1735
- const unknown = Object.keys(input).filter((field) => !fields.includes(field));
1736
- if (unknown.length > 0) {
1737
- const rendered = unknown.map((field) => `"${field}"`).join(", ");
1738
- const label = unknown.length === 1 ? "unknown field" : "unknown fields";
1739
- throw clientValidationError(`${method}: ${label} ${rendered}`);
1740
- }
1741
- }
1742
- function normalizePatchFields(input, fields, options = {}) {
1743
- const preserveNull = new Set(options.preserveNullFields ?? []);
1744
- const normalized = {};
1745
- for (const key of fields) {
1746
- const value = input[key];
1747
- if (value !== undefined && (value !== null || preserveNull.has(key))) {
1748
- normalized[key] = value;
1749
- }
1750
- }
1751
- return normalized;
1752
- }
1753
- function requireConsumerDecision(id, version, allowNew = false) {
1754
- if (typeof id !== "string" ||
1755
- (!/^[a-f0-9]{64}$/.test(id) && !(allowNew && id === "new")) ||
1756
- !Number.isSafeInteger(version) ||
1757
- version < (allowNew ? 0 : 1))
1758
- throw clientValidationError("consumer decision: invalid id or expected version");
1759
- }
1760
- function validateResourceWriteFields(input, options = {}) {
1761
- const errors = [];
1762
- const collect = (fn) => {
1763
- try {
1764
- fn();
1765
- }
1766
- catch (err) {
1767
- if (err instanceof errors_js_1.ValidationError) {
1768
- errors.push(err.detail);
1769
- return;
1770
- }
1771
- throw err;
1772
- }
1773
- };
1774
- collect(() => {
1775
- if (input.private !== undefined && typeof input.private !== "boolean") {
1776
- throw clientValidationError("private: must be a boolean");
1777
- }
1778
- if (input.consumer_access !== undefined) {
1779
- const policy = input.consumer_access;
1780
- requireObjectInput(policy, "consumer_access");
1781
- const p = policy;
1782
- requireNoUnknownFields(p, ["mode", "version", "contract_id"], "consumer_access");
1783
- if (!["keys", "invite_only", "email_capture", "review", "contract"].includes(p.mode) ||
1784
- !Number.isSafeInteger(p.version) ||
1785
- p.version < 1) {
1786
- throw clientValidationError("consumer_access: invalid mode or version");
1787
- }
1788
- if (p.mode === "contract") {
1789
- if (typeof p.contract_id !== "string" ||
1790
- !p.contract_id.trim() ||
1791
- new TextEncoder().encode(p.contract_id).length > 128) {
1792
- throw clientValidationError("consumer_access: contract mode requires a bounded contract_id");
1793
- }
1794
- }
1795
- else if (p.contract_id !== undefined) {
1796
- throw clientValidationError("consumer_access: contract_id requires contract mode");
1797
- }
1798
- if (input.private === false)
1799
- throw clientValidationError("consumer_access: requires a private resource");
1800
- if (options.requireUrlTarget) {
1801
- if (p.version !== 1 || input.private !== true)
1802
- throw clientValidationError("consumer_access: creation requires private true and version one");
1803
- }
1804
- else if (options.allowAliasClear) {
1805
- const expected = input.expected_consumer_access_version;
1806
- if (!Number.isSafeInteger(expected) ||
1807
- expected < 0 ||
1808
- p.version !== expected + 1 ||
1809
- Object.keys(input).some((key) => key !== "consumer_access" && key !== "expected_consumer_access_version")) {
1810
- throw clientValidationError("consumer_access: update requires its expected version and no metadata changes");
1811
- }
1812
- }
1813
- }
1814
- else if (input.expected_consumer_access_version !== undefined) {
1815
- throw clientValidationError("expected_consumer_access_version: requires consumer_access");
1816
- }
1817
- });
1818
- collect(() => {
1819
- const fields = ["allowed_device_keys_add", "allowed_device_keys_remove"];
1820
- for (const field of fields) {
1821
- const keys = input[field];
1822
- if (keys === undefined)
1823
- continue;
1824
- requireStringArrayElements(keys, field, "updateResource");
1825
- if (keys.length > 256 || new Set(keys).size !== keys.length) {
1826
- throw clientValidationError(`${field}: must contain at most 256 unique keys`);
1827
- }
1828
- for (const key of keys) {
1829
- let canonical = false;
1830
- try {
1831
- canonical = key.length === 44 && atob(key).length === 32 && btoa(atob(key)) === key;
1832
- }
1833
- catch {
1834
- /* Invalid base64 is rejected below. */
1835
- }
1836
- if (!canonical) {
1837
- throw clientValidationError(`${field}: keys must be canonical padded base64 of 32 bytes`);
1838
- }
1839
- }
1840
- if (input.custom_domain !== undefined) {
1841
- throw clientValidationError(`${field}: update separately from custom_domain`);
1842
- }
1843
- }
1844
- const added = new Set(input.allowed_device_keys_add);
1845
- if (input.allowed_device_keys_remove?.some((key) => added.has(key))) {
1846
- throw clientValidationError("allowed device key additions and removals must not overlap");
1847
- }
1848
- });
1849
- const resourceType = input.type;
1850
- if (resourceType !== undefined && typeof resourceType !== "string") {
1851
- errors.push(`type: must be a string (got ${resourceType === null ? "null" : typeof resourceType})`);
1852
- }
1853
- collect(() => {
1854
- if (input.target_url === undefined) {
1855
- if (options.requireUrlTarget && (resourceType === undefined || resourceType === "url")) {
1856
- throw clientValidationError("target_url: is required for url resources");
1857
- }
1858
- return;
1859
- }
1860
- requireValidTargetUrl(input.target_url);
1861
- });
1862
- collect(() => requireMaxLength(input.description, "description", MAX_DESCRIPTION));
1863
- collect(() => requireMaxLength(input.custom_domain, "custom_domain", MAX_CUSTOM_DOMAIN));
1864
- collect(() => {
1865
- if (options.allowCustomDomainClear && input.custom_domain === "")
1866
- return;
1867
- requireNonEmptyIfPresent(input.custom_domain, "custom_domain");
1868
- });
1869
- collect(() => requireValidTags(input.tags));
1870
- collect(() => {
1871
- if (input.alias !== undefined) {
1872
- if (input.alias === null) {
1873
- if (!options.allowAliasClear) {
1874
- throw clientValidationError("alias: must be a string (got null)");
1875
- }
1876
- }
1877
- else {
1878
- requireMaxLength(input.alias, "alias", MAX_ALIAS);
1879
- requireNonEmptyIfPresent(input.alias, "alias");
1880
- }
1881
- }
1882
- });
1883
- collect(() => {
1884
- if (input.slug === undefined)
1885
- return;
1886
- requireMaxLength(input.slug, "slug", MAX_SLUG);
1887
- requireNonEmptyIfPresent(input.slug, "slug");
1888
- });
1889
- collect(() => {
1890
- if (options.validateFindOrCreate &&
1891
- input.find_or_create !== undefined &&
1892
- typeof input.find_or_create !== "boolean") {
1893
- throw clientValidationError(`find_or_create: must be a boolean (got ${describeShape(input.find_or_create)})`);
1894
- }
1895
- });
1896
- collect(() => {
1897
- if (options.validatePreserveHost &&
1898
- input.preserve_host !== undefined &&
1899
- typeof input.preserve_host !== "boolean") {
1900
- throw clientValidationError(`preserve_host: must be a boolean (got ${describeShape(input.preserve_host)})`);
1901
- }
1902
- });
1903
- if (errors.length > 0) {
1904
- throw clientValidationError(errors.join("; "));
1905
- }
1906
- }
1907
- function validateWebhookWriteFields(input, method, requiredFields = {}) {
1908
- if (requiredFields.url || input.url !== undefined) {
1909
- requireNonEmptyStringField(input, "url", method);
1910
- requireValidHttpUrl(input.url, "url");
1911
- }
1912
- if (requiredFields.events || input.events !== undefined) {
1913
- requireNonEmptyArrayField(input, "events", method);
1914
- requireStringArrayElements(input.events, "events", method);
1915
- }
1916
- requireMaxLength(input.description, "description", MAX_DESCRIPTION);
1917
- if (input.status !== undefined) {
1918
- requireNonEmptyStringField(input, "status", method);
1919
- }
1920
- }
1921
- function validateApiKeyWriteFields(input, method, requiredFields = {}) {
1922
- if (requiredFields.name || input.name !== undefined) {
1923
- requireNonEmptyStringField(input, "name", method);
1924
- requireMaxLength(input.name, "name", MAX_API_KEY_NAME);
1925
- }
1926
- if (requiredFields.scopes || input.scopes !== undefined) {
1927
- requireNonEmptyArrayField(input, "scopes", method);
1928
- requireStringArrayElements(input.scopes, "scopes", method);
1929
- const unsupported = input.scopes.find((scope) => !API_KEY_REQUEST_SCOPES.has(scope));
1930
- if (unsupported !== undefined) {
1931
- throw clientValidationError(`${method}: scopes contains an unsupported permission ${JSON.stringify(unsupported)}; this SDK accepts ${API_KEY_REQUEST_SCOPE_VALUES.join(", ")}`);
1932
- }
1933
- }
1934
- }
1935
- function validateEnrollmentTokenFields(input, method) {
1936
- if (input.target !== undefined &&
1937
- (typeof input.target !== "string" || !CREDENTIAL_TARGETS.has(input.target))) {
1938
- throw clientValidationError(`${method}: target must be 'agent' or 'connector'`);
1939
- }
1940
- let claimCount = 0;
1941
- if (input.claims !== undefined) {
1942
- if (!Array.isArray(input.claims)) {
1943
- throw clientValidationError(`${method}: claims must be an array`);
1944
- }
1945
- if (input.claims.length > 1) {
1946
- throw clientValidationError(`${method}: claims must contain at most one claim`);
1947
- }
1948
- claimCount = input.claims.length;
1949
- for (const [index, claim] of input.claims.entries()) {
1950
- if (!claim || typeof claim !== "object" || Array.isArray(claim)) {
1951
- throw clientValidationError(`${method}: claims[${index}] must be an object`);
1952
- }
1953
- requireNoUnknownFields(claim, ["type", "id"], `${method}: claims[${index}]`);
1954
- const record = claim;
1955
- if (typeof record.type !== "string" || !CREDENTIAL_CLAIM_TYPES.has(record.type)) {
1956
- throw clientValidationError(`${method}: claims[${index}].type must be 'connector'`);
1957
- }
1958
- if (typeof record.id !== "string" || !CONNECTOR_SLUG_PATTERN.test(record.id)) {
1959
- throw clientValidationError(`${method}: claims[${index}].id is not a valid connector slug; use 3-64 lowercase letters, digits, or hyphens, starting with a letter and ending with a letter or digit`);
1960
- }
1961
- }
1962
- }
1963
- // CreateApiKeyRequest.target permits an agent with zero or one connector claim.
1964
- if (input.target === "connector" && claimCount !== 1) {
1965
- throw clientValidationError(`${method}: target 'connector' requires exactly one claim`);
1966
- }
1967
- if (input.expires_in !== undefined) {
1968
- if (typeof input.expires_in !== "string") {
1969
- throw clientValidationError(`${method}: expires_in must be a duration string`);
1970
- }
1971
- const match = CREDENTIAL_DURATION_PATTERN.exec(input.expires_in);
1972
- if (!match) {
1973
- throw clientValidationError(`${method}: expires_in must match <integer><s|m|h|d|w>`);
1974
- }
1975
- const seconds = BigInt(match[1]) * CREDENTIAL_DURATION_MULTIPLIERS[match[2]];
1976
- if (seconds <= 0n || seconds > MAX_ENROLLMENT_TOKEN_TTL_SECONDS) {
1977
- throw clientValidationError(`${method}: expires_in must be greater than zero and at most 24h`);
1978
- }
1979
- }
1980
- }
1981
- /**
1982
- * Per-entry shape guard for {@link BatchItemResult}. Verifies every
1983
- * non-optional field on the branch of the discriminated union selected
1984
- * by the `success` discriminant. Protects consumers who narrow on
1985
- * `success` and then access `resource_id` / `qurl_link` / `error` as
1986
- * guaranteed-present — if the API ever omits one, we trip the guard
1987
- * rather than returning `undefined` where the type says `string`.
1988
- *
1989
- * Returns `null` when the entry is valid, or a human-readable reason
1990
- * string describing the *specific* field that's missing or wrong. The
1991
- * reason never echoes the entry's values — only field names and shape
1992
- * descriptors — so the caller can safely surface it in error messages
1993
- * that may end up in observability pipelines. Contrast with the outer
1994
- * guard's intentional silence about response-body content for the
1995
- * same info-leak reason.
1996
- */
1997
- function batchItemResultValidationReason(entry) {
1998
- if (!entry || typeof entry !== "object")
1999
- return "not an object";
2000
- const e = entry;
2001
- // `Number.isInteger` rejects NaN, Infinity, and floats; combined
2002
- // with `>= 0`, the check rules out pathological proxy bodies that
2003
- // would attribute to a useless index. `index` is informational, so
2004
- // this isn't a correctness hazard, but tightening the constraint
2005
- // matches the same posture as the count integer guards above.
2006
- if (!Number.isInteger(e.index) || e.index < 0) {
2007
- return "missing/invalid 'index' (expected non-negative integer)";
2008
- }
2009
- if (e.success === true) {
2010
- if (typeof e.resource_id !== "string") {
2011
- return "success-branch missing required field 'resource_id' (expected string)";
2012
- }
2013
- if (typeof e.qurl_link !== "string") {
2014
- return "success-branch missing required field 'qurl_link' (expected string)";
2015
- }
2016
- if (typeof e.qurl_site !== "string") {
2017
- return "success-branch missing required field 'qurl_site' (expected string)";
2018
- }
2019
- return null;
2020
- }
2021
- if (e.success === false) {
2022
- if (!e.error || typeof e.error !== "object") {
2023
- return "failure-branch missing required field 'error' (expected object)";
2024
- }
2025
- const err = e.error;
2026
- if (typeof err.code !== "string") {
2027
- return "failure-branch missing required field 'error.code' (expected string)";
2028
- }
2029
- if (typeof err.message !== "string") {
2030
- return "failure-branch missing required field 'error.message' (expected string)";
2031
- }
2032
- return null;
2033
- }
2034
- return "missing/invalid 'success' discriminant (expected boolean)";
2035
- }
2036
- // ---- Portal-verb helpers (mirrors qurl-go) -------------------------------
2037
- // Client-side guardrails for the portal flow (protectUrl → createPortal →
2038
- // enterPortal), kept in lockstep with qurl-go's option validation and
2039
- // qurl-python's port of it.
2040
- /**
2041
- * Client-side floor for portal lifetimes, mirroring qurl-go's `ValidFor`.
2042
- * The LayerV API remains the source of truth for account limits; there is
2043
- * intentionally no SDK-side maximum because an account may allow
2044
- * longer-lived portals.
2045
- */
2046
- const MIN_PORTAL_VALID_FOR_SECONDS = 60;
2047
- /**
2048
- * Client-side floor for session durations, mirroring qurl-go's
2049
- * `WithSessionDuration` (at least one second; zero is rejected rather than
2050
- * treated as the server default).
2051
- */
2052
- const MIN_SESSION_DURATION_SECONDS = 1;
2053
- /**
2054
- * Shape of qurl-go's offline signed-fragment links: three or more
2055
- * dot-separated base64url parts (<version>.<claims>.<secret>.<sig>).
2056
- * Detected only to give those links a precise error in extractAccessToken.
2057
- *
2058
- * @internal Exported for in-package tests; not re-exported from index.ts.
2059
- */
2060
- exports.SIGNED_FRAGMENT_RE = /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+){2,}$/;
2061
- // Access-token grammar accepted by enterPortal (base64url charset).
2062
- const ACCESS_TOKEN_RE = /^[A-Za-z0-9_-]+$/;
2063
- /**
2064
- * Serialize a duration option to the API's h/m/s duration grammar.
2065
- *
2066
- * Strings pass through unvalidated — the server is authoritative, same as
2067
- * the REST-shaped `expires_in` fields. Numbers are milliseconds and get
2068
- * qurl-go's client-side guardrails: whole seconds only and at least
2069
- * `minimumSeconds` (checked in that order so e.g. 30.5s reports "at least
2070
- * 60s" in every SDK). Serialized with hours as the largest unit so all API
2071
- * duration fields use the same grammar across SDKs.
2072
- */
2073
- function serializeApiDuration(value, field, minimumSeconds) {
2074
- if (value === undefined)
2075
- return undefined;
2076
- if (typeof value === "string") {
2077
- if (value.trim() === "") {
2078
- throw clientValidationError(`${field}: must be a non-empty duration string`);
2079
- }
2080
- return value;
2081
- }
2082
- if (typeof value === "number") {
2083
- // NaN fails no comparison, so an explicit finite check keeps the
2084
- // error actionable instead of a misleading "whole seconds" report.
2085
- if (!Number.isFinite(value)) {
2086
- throw clientValidationError(`${field}: must be a finite number of milliseconds`);
2087
- }
2088
- if (value < minimumSeconds * 1000) {
2089
- throw clientValidationError(`${field}: must be at least ${minimumSeconds}s`);
2090
- }
2091
- if (value % 1000 !== 0) {
2092
- throw clientValidationError(`${field}: must be whole seconds`);
2093
- }
2094
- const seconds = value / 1000;
2095
- if (seconds % 3600 === 0)
2096
- return `${seconds / 3600}h`;
2097
- if (seconds % 60 === 0)
2098
- return `${seconds / 60}m`;
2099
- return `${seconds}s`;
2100
- }
2101
- throw clientValidationError(`${field}: must be a duration string (e.g. "5m") or a number of milliseconds`);
2102
- }
2103
- /**
2104
- * Portal-verb target URL validation (qurl-go `ProtectURL` parity). On top of
2105
- * the shared scheme/length check, the URL must carry a host and must parse,
2106
- * and embedded credentials (userinfo) are rejected. The added checks never
2107
- * echo the URL — a userinfo URL contains credentials that must stay out of
2108
- * logs and error messages. The REST-shaped compatibility layer keeps its
2109
- * original prefix-only check so its accepted inputs don't change.
2110
- */
2111
- function requirePortalTargetUrl(targetUrl) {
2112
- requireValidTargetUrl(targetUrl);
2113
- const url = targetUrl; // requireValidTargetUrl only returns for http(s) strings
2114
- // WHATWG URL normalizes away an empty authority ("https:///x" parses
2115
- // with host "x"), so the host-presence check runs on the raw string,
2116
- // mirroring qurl-go's url.Parse which rejects authority-less URLs.
2117
- const afterScheme = url.slice(url.indexOf("://") + 3);
2118
- if (afterScheme === "" || "/?#".includes(afterScheme[0])) {
2119
- throw clientValidationError("target_url: must include a host");
2120
- }
2121
- let parsed;
2122
- try {
2123
- parsed = new URL(url);
2124
- }
2125
- catch {
2126
- throw clientValidationError("target_url: malformed URL");
2127
- }
2128
- if (parsed.username !== "" || parsed.password !== "") {
2129
- throw clientValidationError("target_url: must not include embedded credentials (userinfo)");
2130
- }
2131
- }
2132
- /**
2133
- * Extract the access token from a qURL link, or pass a bare token through.
2134
- *
2135
- * Platform-issued links carry the access token in the URL fragment
2136
- * (`https://qurl.link/#at_...`). Error messages deliberately never echo the
2137
- * input — qURL links are credentials and must stay out of logs.
2138
- */
2139
- function extractAccessToken(qurlLink) {
2140
- if (typeof qurlLink !== "string" || qurlLink.trim() === "") {
2141
- throw clientValidationError("qurlLink: must be a non-empty string");
2142
- }
2143
- const hashIndex = qurlLink.indexOf("#");
2144
- const token = hashIndex >= 0 ? qurlLink.slice(hashIndex + 1) : qurlLink;
2145
- if (exports.SIGNED_FRAGMENT_RE.test(token)) {
2146
- throw clientValidationError("qurlLink: this is a signed qURL link (offline fragment format), which cannot be " +
2147
- "opened through the API resolver — enterPortal only accepts platform links " +
2148
- "(https://qurl.link/#at_...) or bare access tokens");
2149
- }
2150
- if (token === "" || !ACCESS_TOKEN_RE.test(token)) {
2151
- throw clientValidationError("qurlLink: no access token found — pass a platform qURL link " +
2152
- "(https://qurl.link/#at_...) or a bare access token");
2153
- }
2154
- return token;
2155
- }
2156
- /**
2157
- * Parse an RFC 3339 timestamp from the API into a `Date`, or `undefined`
2158
- * when absent or unparseable. The portal surface exposes `Date` (matching
2159
- * qurl-go's `time.Time` and qurl-python's `datetime`); the REST-shaped
2160
- * layer keeps raw wire strings.
2161
- */
2162
- function parseApiDate(value) {
2163
- if (typeof value !== "string" || value === "")
2164
- return undefined;
2165
- const parsed = new Date(value);
2166
- return Number.isNaN(parsed.getTime()) ? undefined : parsed;
2167
- }
2168
- /**
2169
- * Parse a {@link Portal} from a create-portal API response. `resource_id`
2170
- * and `qurl_link` are required, mirroring qurl-go's fail-closed response
2171
- * check; everything else is optional because the two mint endpoints differ
2172
- * in which ancillary fields they return.
2173
- */
2174
- async function parsePortal(data, method, expectedCrid) {
2175
- if (typeof data?.resource_id !== "string" || data.resource_id.trim() === "") {
2176
- throw unexpectedResponseError(`${method}: response is missing resource_id`);
2177
- }
2178
- if (typeof data.qurl_link !== "string" || data.qurl_link.trim() === "") {
2179
- throw unexpectedResponseError(`${method}: response is missing qurl_link`);
2180
- }
2181
- await verifyResourceIdentity(data, method, expectedCrid);
2182
- return {
2183
- crid: data.crid,
2184
- resourcePublicKey: data.resource_id,
2185
- link: data.qurl_link,
2186
- site: data.qurl_site,
2187
- expiresAt: parseApiDate(data.expires_at),
2188
- // Same empty-string → absent normalization the REST-shaped layer's
2189
- // consumers apply to qurl_id.
2190
- qurlId: data.qurl_id || undefined,
2191
- label: data.label,
2192
- };
2193
- }
2194
- /**
2195
- * Validate portal options and build the create-portal wire body. The service
2196
- * requires a JSON object even when every option is omitted; `{}` selects the
2197
- * owner's plan defaults and matches qurl-go's zero-value request struct.
2198
- */
2199
- function buildCreatePortalBody(opts, method) {
2200
- requireObjectInput(opts, method);
2201
- requireNoUnknownFields(opts, CREATE_PORTAL_OPTION_KEYS, method);
2202
- // null → omitted, via the SDK-wide normalizePatchFields convention.
2203
- const { validFor, label, oneTimeUse, maxSessions, sessionDuration, targetPath } = normalizePatchFields(opts, CREATE_PORTAL_OPTION_KEYS);
2204
- if (label !== undefined) {
2205
- requireMaxLength(label, "label", MAX_LABEL);
2206
- // qurl-go's WithLabel rejects blank labels; the REST-shaped mintLink
2207
- // keeps its permissive whitespace behavior, so the check lives here.
2208
- if (label.trim() === "") {
2209
- throw clientValidationError("label: must not be empty");
2210
- }
2211
- }
2212
- requireBooleanIfPresent(oneTimeUse, "oneTimeUse");
2213
- requireMaxSessionsInRange(maxSessions, "maxSessions");
2214
- requireMaxUtf8Bytes(targetPath, "targetPath", MAX_TARGET_PATH);
2215
- requireNonEmptyIfPresent(targetPath, "targetPath");
2216
- if (targetPath !== undefined && method === "createPortalForUrl") {
2217
- throw clientValidationError("targetPath: requires an existing resource");
2218
- }
2219
- const body = {};
2220
- const expiresIn = serializeApiDuration(validFor, "validFor", MIN_PORTAL_VALID_FOR_SECONDS);
2221
- if (expiresIn !== undefined)
2222
- body.expires_in = expiresIn;
2223
- if (label !== undefined)
2224
- body.label = label;
2225
- if (oneTimeUse !== undefined)
2226
- body.one_time_use = oneTimeUse;
2227
- // Explicit 0 means unlimited and must survive body construction.
2228
- if (maxSessions !== undefined)
2229
- body.max_sessions = maxSessions;
2230
- const wireSessionDuration = serializeApiDuration(sessionDuration, "sessionDuration", MIN_SESSION_DURATION_SECONDS);
2231
- if (wireSessionDuration !== undefined)
2232
- body.session_duration = wireSessionDuration;
2233
- if (targetPath !== undefined)
2234
- body.target_path = targetPath;
2235
- return body;
340
+ }, meta);
2236
341
  }
2237
342
  /** qURL API client. */
2238
343
  class QURLClient {
@@ -2240,19 +345,29 @@ class QURLClient {
2240
345
  apiKey;
2241
346
  fetchFn;
2242
347
  maxRetries;
348
+ maxResponseBodyBytes;
2243
349
  timeout;
2244
350
  userAgent;
2245
351
  debugFn;
2246
352
  constructor(options) {
2247
353
  if (!options.apiKey) {
2248
- throw clientValidationError("apiKey is required");
354
+ throw (0, client_errors_js_1.clientValidationError)("apiKey is required");
2249
355
  }
2250
356
  // Header-injection guard: `Authorization: Bearer ${apiKey}` would
2251
357
  // otherwise be exploitable on a CR/LF-bearing key. Surface as
2252
358
  // ValidationError at construction, not as TypeError on first request.
2253
359
  if (/[\r\n]/.test(options.apiKey)) {
2254
- throw clientValidationError("apiKey: must not contain CR/LF characters");
360
+ throw (0, client_errors_js_1.clientValidationError)("apiKey: must not contain CR/LF characters");
2255
361
  }
362
+ const responseLimit = options.maxResponseBodyBytes === undefined
363
+ ? client_limits_js_1.MAX_RESPONSE_BODY_BYTES
364
+ : options.maxResponseBodyBytes;
365
+ if (!Number.isSafeInteger(responseLimit) ||
366
+ responseLimit < 1 ||
367
+ responseLimit > client_limits_js_1.MAX_CONFIGURABLE_RESPONSE_BODY_BYTES) {
368
+ throw (0, client_errors_js_1.clientValidationError)("maxResponseBodyBytes: must be an integer from 1 through 16777216 bytes");
369
+ }
370
+ this.maxResponseBodyBytes = responseLimit;
2256
371
  this.apiKey = options.apiKey;
2257
372
  const rawBaseUrl = (0, internal_js_1.stripTrailingSlashes)(options.baseUrl ?? DEFAULT_BASE_URL);
2258
373
  // Parse the URL so the loopback exemption matches on the hostname,
@@ -2265,13 +380,13 @@ class QURLClient {
2265
380
  parsedBaseUrl = new URL(rawBaseUrl);
2266
381
  }
2267
382
  catch {
2268
- throw clientValidationError(`baseUrl: must be a valid URL (got ${JSON.stringify(rawBaseUrl).slice(0, 60)})`);
383
+ throw (0, client_errors_js_1.clientValidationError)(`baseUrl: must be a valid URL (got ${JSON.stringify(rawBaseUrl).slice(0, 60)})`);
2269
384
  }
2270
385
  // Reject any non-http/https scheme outright (ftp://, file://,
2271
386
  // javascript:, ...). The loopback exemption logic below handles
2272
387
  // IPv6 bracket form on `URL.hostname` — see the inner comment.
2273
388
  if (parsedBaseUrl.protocol !== "http:" && parsedBaseUrl.protocol !== "https:") {
2274
- throw clientValidationError(`baseUrl: must use http:// or https:// scheme (got ${JSON.stringify(rawBaseUrl).slice(0, 60)})`);
389
+ throw (0, client_errors_js_1.clientValidationError)(`baseUrl: must use http:// or https:// scheme (got ${JSON.stringify(rawBaseUrl).slice(0, 60)})`);
2275
390
  }
2276
391
  if (parsedBaseUrl.protocol === "http:") {
2277
392
  // Node's `URL.hostname` returns IPv6 hosts WITH brackets (e.g.
@@ -2282,7 +397,7 @@ class QURLClient {
2282
397
  // `0.0.0.0` is bind-all, not loopback — would route the bearer to any local listener.
2283
398
  const isLoopback = host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
2284
399
  if (!isLoopback) {
2285
- throw clientValidationError(`baseUrl: must use https:// scheme (got ${JSON.stringify(rawBaseUrl).slice(0, 60)})`);
400
+ throw (0, client_errors_js_1.clientValidationError)(`baseUrl: must use https:// scheme (got ${JSON.stringify(rawBaseUrl).slice(0, 60)})`);
2286
401
  }
2287
402
  }
2288
403
  this.baseUrl = rawBaseUrl;
@@ -2302,7 +417,7 @@ class QURLClient {
2302
417
  : DEFAULT_TIMEOUT;
2303
418
  const userAgent = options.userAgent ?? `qurl-typescript/${version_js_1.VERSION}`;
2304
419
  if (/[\r\n]/.test(userAgent)) {
2305
- throw clientValidationError("userAgent: must not contain CR/LF characters");
420
+ throw (0, client_errors_js_1.clientValidationError)("userAgent: must not contain CR/LF characters");
2306
421
  }
2307
422
  this.userAgent = userAgent;
2308
423
  if (options.debug === true) {
@@ -2349,21 +464,21 @@ class QURLClient {
2349
464
  branch: "non-array",
2350
465
  qurls_type: typeof qurls,
2351
466
  });
2352
- return rest;
467
+ return (0, response_metadata_js_1.copyResponseMetadata)(raw, rest);
2353
468
  }
2354
469
  // null is semantically "no tokens" — silent drop is fine, but surface
2355
470
  // via debug so operators spot it if the API starts emitting null.
2356
471
  if (qurls === null) {
2357
472
  this.log("mapQurlsField: 'qurls' was null; dropping", { branch: "null" });
2358
- return rest;
473
+ return (0, response_metadata_js_1.copyResponseMetadata)(raw, rest);
2359
474
  }
2360
475
  if (qurls === undefined) {
2361
476
  // Symmetric with null / non-array branches.
2362
477
  this.log("mapQurlsField: 'qurls' was undefined; dropping", { branch: "undefined" });
2363
- return rest;
478
+ return (0, response_metadata_js_1.copyResponseMetadata)(raw, rest);
2364
479
  }
2365
480
  if (!rest.access_tokens) {
2366
- return { ...rest, access_tokens: qurls };
481
+ return (0, response_metadata_js_1.copyResponseMetadata)(raw, { ...rest, access_tokens: qurls });
2367
482
  }
2368
483
  // Server-side bug — drop `qurls` rather than merge. If the arrays
2369
484
  // disagree, no consistent reconciliation exists; a silent merge
@@ -2373,7 +488,7 @@ class QURLClient {
2373
488
  qurls_count: qurls.length,
2374
489
  access_tokens_count: rest.access_tokens.length,
2375
490
  });
2376
- return rest;
491
+ return (0, response_metadata_js_1.copyResponseMetadata)(raw, rest);
2377
492
  }
2378
493
  /**
2379
494
  * Validate the response envelope from POST /v1/qurls/batch and return
@@ -2397,9 +512,9 @@ class QURLClient {
2397
512
  * is malformed, additional shape errors don't help debug the upstream
2398
513
  * issue.
2399
514
  */
2400
- validateBatchCreateResponse(envelope, requestItemCount) {
2401
- const result = envelope.data;
2402
- const statusSuffix = envelope.__http_status !== undefined ? ` (HTTP ${envelope.__http_status})` : "";
515
+ validateBatchCreateResponse(response, requestItemCount) {
516
+ const result = response.envelope.data;
517
+ const statusSuffix = ` (HTTP ${response.status})`;
2403
518
  // Thread the server's request_id into the shape-guard error so
2404
519
  // operators debugging "unexpected response" tickets have the
2405
520
  // correlation handle on the error path, mirroring the success-
@@ -2409,7 +524,7 @@ class QURLClient {
2409
524
  // .requestId property AND the message string so a stack trace
2410
525
  // pasted into a support ticket carries the correlation handle
2411
526
  // without a follow-up round-trip.
2412
- const requestId = boundedErrorIdentifier(envelope.meta?.request_id);
527
+ const requestId = (0, client_errors_js_1.boundedErrorIdentifier)(response.envelope.meta?.request_id);
2413
528
  const requestIdSuffix = requestId !== undefined ? ` [request_id=${requestId}]` : "";
2414
529
  // `Number.isInteger` rejects NaN, Infinity, and floats. Combined
2415
530
  // with `>= 0`, this rules out pathological proxy bodies (e.g.
@@ -2422,10 +537,10 @@ class QURLClient {
2422
537
  !Number.isInteger(result.failed) ||
2423
538
  result.failed < 0 ||
2424
539
  !Array.isArray(result.results)) {
2425
- throw unexpectedResponseError(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}${requestIdSuffix}`, { requestId });
540
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}${requestIdSuffix}`, { requestId });
2426
541
  }
2427
542
  if (result.succeeded + result.failed !== result.results.length) {
2428
- throw unexpectedResponseError(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: ` +
543
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: ` +
2429
544
  `counts/results length mismatch (succeeded=${result.succeeded}, ` +
2430
545
  `failed=${result.failed}, results.length=${result.results.length})${requestIdSuffix}`, { requestId });
2431
546
  }
@@ -2437,7 +552,7 @@ class QURLClient {
2437
552
  // index range + dedup checks, this guarantees the seen-indices set is
2438
553
  // exactly [0, requestItemCount).
2439
554
  if (result.results.length !== requestItemCount) {
2440
- throw unexpectedResponseError(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: ` +
555
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: ` +
2441
556
  `results.length (${result.results.length}) does not match request item count (${requestItemCount})${requestIdSuffix}`, { requestId });
2442
557
  }
2443
558
  // Per-entry discriminated-union contract. Reason strings report
@@ -2448,16 +563,16 @@ class QURLClient {
2448
563
  // attribution, so fail closed to match this guard's posture.
2449
564
  const seenIndices = new Set();
2450
565
  for (let i = 0; i < result.results.length; i++) {
2451
- const reason = batchItemResultValidationReason(result.results[i]);
566
+ const reason = (0, client_resources_js_1.batchItemResultValidationReason)(result.results[i]);
2452
567
  if (reason !== null) {
2453
- throw unexpectedResponseError(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: results[${i}] ${reason}${requestIdSuffix}`, { requestId });
568
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: results[${i}] ${reason}${requestIdSuffix}`, { requestId });
2454
569
  }
2455
570
  const entry = result.results[i];
2456
571
  if (entry.index >= requestItemCount) {
2457
- throw unexpectedResponseError(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: results[${i}] index out of range (sent ${requestItemCount} items)${requestIdSuffix}`, { requestId });
572
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: results[${i}] index out of range (sent ${requestItemCount} items)${requestIdSuffix}`, { requestId });
2458
573
  }
2459
574
  if (seenIndices.has(entry.index)) {
2460
- throw unexpectedResponseError(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: results[${i}] duplicate index${requestIdSuffix}`, { requestId });
575
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected response shape from POST /v1/qurls/batch${statusSuffix}: results[${i}] duplicate index${requestIdSuffix}`, { requestId });
2461
576
  }
2462
577
  seenIndices.add(entry.index);
2463
578
  }
@@ -2473,7 +588,7 @@ class QURLClient {
2473
588
  pageCount += 1;
2474
589
  const page = await loadPage({ ...input, cursor });
2475
590
  for (const item of itemsFromPage(page)) {
2476
- yield item;
591
+ yield (0, response_metadata_js_1.copyResponseMetadata)(page, item);
2477
592
  }
2478
593
  cursor = page.next_cursor;
2479
594
  if (page.has_more === false) {
@@ -2483,11 +598,11 @@ class QURLClient {
2483
598
  break;
2484
599
  }
2485
600
  if (seenCursors.has(cursor)) {
2486
- throw unexpectedResponseError(`${methodName}: server returned repeated cursor after ${pageCount} auto-pagination pages`, { requestId: page.request_id });
601
+ throw (0, client_errors_js_1.unexpectedResponseError)(`${methodName}: server returned repeated cursor after ${pageCount} auto-pagination pages`, { requestId: page.request_id });
2487
602
  }
2488
603
  seenCursors.add(cursor);
2489
- if (pageCount >= MAX_AUTO_PAGINATION_PAGES) {
2490
- throw unexpectedResponseError(`${methodName}: exceeded ${MAX_AUTO_PAGINATION_PAGES} auto-pagination pages without termination`, { requestId: page.request_id });
604
+ if (pageCount >= client_limits_js_1.MAX_AUTO_PAGINATION_PAGES) {
605
+ throw (0, client_errors_js_1.unexpectedResponseError)(`${methodName}: exceeded ${client_limits_js_1.MAX_AUTO_PAGINATION_PAGES} auto-pagination pages without termination`, { requestId: page.request_id });
2491
606
  }
2492
607
  } while (cursor);
2493
608
  }
@@ -2500,7 +615,9 @@ class QURLClient {
2500
615
  * {@link ProtectedResource.createPortal}:
2501
616
  *
2502
617
  * ```ts
2503
- * const resource = await client.protectUrl("https://internal.example.com/dashboard");
618
+ * const resource = await client.protectUrl("https://internal.example.com/dashboard", {
619
+ * name: "Team dashboard",
620
+ * });
2504
621
  * const portal = await resource.createPortal({ validFor: "5m" });
2505
622
  * share(portal.link);
2506
623
  * ```
@@ -2511,13 +628,15 @@ class QURLClient {
2511
628
  * {@link createResource}.
2512
629
  */
2513
630
  async protectUrl(targetUrl, opts = {}, requestOptions) {
2514
- requirePortalTargetUrl(targetUrl);
2515
- requireObjectInput(opts, "protectUrl");
2516
- requireNoUnknownFields(opts, PROTECT_URL_OPTION_KEYS, "protectUrl");
631
+ (0, client_validation_js_1.requirePortalTargetUrl)(targetUrl);
632
+ (0, client_validation_js_1.requireObjectInput)(opts, "protectUrl");
633
+ (0, client_validation_js_1.requireNoUnknownFields)(opts, client_input_fields_js_1.PROTECT_URL_OPTION_KEYS, "protectUrl");
2517
634
  // camelCase options → wire fields; null is treated as omitted, via the
2518
635
  // SDK-wide normalizePatchFields convention.
2519
- const normalized = normalizePatchFields(opts, PROTECT_URL_OPTION_KEYS);
636
+ const normalized = (0, client_validation_js_1.normalizePatchFields)(opts, client_input_fields_js_1.PROTECT_URL_OPTION_KEYS);
2520
637
  const body = { target_url: targetUrl };
638
+ if (normalized.name !== undefined)
639
+ body.name = normalized.name;
2521
640
  if (normalized.private !== undefined)
2522
641
  body.private = normalized.private;
2523
642
  if (normalized.consumerAccess !== undefined) {
@@ -2532,20 +651,20 @@ class QURLClient {
2532
651
  body.custom_domain = normalized.customDomain;
2533
652
  if (normalized.alias !== undefined)
2534
653
  body.alias = normalized.alias;
2535
- validateResourceWriteFields(body, { requireUrlTarget: true });
654
+ (0, client_resources_js_1.validateResourceWriteFields)(body, { requireUrlTarget: true });
2536
655
  const response = await this.rawRequest("POST", "/v1/resources", body, {
2537
656
  requestOptions,
2538
657
  });
2539
- if (response.__http_status === 204)
2540
- throw unexpectedResponseError("protectUrl: expected a resource response body");
2541
- const resource = response.data;
2542
- await verifyResourceIdentity(resource, "protectUrl");
658
+ if (response.status === 204)
659
+ throw (0, client_errors_js_1.unexpectedResponseError)("protectUrl: expected a resource response body");
660
+ const resource = withEnvelopeMetadata(response.envelope.data, response.envelope.meta);
661
+ await (0, client_connectors_js_1.verifyResourceIdentity)(resource, "protectUrl");
2543
662
  if (normalized.consumerAccess &&
2544
663
  (resource.private !== true ||
2545
- !sameConsumerPolicy(resource.consumer_access, normalized.consumerAccess, response.meta?.found_existing === true))) {
2546
- throw unexpectedResponseError("protectUrl: private policy was not confirmed");
664
+ !(0, client_resources_js_1.sameConsumerPolicy)(resource.consumer_access, normalized.consumerAccess, response.envelope.meta?.found_existing === true))) {
665
+ throw (0, client_errors_js_1.unexpectedResponseError)("protectUrl: private policy was not confirmed");
2547
666
  }
2548
- return new ProtectedResource(this, resource.crid,
667
+ return new client_portals_js_1.ProtectedResource(this, resource.crid,
2549
668
  // Some resource types redact target_url in responses; fall back to
2550
669
  // the caller-supplied URL so the handle stays useful.
2551
670
  resource.target_url || targetUrl, resource);
@@ -2566,20 +685,31 @@ class QURLClient {
2566
685
  * need the full resource details.
2567
686
  */
2568
687
  resourceByCrid(id) {
2569
- requireResourceCrid(id, "resourceByCrid");
2570
- return new ProtectedResource(this, id);
688
+ (0, client_paths_js_1.requireResourceCrid)(id, "resourceByCrid");
689
+ return new client_portals_js_1.ProtectedResource(this, id);
2571
690
  }
2572
- /** Find or create the active qURL Connector resource for an immutable slug. */
2573
- async ensureConnectorResource(slug, requestOptions, consumerAccess) {
2574
- requireConnectorSlug(slug, "ensureConnectorResource");
691
+ /**
692
+ * Find or create the active qURL Connector resource for an immutable slug.
693
+ *
694
+ * `name` is the public name recipients see. The service requires it when the
695
+ * slug is new (`400 name_required` otherwise) and ignores it on reuse.
696
+ *
697
+ * @param slug - Immutable owner-scoped Connector slug.
698
+ * @param requestOptions - Per-call request options.
699
+ * @param consumerAccess - Private consumer policy for a new resource.
700
+ * @param name - Public resource name; required when the slug is new.
701
+ */
702
+ async ensureConnectorResource(slug, requestOptions, consumerAccess, name) {
703
+ (0, client_connectors_js_1.requireConnectorSlug)(slug, "ensureConnectorResource");
704
+ (0, client_validation_js_1.requireResourceNameIfPresent)(name);
2575
705
  if (consumerAccess !== undefined) {
2576
- validateResourceWriteFields({ consumer_access: consumerAccess, private: true });
706
+ (0, client_resources_js_1.validateResourceWriteFields)({ consumer_access: consumerAccess, private: true });
2577
707
  if (consumerAccess.version !== 1)
2578
- throw clientValidationError("consumer_access: creation requires version one");
708
+ throw (0, client_errors_js_1.clientValidationError)("consumer_access: creation requires version one");
2579
709
  }
2580
710
  // Response validation imports the producer's P-256 resource key. Preflight
2581
711
  // before POST so a missing runtime capability cannot orphan a committed row.
2582
- requireConnectorSubtleCrypto("ensureConnectorResource");
712
+ (0, client_connectors_js_1.requireConnectorSubtleCrypto)("ensureConnectorResource");
2583
713
  // Keep invalid options in the known pre-dispatch error arm. rawRequest
2584
714
  // validates again when building the request, which protects future callers
2585
715
  // that do not have this mutation-specific outcome contract.
@@ -2591,67 +721,67 @@ class QURLClient {
2591
721
  type: "tunnel",
2592
722
  slug,
2593
723
  find_or_create: true,
724
+ ...(name !== undefined ? { name } : {}),
2594
725
  ...(consumerAccess ? { private: true, consumer_access: consumerAccess } : {}),
2595
726
  }, { requestOptions, retry: false, generateIdempotencyKey: false });
2596
727
  // The find-or-create endpoint returns 201 for both newly-created
2597
728
  // and found-existing rows; meta.found_existing distinguishes the arms.
2598
- if (response.__http_status !== 201) {
2599
- throw unexpectedResponseError(`ensureConnectorResource: expected HTTP 201, got ${response.__http_status ?? "unknown"}`);
729
+ if (response.status !== 201) {
730
+ throw (0, client_errors_js_1.unexpectedResponseError)(`ensureConnectorResource: expected HTTP 201, got ${response.status ?? "unknown"}`);
2600
731
  }
2601
732
  if (consumerAccess &&
2602
- (response.data.private !== true ||
2603
- !sameConsumerPolicy(response.data.consumer_access, consumerAccess, response.meta?.found_existing === true))) {
2604
- throw unexpectedResponseError("ensureConnectorResource: private policy was not confirmed");
733
+ (response.envelope.data.private !== true ||
734
+ !(0, client_resources_js_1.sameConsumerPolicy)(response.envelope.data.consumer_access, consumerAccess, response.envelope.meta?.found_existing === true))) {
735
+ throw (0, client_errors_js_1.unexpectedResponseError)("ensureConnectorResource: private policy was not confirmed");
2605
736
  }
2606
- resource = await parseConnectorResource(this, response.data, "ensureConnectorResource", {
737
+ resource = await (0, client_connectors_js_1.parseConnectorResource)(this, response.envelope.data, "ensureConnectorResource", {
2607
738
  slug,
2608
739
  });
2609
740
  }
2610
741
  catch (error) {
2611
- classifyConnectorMutationFailure("ensure", error);
742
+ (0, client_connectors_js_1.classifyConnectorMutationFailure)("ensure", error);
2612
743
  }
2613
- if (response.meta !== undefined &&
2614
- response.meta !== null &&
2615
- (typeof response.meta !== "object" || Array.isArray(response.meta))) {
2616
- classifyConnectorMutationFailure("ensure", unexpectedResponseError("ensureConnectorResource: response has invalid meta"));
744
+ if (response.envelope.meta !== undefined &&
745
+ response.envelope.meta !== null &&
746
+ (typeof response.envelope.meta !== "object" || Array.isArray(response.envelope.meta))) {
747
+ (0, client_connectors_js_1.classifyConnectorMutationFailure)("ensure", (0, client_errors_js_1.unexpectedResponseError)("ensureConnectorResource: response has invalid meta"));
2617
748
  }
2618
- const foundExisting = response.meta
2619
- ?.found_existing;
749
+ const foundExisting = response.envelope.meta?.found_existing;
2620
750
  if (foundExisting === undefined || foundExisting === null) {
2621
- throw unexpectedResponseError("ensureConnectorResource: response is missing meta.found_existing; the validated resource exists, so reconcile by slug before retrying");
751
+ throw (0, client_errors_js_1.unexpectedResponseError)("ensureConnectorResource: response is missing meta.found_existing; the validated resource exists, so reconcile by slug before retrying");
2622
752
  }
2623
753
  if (typeof foundExisting !== "boolean") {
2624
- classifyConnectorMutationFailure("ensure", unexpectedResponseError("ensureConnectorResource: response has invalid meta.found_existing"));
754
+ (0, client_connectors_js_1.classifyConnectorMutationFailure)("ensure", (0, client_errors_js_1.unexpectedResponseError)("ensureConnectorResource: response has invalid meta.found_existing"));
2625
755
  }
2626
- return { resource, foundExisting };
756
+ return withEnvelopeMetadata({ resource: withEnvelopeMetadata(resource, response.envelope.meta), foundExisting }, response.envelope.meta);
2627
757
  }
2628
758
  /** Fetch a qURL Connector resource by its CRID. */
2629
759
  async getConnectorResource(crid) {
2630
- requireResourceCrid(crid, "getConnectorResource");
2631
- requireConnectorSubtleCrypto("getConnectorResource");
2632
- const { data, __http_status } = await this.rawRequest("GET", `/v1/resources/${encodeURIComponent(crid)}`, undefined, { retry: false });
2633
- if (__http_status !== 200) {
2634
- throw unexpectedResponseError(`getConnectorResource: expected HTTP 200, got ${__http_status ?? "unknown"}`);
760
+ (0, client_paths_js_1.requireResourceCrid)(crid, "getConnectorResource");
761
+ (0, client_connectors_js_1.requireConnectorSubtleCrypto)("getConnectorResource");
762
+ const { envelope: { data, meta }, status: httpStatus, } = await this.rawRequest("GET", `/v1/resources/${encodeURIComponent(crid)}`, undefined, { retry: false });
763
+ if (httpStatus !== 200) {
764
+ throw (0, client_errors_js_1.unexpectedResponseError)(`getConnectorResource: expected HTTP 200, got ${httpStatus ?? "unknown"}`);
2635
765
  }
2636
- return parseConnectorResource(this, data?.resource, "getConnectorResource", {
766
+ return withEnvelopeMetadata(await (0, client_connectors_js_1.parseConnectorResource)(this, data?.resource, "getConnectorResource", {
2637
767
  crid,
2638
768
  revokedIsLifecycleError: true,
2639
- });
769
+ }), meta);
2640
770
  }
2641
771
  /** Fetch the single active qURL Connector resource for an immutable slug. */
2642
772
  async getConnectorResourceBySlug(slug) {
2643
- requireConnectorSlug(slug, "getConnectorResourceBySlug");
773
+ (0, client_connectors_js_1.requireConnectorSlug)(slug, "getConnectorResourceBySlug");
2644
774
  // Parsing validates the returned P-256 resource key; avoid dispatching a
2645
775
  // read that this runtime cannot safely consume.
2646
- requireConnectorSubtleCrypto("getConnectorResourceBySlug");
776
+ (0, client_connectors_js_1.requireConnectorSubtleCrypto)("getConnectorResourceBySlug");
2647
777
  // The slug point lookup is intrinsically active-only. It also
2648
778
  // rejects combining `slug` with `status`, so this query must stay slug-only.
2649
- const { data, __http_status } = await this.rawRequest("GET", `/v1/resources?slug=${encodeURIComponent(slug)}`, undefined, { retry: false });
2650
- if (__http_status !== 200) {
2651
- throw unexpectedResponseError(`getConnectorResourceBySlug: expected HTTP 200, got ${__http_status ?? "unknown"}`);
779
+ const { envelope: { data, meta }, status: httpStatus, } = await this.rawRequest("GET", `/v1/resources?slug=${encodeURIComponent(slug)}`, undefined, { retry: false });
780
+ if (httpStatus !== 200) {
781
+ throw (0, client_errors_js_1.unexpectedResponseError)(`getConnectorResourceBySlug: expected HTTP 200, got ${httpStatus ?? "unknown"}`);
2652
782
  }
2653
783
  if (!Array.isArray(data)) {
2654
- throw unexpectedResponseError("getConnectorResourceBySlug: response has missing or invalid data");
784
+ throw (0, client_errors_js_1.unexpectedResponseError)("getConnectorResourceBySlug: response has missing or invalid data");
2655
785
  }
2656
786
  if (data.length === 0) {
2657
787
  throw new errors_js_1.NotFoundError({
@@ -2669,7 +799,7 @@ class QURLClient {
2669
799
  detail: `getConnectorResourceBySlug: expected one active resource, got ${data.length}`,
2670
800
  });
2671
801
  }
2672
- return parseConnectorResource(this, data[0], "getConnectorResourceBySlug", { slug });
802
+ return withEnvelopeMetadata(await (0, client_connectors_js_1.parseConnectorResource)(this, data[0], "getConnectorResourceBySlug", { slug }), meta);
2673
803
  }
2674
804
  /**
2675
805
  * Revoke a qURL Connector resource by CRID.
@@ -2678,12 +808,12 @@ class QURLClient {
2678
808
  * outcome-unknown failure, reconcile by ID before issuing a deliberate retry.
2679
809
  */
2680
810
  async deleteConnectorResource(crid) {
2681
- requireResourceCrid(crid, "deleteConnectorResource");
811
+ (0, client_paths_js_1.requireResourceCrid)(crid, "deleteConnectorResource");
2682
812
  try {
2683
813
  await this.requestNoContent(`/v1/resources/${encodeURIComponent(crid)}`);
2684
814
  }
2685
815
  catch (error) {
2686
- classifyConnectorMutationFailure("delete", error);
816
+ (0, client_connectors_js_1.classifyConnectorMutationFailure)("delete", error);
2687
817
  }
2688
818
  }
2689
819
  /**
@@ -2708,19 +838,19 @@ class QURLClient {
2708
838
  if (typeof resource === "string") {
2709
839
  resourceId = resource;
2710
840
  }
2711
- else if (resource instanceof ProtectedResource) {
2712
- if (!ProtectedResource.isBoundTo(resource, this)) {
2713
- throw clientValidationError("createPortal: resource is bound to a different client");
841
+ else if (resource instanceof client_portals_js_1.ProtectedResource) {
842
+ if (!client_portals_js_1.ProtectedResource.isBoundTo(resource, this)) {
843
+ throw (0, client_errors_js_1.clientValidationError)("createPortal: resource is bound to a different client");
2714
844
  }
2715
845
  resourceId = resource.crid;
2716
846
  }
2717
847
  else {
2718
- throw clientValidationError(`createPortal: resource must be a ProtectedResource handle or a CRID string (got ${describeShape(resource)})`);
848
+ throw (0, client_errors_js_1.clientValidationError)(`createPortal: resource must be a ProtectedResource handle or a CRID string (got ${(0, client_validation_js_1.describeShape)(resource)})`);
2719
849
  }
2720
- requireResourceCrid(resourceId, "createPortal");
2721
- const body = buildCreatePortalBody(opts, "createPortal");
850
+ (0, client_paths_js_1.requireResourceCrid)(resourceId, "createPortal");
851
+ const body = (0, client_portals_js_1.buildCreatePortalBody)(opts, "createPortal");
2722
852
  const data = await this.request("POST", `/v1/resources/${encodeURIComponent(resourceId)}/qurls`, body, requestOptions);
2723
- return parsePortal(data, "createPortal", resourceId);
853
+ return (0, client_portals_js_1.parsePortal)(data, "createPortal", resourceId, body.target_path);
2724
854
  }
2725
855
  /**
2726
856
  * Protect `targetUrl` and mint a portal in one API call.
@@ -2736,17 +866,17 @@ class QURLClient {
2736
866
  * equivalent: {@link create}.
2737
867
  */
2738
868
  async createPortalForUrl(targetUrl, opts = {}, requestOptions) {
2739
- requirePortalTargetUrl(targetUrl);
869
+ (0, client_validation_js_1.requirePortalTargetUrl)(targetUrl);
2740
870
  const body = {
2741
871
  target_url: targetUrl,
2742
- ...buildCreatePortalBody(opts, "createPortalForUrl"),
872
+ ...(0, client_portals_js_1.buildCreatePortalBody)(opts, "createPortalForUrl"),
2743
873
  };
2744
874
  const data = await this.request("POST", "/v1/qurls", body, requestOptions);
2745
- const portal = await parsePortal(data, "createPortalForUrl");
2746
- return {
875
+ const portal = await (0, client_portals_js_1.parsePortal)(data, "createPortalForUrl");
876
+ return (0, response_metadata_js_1.copyResponseMetadata)(data, {
2747
877
  portal,
2748
- resource: new ProtectedResource(this, portal.crid, targetUrl),
2749
- };
878
+ resource: (0, response_metadata_js_1.copyResponseMetadata)(data, new client_portals_js_1.ProtectedResource(this, portal.crid, targetUrl)),
879
+ });
2750
880
  }
2751
881
  /**
2752
882
  * Open a qURL link programmatically and return the reachable resource.
@@ -2771,16 +901,16 @@ class QURLClient {
2771
901
  * credentials.
2772
902
  */
2773
903
  async enterPortal(qurlLink, requestOptions) {
2774
- const token = extractAccessToken(qurlLink);
904
+ const token = (0, client_portals_js_1.extractAccessToken)(qurlLink);
2775
905
  const resolved = await this.resolve(token, requestOptions);
2776
906
  if (typeof resolved?.target_url !== "string" || resolved.target_url === "") {
2777
- throw unexpectedResponseError("enterPortal: access was granted but the API returned no resource URL");
907
+ throw (0, client_errors_js_1.unexpectedResponseError)("enterPortal: access was granted but the API returned no resource URL");
2778
908
  }
2779
- return {
909
+ return (0, response_metadata_js_1.copyResponseMetadata)(resolved, {
2780
910
  resourceUrl: resolved.target_url,
2781
911
  openSeconds: resolved.access_grant?.expires_in ?? 0,
2782
912
  resourcePublicKey: resolved.resource_id,
2783
- };
913
+ });
2784
914
  }
2785
915
  // --- Public API ---
2786
916
  /**
@@ -2790,7 +920,7 @@ class QURLClient {
2790
920
  * returns a {@link Portal} plus a reusable resource handle).
2791
921
  */
2792
922
  async create(input, options) {
2793
- validateCreateInput(input);
923
+ (0, client_resources_js_1.validateCreateInput)(input);
2794
924
  return this.request("POST", "/v1/qurls", input, options);
2795
925
  }
2796
926
  /**
@@ -2804,6 +934,8 @@ class QURLClient {
2804
934
  * populated `BatchCreateOutput` body in this case; whitelisted via
2805
935
  * `passthroughStatuses` so the structured per-item errors aren't
2806
936
  * swallowed by the generic error path.
937
+ * HTTP 400 is the documented all-item validation failure. HTTP 422 is
938
+ * not a batch passthrough status and follows normal error handling.
2807
939
  *
2808
940
  * Callers that only catch thrown errors will **silently miss partial
2809
941
  * failures**. Always branch on `result.failed > 0` before treating the
@@ -2851,17 +983,17 @@ class QURLClient {
2851
983
  // for non-conforming inputs. Without this guard, `batchCreate({} as
2852
984
  // any)` would throw a raw TypeError on `.length`. Match the pattern.
2853
985
  if (!input || !Array.isArray(input.items)) {
2854
- throw clientValidationError("batchCreate: items must be an array");
986
+ throw (0, client_errors_js_1.clientValidationError)("batchCreate: items must be an array");
2855
987
  }
2856
988
  // Size checks are fail-fast (binary, no useful aggregation), per-item
2857
989
  // validation below is collect-all (multiple bad items aggregate into
2858
990
  // one throw). The asymmetry is deliberate: there's nothing to combine
2859
991
  // for empty input or oversized batches — the size is wrong or it isn't.
2860
992
  if (input.items.length === 0) {
2861
- throw clientValidationError("batchCreate requires at least 1 item");
993
+ throw (0, client_errors_js_1.clientValidationError)("batchCreate requires at least 1 item");
2862
994
  }
2863
- if (input.items.length > MAX_BATCH_ITEMS) {
2864
- throw clientValidationError(`batchCreate accepts at most ${MAX_BATCH_ITEMS} items, got ${input.items.length}`);
995
+ if (input.items.length > client_limits_js_1.MAX_BATCH_ITEMS) {
996
+ throw (0, client_errors_js_1.clientValidationError)(`batchCreate accepts at most ${client_limits_js_1.MAX_BATCH_ITEMS} items, got ${input.items.length}`);
2865
997
  }
2866
998
  // Collect ALL per-item validation errors in one pass (not fail-fast)
2867
999
  // so callers see every problem without fix-re-run-repeat. Kept inline
@@ -2870,7 +1002,7 @@ class QURLClient {
2870
1002
  const perItemErrors = [];
2871
1003
  for (let i = 0; i < input.items.length; i++) {
2872
1004
  try {
2873
- validateCreateInput(input.items[i]);
1005
+ (0, client_resources_js_1.validateCreateInput)(input.items[i]);
2874
1006
  }
2875
1007
  catch (err) {
2876
1008
  if (err instanceof errors_js_1.ValidationError) {
@@ -2887,18 +1019,18 @@ class QURLClient {
2887
1019
  // Without this asymmetry, the same `; ` at both levels makes
2888
1020
  // it ambiguous which separator is "next item" vs "next field
2889
1021
  // within item". Mirrors the JSDoc example shape.
2890
- throw clientValidationError(`batchCreate: ${perItemErrors.join(" | ")}`);
1022
+ throw (0, client_errors_js_1.clientValidationError)(`batchCreate: ${perItemErrors.join(" | ")}`);
2891
1023
  }
2892
1024
  // 400 carries per-item errors (see rawRequest JSDoc). Use rawRequest
2893
1025
  // directly (not `this.request`) so we can read `meta.request_id`
2894
1026
  // from the envelope and propagate it into the returned
2895
1027
  // BatchCreateOutput — consumers filing support tickets on partial
2896
1028
  // or total batch failures need the correlation ID.
2897
- const envelope = await this.rawRequest("POST", "/v1/qurls/batch", input, {
1029
+ const response = await this.rawRequest("POST", "/v1/qurls/batch", input, {
2898
1030
  passthroughStatuses: BATCH_PASSTHROUGH_STATUSES,
2899
1031
  requestOptions: options,
2900
1032
  });
2901
- const result = this.validateBatchCreateResponse(envelope, input.items.length);
1033
+ const result = this.validateBatchCreateResponse(response, input.items.length);
2902
1034
  // Attach the server request_id from the envelope meta without
2903
1035
  // mutating `result` (which is the parsed JSON body). Mirrors the
2904
1036
  // non-mutating style used elsewhere (e.g. `mapQurlsField`). In the
@@ -2915,7 +1047,7 @@ class QURLClient {
2915
1047
  // as the source, and `meta.request_id` is the canonical envelope
2916
1048
  // field. The data-side variant would be a wire-format duplication
2917
1049
  // we don't want to silently honor.
2918
- const requestId = envelope.meta?.request_id;
1050
+ const requestId = response.envelope.meta?.request_id;
2919
1051
  // Both-fields-present and data-only-present are both worth surfacing
2920
1052
  // via debug — the former because meta wins silently, the latter
2921
1053
  // because it suggests an envelope-shape transition. Data-side value
@@ -2937,7 +1069,7 @@ class QURLClient {
2937
1069
  });
2938
1070
  }
2939
1071
  }
2940
- return requestId !== undefined ? { ...result, request_id: requestId } : result;
1072
+ return withEnvelopeMetadata(requestId !== undefined ? { ...result, request_id: requestId } : result, response.envelope.meta);
2941
1073
  }
2942
1074
  /**
2943
1075
  * Start one durable delegated qURL batch. The SDK sends this mutation once.
@@ -2945,12 +1077,12 @@ class QURLClient {
2945
1077
  * semantic body.
2946
1078
  */
2947
1079
  async createDelegatedQurlBatch(input, options) {
2948
- validateCreateDelegatedQurlBatchInput(input);
2949
- validateDelegatedBatchCreateOptions(options);
1080
+ (0, client_delegated_js_1.validateCreateDelegatedQurlBatchInput)(input);
1081
+ (0, client_delegated_js_1.validateDelegatedBatchCreateOptions)(options);
2950
1082
  let dispatched = false;
2951
1083
  let acceptedBatchId;
2952
1084
  try {
2953
- const envelope = await this.rawRequest("POST", "/v1/delegated-qurl-batches", input, {
1085
+ const response = await this.rawRequest("POST", "/v1/delegated-qurl-batches", input, {
2954
1086
  requestOptions: options,
2955
1087
  retry: false,
2956
1088
  generateIdempotencyKey: false,
@@ -2959,25 +1091,25 @@ class QURLClient {
2959
1091
  dispatched = true;
2960
1092
  },
2961
1093
  });
2962
- const status = envelope.__http_status ?? 0;
1094
+ const status = response.status ?? 0;
2963
1095
  if (status !== 202) {
2964
- throw delegatedResponseError(status, "create returned an unexpected success status");
1096
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "create returned an unexpected success status");
2965
1097
  }
2966
- const rawData = envelope.data;
1098
+ const rawData = response.envelope.data;
2967
1099
  if (typeof rawData === "object" && rawData !== null && !Array.isArray(rawData)) {
2968
1100
  const candidate = rawData.batch_id;
2969
- if (typeof candidate === "string" && DELEGATED_BATCH_ID_PATTERN.test(candidate)) {
1101
+ if (typeof candidate === "string" && client_delegated_js_1.DELEGATED_BATCH_ID_PATTERN.test(candidate)) {
2970
1102
  acceptedBatchId = candidate;
2971
1103
  }
2972
1104
  }
2973
- const { data, requestId } = delegatedEnvelope(envelope, status);
2974
- const base = delegatedBatchBase(data, status, requestId);
1105
+ const { data, requestId } = (0, client_delegated_js_1.delegatedEnvelope)(response, status);
1106
+ const base = (0, client_delegated_js_1.delegatedBatchBase)(data, status, requestId);
2975
1107
  if (data.status !== "queued" || base.itemCount !== input.grants.length) {
2976
- throw delegatedResponseError(status, "acceptance data is inconsistent", requestId);
1108
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "acceptance data is inconsistent", requestId);
2977
1109
  }
2978
- const responseHeaders = delegatedResponseHeaders(envelope, status, requestId);
2979
- const location = validateDelegatedLocation(requiredDelegatedHeader(envelope, "Location", status, requestId), this.baseUrl, base.batchId, status, requestId);
2980
- return {
1110
+ const responseHeaders = (0, client_delegated_js_1.delegatedResponseHeaders)(response, status, requestId);
1111
+ const location = (0, client_delegated_js_1.validateDelegatedLocation)((0, client_delegated_js_1.requiredDelegatedHeader)(response, "Location", status, requestId), this.baseUrl, base.batchId, status, requestId);
1112
+ return withEnvelopeMetadata({
2981
1113
  http_status: 202,
2982
1114
  batch_id: base.batchId,
2983
1115
  status: "queued",
@@ -2987,12 +1119,12 @@ class QURLClient {
2987
1119
  retry_after: responseHeaders.retryAfter,
2988
1120
  location,
2989
1121
  request_id: requestId,
2990
- };
1122
+ }, response.envelope.meta);
2991
1123
  }
2992
1124
  catch (error) {
2993
1125
  if (!dispatched)
2994
1126
  throw error;
2995
- classifyDelegatedBatchCreateFailure(error, acceptedBatchId);
1127
+ (0, client_delegated_js_1.classifyDelegatedBatchCreateFailure)(error, acceptedBatchId);
2996
1128
  }
2997
1129
  }
2998
1130
  /**
@@ -3000,45 +1132,45 @@ class QURLClient {
3000
1132
  * callers retain control of the poll count and total deadline.
3001
1133
  */
3002
1134
  async getDelegatedQurlBatch(batchId, options) {
3003
- if (typeof batchId !== "string" || !DELEGATED_BATCH_ID_PATTERN.test(batchId)) {
3004
- throw clientValidationError("getDelegatedQurlBatch: batchId must match dqb_ plus 22 URL-safe characters");
1135
+ if (typeof batchId !== "string" || !client_delegated_js_1.DELEGATED_BATCH_ID_PATTERN.test(batchId)) {
1136
+ throw (0, client_errors_js_1.clientValidationError)("getDelegatedQurlBatch: batchId must match dqb_ plus 22 URL-safe characters");
3005
1137
  }
3006
- validateGetDelegatedQurlBatchOptions(options);
3007
- const envelope = await this.rawRequest("GET", `/v1/delegated-qurl-batches/${encodeURIComponent(batchId)}`, undefined, {
1138
+ (0, client_delegated_js_1.validateGetDelegatedQurlBatchOptions)(options);
1139
+ const response = await this.rawRequest("GET", `/v1/delegated-qurl-batches/${encodeURIComponent(batchId)}`, undefined, {
3008
1140
  passthroughStatuses: DELEGATED_BATCH_PASSTHROUGH_STATUSES,
3009
1141
  allowEmptyPassthroughBody: true,
3010
1142
  ifNoneMatch: options?.etag,
3011
1143
  retry: false,
3012
1144
  captureResponseHeaders: true,
3013
1145
  });
3014
- const status = envelope.__http_status ?? 0;
1146
+ const status = response.status ?? 0;
3015
1147
  if (status === 304) {
3016
- if (envelope.__http_body_empty !== true || options?.etag === undefined) {
3017
- throw delegatedResponseError(status, "304 response is inconsistent");
1148
+ if (response.bodyEmpty !== true || options?.etag === undefined) {
1149
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "304 response is inconsistent");
3018
1150
  }
3019
- const responseHeaders = delegatedResponseHeaders(envelope, status);
1151
+ const responseHeaders = (0, client_delegated_js_1.delegatedResponseHeaders)(response, status);
3020
1152
  if (responseHeaders.etag !== undefined && responseHeaders.etag !== options.etag) {
3021
- throw delegatedResponseError(status, "304 response changed the ETag");
1153
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "304 response changed the ETag");
3022
1154
  }
3023
- return {
1155
+ return withEnvelopeMetadata({
3024
1156
  http_status: 304,
3025
1157
  etag: responseHeaders.etag ?? options.etag,
3026
1158
  ...(responseHeaders.retryAfter === undefined
3027
1159
  ? {}
3028
1160
  : { retry_after: responseHeaders.retryAfter }),
3029
- };
1161
+ }, response.envelope.meta);
3030
1162
  }
3031
1163
  if (status !== 200 && status !== 202) {
3032
- throw delegatedResponseError(status, "read returned an unexpected success status");
1164
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "read returned an unexpected success status");
3033
1165
  }
3034
- const { data, requestId } = delegatedEnvelope(envelope, status);
3035
- const base = delegatedBatchBase(data, status, requestId, batchId);
1166
+ const { data, requestId } = (0, client_delegated_js_1.delegatedEnvelope)(response, status);
1167
+ const base = (0, client_delegated_js_1.delegatedBatchBase)(data, status, requestId, batchId);
3036
1168
  if (status === 202) {
3037
1169
  if (data.status !== "queued" && data.status !== "running") {
3038
- throw delegatedResponseError(status, "pending data has a terminal status", requestId);
1170
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "pending data has a terminal status", requestId);
3039
1171
  }
3040
- const responseHeaders = delegatedResponseHeaders(envelope, status, requestId);
3041
- return {
1172
+ const responseHeaders = (0, client_delegated_js_1.delegatedResponseHeaders)(response, status, requestId);
1173
+ return withEnvelopeMetadata({
3042
1174
  http_status: 202,
3043
1175
  batch_id: base.batchId,
3044
1176
  status: data.status,
@@ -3047,19 +1179,19 @@ class QURLClient {
3047
1179
  etag: responseHeaders.etag,
3048
1180
  retry_after: responseHeaders.retryAfter,
3049
1181
  request_id: requestId,
3050
- };
1182
+ }, response.envelope.meta);
3051
1183
  }
3052
1184
  if (data.status !== "succeeded" &&
3053
1185
  data.status !== "partially_failed" &&
3054
1186
  data.status !== "failed") {
3055
- throw delegatedResponseError(status, "terminal data has a non-terminal status", requestId);
1187
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "terminal data has a non-terminal status", requestId);
3056
1188
  }
3057
1189
  let results;
3058
1190
  try {
3059
- if (!isUtcTimestamp(data.completed_at)) {
3060
- throw delegatedResponseError(status, "terminal data has invalid completed_at", requestId);
1191
+ if (!(0, client_delegated_js_1.isUtcTimestamp)(data.completed_at)) {
1192
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "terminal data has invalid completed_at", requestId);
3061
1193
  }
3062
- results = validateDelegatedBatchResults(data.results, base.itemCount, data.status, status, requestId);
1194
+ results = (0, client_delegated_js_1.validateDelegatedBatchResults)(data.results, base.itemCount, data.status, status, requestId);
3063
1195
  }
3064
1196
  catch (error) {
3065
1197
  const partialQurlIds = Array.isArray(data.results)
@@ -3068,7 +1200,7 @@ class QURLClient {
3068
1200
  typeof item === "object" &&
3069
1201
  item.status === "succeeded" &&
3070
1202
  typeof item.qurl?.qurl_id === "string" &&
3071
- DELEGATED_QURL_ID_PATTERN.test(item.qurl.qurl_id)
1203
+ client_delegated_js_1.DELEGATED_QURL_ID_PATTERN.test(item.qurl.qurl_id)
3072
1204
  ? [item.qurl.qurl_id]
3073
1205
  : [])),
3074
1206
  ]
@@ -3083,17 +1215,17 @@ class QURLClient {
3083
1215
  // This is the caller's only copy of the bearer links. Do not discard a
3084
1216
  // valid terminal body because an intermediary removed a cache validator;
3085
1217
  // qurl-service owns the required no-store response policy.
3086
- const cacheControl = envelope.__http_headers?.get("Cache-Control") ?? null;
3087
- if (!hasDelegatedNoStore(cacheControl)) {
1218
+ const cacheControl = response.headers?.get("Cache-Control") ?? null;
1219
+ if (!(0, client_delegated_js_1.hasDelegatedNoStore)(cacheControl)) {
3088
1220
  this.log("delegated batch terminal response is missing private, no-store", {
3089
1221
  batch_id: base.batchId,
3090
1222
  });
3091
1223
  }
3092
- const etag = envelope.__http_headers?.get("ETag") ?? undefined;
3093
- const terminalEtag = etag !== undefined && etag.length <= MAX_DELEGATED_ETAG && STRONG_ETAG_PATTERN.test(etag)
1224
+ const etag = response.headers?.get("ETag") ?? undefined;
1225
+ const terminalEtag = etag !== undefined && etag.length <= client_limits_js_1.MAX_DELEGATED_ETAG && client_delegated_js_1.STRONG_ETAG_PATTERN.test(etag)
3094
1226
  ? etag
3095
1227
  : undefined;
3096
- return {
1228
+ return withEnvelopeMetadata({
3097
1229
  http_status: 200,
3098
1230
  batch_id: base.batchId,
3099
1231
  status: data.status,
@@ -3103,28 +1235,28 @@ class QURLClient {
3103
1235
  results,
3104
1236
  ...(terminalEtag === undefined ? {} : { etag: terminalEtag }),
3105
1237
  request_id: requestId,
3106
- };
1238
+ }, response.envelope.meta);
3107
1239
  }
3108
1240
  /** Read owner-visible metadata for one delegated qURL. */
3109
1241
  async getDelegatedQurl(qurlId) {
3110
- if (typeof qurlId !== "string" || !DELEGATED_QURL_ID_PATTERN.test(qurlId)) {
3111
- throw clientValidationError("getDelegatedQurl: qurlId must match q_ plus 11 lowercase hexadecimal characters");
1242
+ if (typeof qurlId !== "string" || !client_delegated_js_1.DELEGATED_QURL_ID_PATTERN.test(qurlId)) {
1243
+ throw (0, client_errors_js_1.clientValidationError)("getDelegatedQurl: qurlId must match q_ plus 11 lowercase hexadecimal characters");
3112
1244
  }
3113
- const envelope = await this.rawRequest("GET", `/v1/delegated-qurls/${encodeURIComponent(qurlId)}`);
3114
- const status = envelope.__http_status ?? 0;
1245
+ const response = await this.rawRequest("GET", `/v1/delegated-qurls/${encodeURIComponent(qurlId)}`);
1246
+ const status = response.status ?? 0;
3115
1247
  if (status !== 200) {
3116
- throw delegatedResponseError(status, "read returned an unexpected success status");
1248
+ throw (0, client_delegated_js_1.delegatedResponseError)(status, "read returned an unexpected success status");
3117
1249
  }
3118
- const { data, requestId } = delegatedEnvelope(envelope, status);
3119
- return validateDelegatedQurl(data, qurlId, status, requestId);
1250
+ const { data, requestId } = (0, client_delegated_js_1.delegatedEnvelope)(response, status);
1251
+ return withEnvelopeMetadata((0, client_delegated_js_1.validateDelegatedQurl)(data, qurlId, status, requestId), response.envelope.meta);
3120
1252
  }
3121
1253
  /**
3122
1254
  * Revoke one delegated qURL. DELETE is never retried automatically. A 503
3123
1255
  * `mutation_outcome_unknown` is safe to retry with the same qURL ID.
3124
1256
  */
3125
1257
  async deleteDelegatedQurl(qurlId) {
3126
- if (typeof qurlId !== "string" || !DELEGATED_QURL_ID_PATTERN.test(qurlId)) {
3127
- throw clientValidationError("deleteDelegatedQurl: qurlId must match q_ plus 11 lowercase hexadecimal characters");
1258
+ if (typeof qurlId !== "string" || !client_delegated_js_1.DELEGATED_QURL_ID_PATTERN.test(qurlId)) {
1259
+ throw (0, client_errors_js_1.clientValidationError)("deleteDelegatedQurl: qurlId must match q_ plus 11 lowercase hexadecimal characters");
3128
1260
  }
3129
1261
  await this.requestNoContent(`/v1/delegated-qurls/${encodeURIComponent(qurlId)}`);
3130
1262
  }
@@ -3136,7 +1268,7 @@ class QURLClient {
3136
1268
  * resource automatically.
3137
1269
  */
3138
1270
  async get(id) {
3139
- validatePathId(id, "get", "id", RESOURCE_OR_QURL_ID_PATH_OPTIONS);
1271
+ (0, client_paths_js_1.validatePathId)(id, "get", "id", client_paths_js_1.RESOURCE_OR_QURL_ID_PATH_OPTIONS);
3140
1272
  const raw = await this.request("GET", `/v1/qurls/${encodeURIComponent(id)}`);
3141
1273
  return this.mapQurlsField(raw);
3142
1274
  }
@@ -3153,8 +1285,8 @@ class QURLClient {
3153
1285
  * validation style used for `max_sessions`, `tag count`, URL length.
3154
1286
  */
3155
1287
  async list(input = {}) {
3156
- const path = appendQuery("/v1/qurls", input, LIST_PARAM_KEYS, "list");
3157
- const { data, meta } = await this.rawRequest("GET", path);
1288
+ const path = appendQuery("/v1/qurls", input, client_input_fields_js_1.LIST_PARAM_KEYS, "list");
1289
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", path);
3158
1290
  return pageFromMeta({
3159
1291
  // Defensive: map in case API includes nested tokens on list items in the future.
3160
1292
  qurls: (data ?? []).map((raw) => this.mapQurlsField(raw)),
@@ -3172,7 +1304,7 @@ class QURLClient {
3172
1304
  * iteration begins.
3173
1305
  */
3174
1306
  listAll(input = {}) {
3175
- validateListAllInput(input, LIST_PARAM_KEYS, "listAll");
1307
+ validateListAllInput(input, client_input_fields_js_1.LIST_PARAM_KEYS, "listAll");
3176
1308
  return this.paginateAll("listAll", input, (pageInput) => this.list(pageInput), (page) => page.qurls);
3177
1309
  }
3178
1310
  /**
@@ -3184,7 +1316,7 @@ class QURLClient {
3184
1316
  * @throws {ValidationError} If `id` is not a canonical CRID.
3185
1317
  */
3186
1318
  async delete(id) {
3187
- requireResourceCrid(id, "delete");
1319
+ (0, client_paths_js_1.requireResourceCrid)(id, "delete");
3188
1320
  await this.requestNoContent(`/v1/qurls/${encodeURIComponent(id)}`);
3189
1321
  }
3190
1322
  /**
@@ -3203,14 +1335,14 @@ class QURLClient {
3203
1335
  * enforced separately by the runtime check inside `update()`.
3204
1336
  */
3205
1337
  async extend(id, input, options) {
3206
- validatePathId(id, "extend", "id", RESOURCE_OR_QURL_ID_PATH_OPTIONS);
3207
- requireObjectInput(input, "extend");
1338
+ (0, client_paths_js_1.validatePathId)(id, "extend", "id", client_paths_js_1.RESOURCE_OR_QURL_ID_PATH_OPTIONS);
1339
+ (0, client_validation_js_1.requireObjectInput)(input, "extend");
3208
1340
  const { extend_by, expires_at } = input;
3209
1341
  if (extend_by === undefined && expires_at === undefined) {
3210
- throw clientValidationError("extend: exactly one of `extend_by` or `expires_at` is required");
1342
+ throw (0, client_errors_js_1.clientValidationError)("extend: exactly one of `extend_by` or `expires_at` is required");
3211
1343
  }
3212
1344
  if (extend_by !== undefined && expires_at !== undefined) {
3213
- throw clientValidationError("extend: `extend_by` and `expires_at` are mutually exclusive — provide exactly one");
1345
+ throw (0, client_errors_js_1.clientValidationError)("extend: `extend_by` and `expires_at` are mutually exclusive — provide exactly one");
3214
1346
  }
3215
1347
  return this.update(id, { extend_by, expires_at }, options);
3216
1348
  }
@@ -3222,25 +1354,25 @@ class QURLClient {
3222
1354
  * resource automatically.
3223
1355
  */
3224
1356
  async update(id, input, options) {
3225
- validatePathId(id, "update", "id", RESOURCE_OR_QURL_ID_PATH_OPTIONS);
3226
- requireObjectInput(input, "update");
3227
- requireNoUnknownFields(input, UPDATE_FIELD_KEYS, "update");
1357
+ (0, client_paths_js_1.validatePathId)(id, "update", "id", client_paths_js_1.RESOURCE_OR_QURL_ID_PATH_OPTIONS);
1358
+ (0, client_validation_js_1.requireObjectInput)(input, "update");
1359
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.UPDATE_FIELD_KEYS, "update");
3228
1360
  // Normalize null → undefined so untyped-JS callers passing
3229
1361
  // `{ tags: null }` don't leak null into the wire body or crash
3230
1362
  // downstream validators. Shallow copy keeps caller input untouched.
3231
- const normalized = normalizePatchFields(input, UPDATE_FIELD_KEYS);
1363
+ const normalized = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.UPDATE_FIELD_KEYS);
3232
1364
  // Timing fields have no clear-semantic (unlike `description: ""`).
3233
- requireNonEmptyIfPresent(normalized.extend_by, "extend_by");
3234
- requireNonEmptyIfPresent(normalized.expires_at, "expires_at");
1365
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(normalized.extend_by, "extend_by");
1366
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(normalized.expires_at, "expires_at");
3235
1367
  if (normalized.extend_by !== undefined && normalized.expires_at !== undefined) {
3236
- throw clientValidationError("update: `extend_by` and `expires_at` are mutually exclusive — provide at most one");
1368
+ throw (0, client_errors_js_1.clientValidationError)("update: `extend_by` and `expires_at` are mutually exclusive — provide at most one");
3237
1369
  }
3238
- requireAtLeastOneField(normalized, UPDATE_FIELD_KEYS, "update");
1370
+ (0, client_validation_js_1.requireAtLeastOneField)(normalized, client_input_fields_js_1.UPDATE_FIELD_KEYS, "update");
3239
1371
  // Per-field validators run after the empty-input guard so the
3240
1372
  // cheap binary check short-circuits first; order is correctness-neutral
3241
1373
  // (`requireMaxLength(undefined, …)` is a no-op).
3242
- requireMaxLength(normalized.description, "description", MAX_DESCRIPTION);
3243
- requireValidTags(normalized.tags);
1374
+ (0, client_validation_js_1.requireMaxLength)(normalized.description, "description", client_limits_js_1.MAX_DESCRIPTION);
1375
+ (0, client_validation_js_1.requireValidTags)(normalized.tags);
3244
1376
  const raw = await this.request("PATCH", `/v1/qurls/${encodeURIComponent(id)}`, normalized, options);
3245
1377
  return this.mapQurlsField(raw);
3246
1378
  }
@@ -3258,7 +1390,7 @@ class QURLClient {
3258
1390
  * server applies its 24h default expiration.
3259
1391
  */
3260
1392
  async mintLink(id, input, options) {
3261
- validatePathId(id, "mintLink", "id", RESOURCE_OR_QURL_ID_PATH_OPTIONS);
1393
+ (0, client_paths_js_1.validatePathId)(id, "mintLink", "id", client_paths_js_1.RESOURCE_OR_QURL_ID_PATH_OPTIONS);
3262
1394
  // Normalize null → omitted so untyped-JS callers passing
3263
1395
  // `{ expires_in: null, expires_at: "..." }` don't leak null into
3264
1396
  // the wire body via JSON.stringify and don't bypass the XOR check
@@ -3266,15 +1398,15 @@ class QURLClient {
3266
1398
  // pattern used by update(); keeps the two write surfaces symmetric.
3267
1399
  let normalized = input;
3268
1400
  if (input !== undefined) {
3269
- requireObjectInput(input, "mintLink");
3270
- requireNoUnknownFields(input, MINT_FIELD_KEYS, "mintLink");
1401
+ (0, client_validation_js_1.requireObjectInput)(input, "mintLink");
1402
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.MINT_FIELD_KEYS, "mintLink");
3271
1403
  // Iterate the explicit allowlist (matching update() / list())
3272
1404
  // so unknown keys from untyped-JS callers can't leak through
3273
1405
  // to the wire body. The compile-time `assertExhaustive` check
3274
1406
  // on MINT_FIELD_KEYS keeps this in sync with MintInput.
3275
1407
  const stripped = {};
3276
1408
  let hasAnyField = false;
3277
- for (const key of MINT_FIELD_KEYS) {
1409
+ for (const key of client_input_fields_js_1.MINT_FIELD_KEYS) {
3278
1410
  const value = input[key];
3279
1411
  if (value !== null && value !== undefined) {
3280
1412
  stripped[key] = value;
@@ -3287,16 +1419,16 @@ class QURLClient {
3287
1419
  normalized = hasAnyField ? stripped : undefined;
3288
1420
  }
3289
1421
  if (normalized !== undefined) {
3290
- requireNonEmptyIfPresent(normalized.expires_in, "expires_in");
3291
- requireNonEmptyIfPresent(normalized.expires_at, "expires_at");
1422
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(normalized.expires_in, "expires_in");
1423
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(normalized.expires_at, "expires_at");
3292
1424
  if (normalized.expires_in !== undefined && normalized.expires_at !== undefined) {
3293
- throw clientValidationError("mintLink: `expires_in` and `expires_at` are mutually exclusive — provide at most one");
1425
+ throw (0, client_errors_js_1.clientValidationError)("mintLink: `expires_in` and `expires_at` are mutually exclusive — provide at most one");
3294
1426
  }
3295
- requireMaxLength(normalized.label, "label", MAX_LABEL);
3296
- requireNonEmptyIfPresent(normalized.label, "label");
3297
- requireNonEmptyIfPresent(normalized.session_duration, "session_duration");
3298
- requireMaxSessionsInRange(normalized.max_sessions);
3299
- requireValidAccessPolicy(normalized.access_policy);
1427
+ (0, client_validation_js_1.requireMaxLength)(normalized.label, "label", client_limits_js_1.MAX_LABEL);
1428
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(normalized.label, "label");
1429
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(normalized.session_duration, "session_duration");
1430
+ (0, client_validation_js_1.requireMaxSessionsInRange)(normalized.max_sessions);
1431
+ (0, client_validation_js_1.requireValidAccessPolicy)(normalized.access_policy);
3300
1432
  // Duration *grammar* and access_policy contents are server-authoritative
3301
1433
  // (same rationale as validateCreateInput). Empty-string rejection happens
3302
1434
  // above; bound/unit checks happen server-side.
@@ -3319,29 +1451,47 @@ class QURLClient {
3319
1451
  let body;
3320
1452
  if (typeof input === "string") {
3321
1453
  if (input.length === 0) {
3322
- throw clientValidationError("resolve: access_token must be a non-empty string");
1454
+ throw (0, client_errors_js_1.clientValidationError)("resolve: access_token must be a non-empty string");
3323
1455
  }
3324
1456
  body = { access_token: input };
3325
1457
  }
3326
1458
  else if (typeof input === "object" && input !== null) {
3327
1459
  if (typeof input.access_token !== "string" || input.access_token.length === 0) {
3328
- throw clientValidationError("resolve: access_token must be a non-empty string");
1460
+ throw (0, client_errors_js_1.clientValidationError)("resolve: access_token must be a non-empty string");
3329
1461
  }
3330
1462
  // Allowlist-rebuild — symmetric with list()/update()/mintLink().
3331
1463
  body = { access_token: input.access_token };
3332
1464
  }
3333
1465
  else {
3334
- throw clientValidationError(`resolve: input must be a string or { access_token } object (got ${input === null ? "null" : typeof input})`);
1466
+ throw (0, client_errors_js_1.clientValidationError)(`resolve: input must be a string or { access_token } object (got ${input === null ? "null" : typeof input})`);
3335
1467
  }
3336
1468
  return this.request("POST", "/v1/resolve", body, options);
3337
1469
  }
1470
+ /** Get the caller's account and credential identity. */
1471
+ async getIdentity() {
1472
+ const identity = await this.request("GET", "/v1/me");
1473
+ if (!identity ||
1474
+ typeof identity !== "object" ||
1475
+ Array.isArray(identity) ||
1476
+ typeof identity.owner_id !== "string" ||
1477
+ !identity.owner_id ||
1478
+ typeof identity.auth_type !== "string" ||
1479
+ !identity.auth_type ||
1480
+ (identity.api_key !== undefined &&
1481
+ (!identity.api_key ||
1482
+ typeof identity.api_key !== "object" ||
1483
+ Array.isArray(identity.api_key)))) {
1484
+ throw (0, client_errors_js_1.unexpectedResponseError)("getIdentity: invalid caller identity response");
1485
+ }
1486
+ return identity;
1487
+ }
3338
1488
  /** Get quota and usage information. */
3339
1489
  async getQuota() {
3340
1490
  return this.request("GET", "/v1/quota");
3341
1491
  }
3342
1492
  /** List resources from the `/v1/resources` API. */
3343
1493
  async listResources(input = {}) {
3344
- const { data, meta } = await this.rawRequest("GET", appendQuery("/v1/resources", input, RESOURCE_LIST_PARAM_KEYS, "listResources"));
1494
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery("/v1/resources", input, client_input_fields_js_1.RESOURCE_LIST_PARAM_KEYS, "listResources"));
3345
1495
  return pageFromMeta({ resources: data ?? [] }, meta);
3346
1496
  }
3347
1497
  /**
@@ -3351,7 +1501,7 @@ class QURLClient {
3351
1501
  * iteration begins.
3352
1502
  */
3353
1503
  listAllResources(input = {}) {
3354
- validateListAllInput(input, RESOURCE_LIST_PARAM_KEYS, "listAllResources");
1504
+ validateListAllInput(input, client_input_fields_js_1.RESOURCE_LIST_PARAM_KEYS, "listAllResources");
3355
1505
  return this.paginateAll("listAllResources", input, (pageInput) => this.listResources(pageInput), (page) => page.resources);
3356
1506
  }
3357
1507
  /**
@@ -3361,10 +1511,10 @@ class QURLClient {
3361
1511
  * a portal-minting handle).
3362
1512
  */
3363
1513
  async createResource(input, options) {
3364
- requireObjectInput(input, "createResource");
3365
- requireNoUnknownFields(input, CREATE_RESOURCE_FIELD_KEYS, "createResource");
3366
- const normalized = normalizePatchFields(input, CREATE_RESOURCE_FIELD_KEYS);
3367
- validateResourceWriteFields(normalized, {
1514
+ (0, client_validation_js_1.requireObjectInput)(input, "createResource");
1515
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.CREATE_RESOURCE_FIELD_KEYS, "createResource");
1516
+ const normalized = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.CREATE_RESOURCE_FIELD_KEYS);
1517
+ (0, client_resources_js_1.validateResourceWriteFields)(normalized, {
3368
1518
  requireUrlTarget: true,
3369
1519
  validateFindOrCreate: true,
3370
1520
  });
@@ -3372,57 +1522,82 @@ class QURLClient {
3372
1522
  }
3373
1523
  /** Get one resource plus its bounded qURL preview. */
3374
1524
  async getResource(id) {
3375
- requireResourceCrid(id, "getResource");
1525
+ (0, client_paths_js_1.requireResourceCrid)(id, "getResource");
3376
1526
  return this.request("GET", `/v1/resources/${encodeURIComponent(id)}`);
3377
1527
  }
3378
1528
  /** Update resource metadata. */
3379
1529
  async updateResource(id, input, options) {
3380
- requireResourceCrid(id, "updateResource");
3381
- requireObjectInput(input, "updateResource");
3382
- requireNoUnknownFields(input, UPDATE_RESOURCE_FIELD_KEYS, "updateResource");
3383
- const validationInput = normalizePatchFields(input, UPDATE_RESOURCE_FIELD_KEYS, { preserveNullFields: ["alias"] });
3384
- requireAtLeastOneField(validationInput, UPDATE_RESOURCE_FIELD_KEYS, "updateResource");
3385
- validateResourceWriteFields(validationInput, {
1530
+ (0, client_paths_js_1.requireResourceCrid)(id, "updateResource");
1531
+ (0, client_validation_js_1.requireObjectInput)(input, "updateResource");
1532
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.UPDATE_RESOURCE_FIELD_KEYS, "updateResource");
1533
+ const validationInput = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.UPDATE_RESOURCE_FIELD_KEYS, { preserveNullFields: ["alias"] });
1534
+ (0, client_validation_js_1.requireAtLeastOneField)(validationInput, client_input_fields_js_1.UPDATE_RESOURCE_FIELD_KEYS, "updateResource");
1535
+ (0, client_resources_js_1.validateResourceWriteFields)(validationInput, {
3386
1536
  allowAliasClear: true,
3387
1537
  allowCustomDomainClear: true,
3388
1538
  validatePreserveHost: true,
3389
1539
  });
3390
1540
  return this.request("PATCH", `/v1/resources/${encodeURIComponent(id)}`, validationInput, options);
3391
1541
  }
1542
+ /** Start Stripe Connect onboarding for this publisher. LayerV collects 3%. */
1543
+ async connectPublisherStripe() {
1544
+ const result = await this.request("POST", "/v1/payments/connect");
1545
+ let target;
1546
+ try {
1547
+ target = new URL(result.url);
1548
+ }
1549
+ catch {
1550
+ throw (0, client_errors_js_1.clientValidationError)("invalid Stripe connection URL");
1551
+ }
1552
+ if (target.protocol !== "https:" ||
1553
+ target.host !== "connect.stripe.com" ||
1554
+ target.username ||
1555
+ target.password)
1556
+ throw (0, client_errors_js_1.clientValidationError)("invalid Stripe connection URL");
1557
+ return result;
1558
+ }
1559
+ /** Read this publisher's connected account and the fixed application fee. */
1560
+ async publisherStripeAccount() {
1561
+ const result = await this.request("GET", "/v1/payments/account");
1562
+ if ((result.account_id !== "" && !/^acct_[A-Za-z0-9]{1,128}$/.test(result.account_id)) ||
1563
+ result.layerv_fee_percent !== 3)
1564
+ throw (0, client_errors_js_1.clientValidationError)("invalid Stripe account response");
1565
+ return result;
1566
+ }
3392
1567
  /** Send a publisher-only invitation. Mail delivery does not prove enrollment. */
3393
1568
  async inviteConsumer(id, email) {
3394
- requireResourceCrid(id, "inviteConsumer");
3395
- requireNonEmptyIfPresent(email, "email");
1569
+ (0, client_paths_js_1.requireResourceCrid)(id, "inviteConsumer");
1570
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(email, "email");
3396
1571
  if (typeof email !== "string" || email.length > 320)
3397
- throw clientValidationError("email: must be a bounded string");
1572
+ throw (0, client_errors_js_1.clientValidationError)("email: must be a bounded string");
3398
1573
  return this.request("POST", `/v1/resources/${encodeURIComponent(id)}/invitations`, { email });
3399
1574
  }
3400
1575
  async listConsumerInvitations(id, input = {}) {
3401
- requireResourceCrid(id, "listConsumerInvitations");
3402
- const { data, meta } = await this.rawRequest("GET", appendQuery(`/v1/resources/${encodeURIComponent(id)}/invitations`, input, ["cursor", "limit"], "listConsumerInvitations"));
1576
+ (0, client_paths_js_1.requireResourceCrid)(id, "listConsumerInvitations");
1577
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery(`/v1/resources/${encodeURIComponent(id)}/invitations`, input, ["cursor", "limit"], "listConsumerInvitations"));
3403
1578
  return pageFromMeta({ invitations: data ?? [] }, meta);
3404
1579
  }
3405
1580
  async listConsumerGrants(id, input = {}) {
3406
- requireResourceCrid(id, "listConsumerGrants");
3407
- const { data, meta } = await this.rawRequest("GET", appendQuery(`/v1/resources/${encodeURIComponent(id)}/consumer-grants`, input, ["cursor", "limit"], "listConsumerGrants"));
1581
+ (0, client_paths_js_1.requireResourceCrid)(id, "listConsumerGrants");
1582
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery(`/v1/resources/${encodeURIComponent(id)}/consumer-grants`, input, ["cursor", "limit"], "listConsumerGrants"));
3408
1583
  return pageFromMeta({ grants: data ?? [] }, meta);
3409
1584
  }
3410
1585
  async resendConsumerInvitation(id, invitationId, expectedVersion) {
3411
- requireResourceCrid(id, "resendConsumerInvitation");
3412
- requireConsumerDecision(invitationId, expectedVersion);
1586
+ (0, client_paths_js_1.requireResourceCrid)(id, "resendConsumerInvitation");
1587
+ (0, client_validation_js_1.requireConsumerDecision)(invitationId, expectedVersion);
3413
1588
  return this.request("POST", `/v1/resources/${encodeURIComponent(id)}/invitations/${encodeURIComponent(invitationId)}/resend`, { expected_version: expectedVersion });
3414
1589
  }
3415
1590
  /** Remove the invitation and its grant. Inspect session_cleanup before reporting stream removal. */
3416
1591
  async revokeConsumerInvitation(id, invitationId, expectedVersion) {
3417
- requireResourceCrid(id, "revokeConsumerInvitation");
3418
- requireConsumerDecision(invitationId, expectedVersion);
1592
+ (0, client_paths_js_1.requireResourceCrid)(id, "revokeConsumerInvitation");
1593
+ (0, client_validation_js_1.requireConsumerDecision)(invitationId, expectedVersion);
3419
1594
  return this.request("DELETE", `/v1/resources/${encodeURIComponent(id)}/invitations/${encodeURIComponent(invitationId)}?expected_version=${expectedVersion}`);
3420
1595
  }
3421
1596
  /** Review approval, key opt-in with grantId new, or trusted publisher entitlement. */
3422
1597
  async approveConsumerGrant(id, grantId, input) {
3423
- requireResourceCrid(id, "approveConsumerGrant");
3424
- requireObjectInput(input, "approveConsumerGrant");
3425
- requireNoUnknownFields(input, [
1598
+ (0, client_paths_js_1.requireResourceCrid)(id, "approveConsumerGrant");
1599
+ (0, client_validation_js_1.requireObjectInput)(input, "approveConsumerGrant");
1600
+ (0, client_validation_js_1.requireNoUnknownFields)(input, [
3426
1601
  "expected_version",
3427
1602
  "credential_kind",
3428
1603
  "credential_id",
@@ -3432,19 +1607,19 @@ class QURLClient {
3432
1607
  ], "approveConsumerGrant");
3433
1608
  if (input.credential_kind !== undefined &&
3434
1609
  !["passkey", "device"].includes(input.credential_kind)) {
3435
- throw clientValidationError("approveConsumerGrant: credential_kind must be passkey or device");
1610
+ throw (0, client_errors_js_1.clientValidationError)("approveConsumerGrant: credential_kind must be passkey or device");
3436
1611
  }
3437
- requireConsumerDecision(grantId, input.expected_version, grantId === "new");
1612
+ (0, client_validation_js_1.requireConsumerDecision)(grantId, input.expected_version, grantId === "new");
3438
1613
  return this.request("POST", `/v1/resources/${encodeURIComponent(id)}/consumer-grants/${encodeURIComponent(grantId)}/approve`, input);
3439
1614
  }
3440
1615
  async denyConsumerGrant(id, grantId, expectedVersion) {
3441
- requireResourceCrid(id, "denyConsumerGrant");
3442
- requireConsumerDecision(grantId, expectedVersion);
1616
+ (0, client_paths_js_1.requireResourceCrid)(id, "denyConsumerGrant");
1617
+ (0, client_validation_js_1.requireConsumerDecision)(grantId, expectedVersion);
3443
1618
  return this.request("POST", `/v1/resources/${encodeURIComponent(id)}/consumer-grants/${encodeURIComponent(grantId)}/deny`, { expected_version: expectedVersion });
3444
1619
  }
3445
1620
  async revokeConsumerGrant(id, grantId, expectedVersion) {
3446
- requireResourceCrid(id, "revokeConsumerGrant");
3447
- requireConsumerDecision(grantId, expectedVersion);
1621
+ (0, client_paths_js_1.requireResourceCrid)(id, "revokeConsumerGrant");
1622
+ (0, client_validation_js_1.requireConsumerDecision)(grantId, expectedVersion);
3448
1623
  return this.request("DELETE", `/v1/resources/${encodeURIComponent(id)}/consumer-grants/${encodeURIComponent(grantId)}?expected_version=${expectedVersion}`);
3449
1624
  }
3450
1625
  /**
@@ -3453,19 +1628,19 @@ class QURLClient {
3453
1628
  */
3454
1629
  async deleteResourceByIdentifier(id) {
3455
1630
  if (typeof id !== "string")
3456
- throw clientValidationError("id: invalid resource identifier");
1631
+ throw (0, client_errors_js_1.clientValidationError)("id: invalid resource identifier");
3457
1632
  if (/^r_[a-z0-9_-]{11}$/.test(id)) {
3458
1633
  await this.requestNoContent(`/v1/qurls/${encodeURIComponent(id)}`);
3459
1634
  return;
3460
1635
  }
3461
1636
  const publicKey = /^[A-Za-z0-9_-]{107,214}$/.test(id) && id.length % 4 !== 1;
3462
1637
  if (!publicKey)
3463
- requireResourceCrid(id, "deleteResourceByIdentifier");
1638
+ (0, client_paths_js_1.requireResourceCrid)(id, "deleteResourceByIdentifier");
3464
1639
  await this.requestNoContent(`/v1/resources/${encodeURIComponent(id)}`);
3465
1640
  }
3466
1641
  /** Revoke a resource and all of its qURLs. */
3467
1642
  async deleteResource(id) {
3468
- requireResourceCrid(id, "deleteResource");
1643
+ (0, client_paths_js_1.requireResourceCrid)(id, "deleteResource");
3469
1644
  await this.requestNoContent(`/v1/resources/${encodeURIComponent(id)}`);
3470
1645
  }
3471
1646
  /**
@@ -3474,15 +1649,15 @@ class QURLClient {
3474
1649
  * Portal-flow equivalent: {@link createPortal} (same endpoint).
3475
1650
  */
3476
1651
  async createQurlForResource(id, input, options) {
3477
- requireResourceCrid(id, "createQurlForResource");
1652
+ (0, client_paths_js_1.requireResourceCrid)(id, "createQurlForResource");
3478
1653
  let normalized = input;
3479
1654
  if (input !== undefined) {
3480
- requireObjectInput(input, "createQurlForResource");
3481
- requireNoUnknownFields(input, CREATE_QURL_FOR_RESOURCE_FIELD_KEYS, "createQurlForResource");
3482
- const stripped = normalizePatchFields(input, CREATE_QURL_FOR_RESOURCE_FIELD_KEYS);
1655
+ (0, client_validation_js_1.requireObjectInput)(input, "createQurlForResource");
1656
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.CREATE_QURL_FOR_RESOURCE_FIELD_KEYS, "createQurlForResource");
1657
+ const stripped = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.CREATE_QURL_FOR_RESOURCE_FIELD_KEYS);
3483
1658
  normalized = Object.keys(stripped).length > 0 ? stripped : undefined;
3484
1659
  }
3485
- validateQurlTokenOptions(normalized);
1660
+ (0, client_resources_js_1.validateQurlTokenOptions)(normalized);
3486
1661
  return this.request("POST", `/v1/resources/${encodeURIComponent(id)}/qurls`, normalized, options);
3487
1662
  }
3488
1663
  /**
@@ -3492,36 +1667,36 @@ class QURLClient {
3492
1667
  * before delivery to verify that the response CRID derives from that key.
3493
1668
  */
3494
1669
  async shareResource(id, options = {}, requestOptions) {
3495
- requireResourceCrid(id, "shareResource");
3496
- requireObjectInput(options, "shareResource");
3497
- requireNoUnknownFields(options, SHARE_RESOURCE_OPTION_KEYS, "shareResource");
1670
+ (0, client_paths_js_1.requireResourceCrid)(id, "shareResource");
1671
+ (0, client_validation_js_1.requireObjectInput)(options, "shareResource");
1672
+ (0, client_validation_js_1.requireNoUnknownFields)(options, client_input_fields_js_1.SHARE_RESOURCE_OPTION_KEYS, "shareResource");
3498
1673
  const body = {};
3499
1674
  if (options.ttlSeconds !== undefined) {
3500
1675
  if (typeof options.ttlSeconds !== "number" ||
3501
1676
  !Number.isSafeInteger(options.ttlSeconds) ||
3502
1677
  options.ttlSeconds <= 0) {
3503
- throw clientValidationError("shareResource: ttlSeconds must be a positive safe integer");
1678
+ throw (0, client_errors_js_1.clientValidationError)("shareResource: ttlSeconds must be a positive safe integer");
3504
1679
  }
3505
1680
  body.ttl_seconds = options.ttlSeconds;
3506
1681
  }
3507
1682
  const data = await this.request("POST", `/v1/resources/${encodeURIComponent(id)}/share`, body, requestOptions);
3508
1683
  if (typeof data?.qurl !== "string" || data.qurl.trim() === "") {
3509
- throw unexpectedResponseError("shareResource: response is missing qurl");
1684
+ throw (0, client_errors_js_1.unexpectedResponseError)("shareResource: response is missing qurl");
3510
1685
  }
3511
1686
  if (data.crid !== id) {
3512
- throw unexpectedResponseError("shareResource: response CRID does not match the requested CRID");
1687
+ throw (0, client_errors_js_1.unexpectedResponseError)("shareResource: response CRID does not match the requested CRID");
3513
1688
  }
3514
1689
  const wire = data;
3515
1690
  for (const field of ["qurl_id", "type", "expires_at"]) {
3516
1691
  const value = wire[field];
3517
1692
  if (value !== undefined && value !== null && typeof value !== "string") {
3518
- throw unexpectedResponseError(`shareResource: response has invalid ${field}`);
1693
+ throw (0, client_errors_js_1.unexpectedResponseError)(`shareResource: response has invalid ${field}`);
3519
1694
  }
3520
1695
  }
3521
1696
  for (const field of ["qurl_id", "type"]) {
3522
1697
  const value = wire[field];
3523
1698
  if (typeof value === "string" && (value.trim() === "" || value.trim() !== value)) {
3524
- throw unexpectedResponseError(`shareResource: response has invalid ${field}`);
1699
+ throw (0, client_errors_js_1.unexpectedResponseError)(`shareResource: response has invalid ${field}`);
3525
1700
  }
3526
1701
  }
3527
1702
  if (wire.expires_in_seconds !== undefined &&
@@ -3529,18 +1704,18 @@ class QURLClient {
3529
1704
  (typeof wire.expires_in_seconds !== "number" ||
3530
1705
  !Number.isSafeInteger(wire.expires_in_seconds) ||
3531
1706
  wire.expires_in_seconds < 0)) {
3532
- throw unexpectedResponseError("shareResource: response has invalid expires_in_seconds");
1707
+ throw (0, client_errors_js_1.unexpectedResponseError)("shareResource: response has invalid expires_in_seconds");
3533
1708
  }
3534
1709
  if (wire.single_use !== undefined &&
3535
1710
  wire.single_use !== null &&
3536
1711
  typeof wire.single_use !== "boolean") {
3537
- throw unexpectedResponseError("shareResource: response has invalid single_use");
1712
+ throw (0, client_errors_js_1.unexpectedResponseError)("shareResource: response has invalid single_use");
3538
1713
  }
3539
- const expiresAt = parseApiDate(typeof wire.expires_at === "string" ? wire.expires_at : undefined);
1714
+ const expiresAt = (0, client_portals_js_1.parseApiDate)(typeof wire.expires_at === "string" ? wire.expires_at : undefined);
3540
1715
  if (typeof wire.expires_at === "string" && expiresAt === undefined) {
3541
- throw unexpectedResponseError("shareResource: response has invalid expires_at");
1716
+ throw (0, client_errors_js_1.unexpectedResponseError)("shareResource: response has invalid expires_at");
3542
1717
  }
3543
- return new share_js_1.ShareLink({
1718
+ return new share_js_1.ShareLink((0, response_metadata_js_1.copyResponseMetadata)(data, {
3544
1719
  // Repair transport-only edge whitespace on the one-time credential;
3545
1720
  // ancillary identifiers fail closed instead of being normalized.
3546
1721
  link: data.qurl.trim(),
@@ -3550,45 +1725,45 @@ class QURLClient {
3550
1725
  expiresAt,
3551
1726
  expiresInSeconds: typeof wire.expires_in_seconds === "number" ? wire.expires_in_seconds : undefined,
3552
1727
  singleUse: typeof wire.single_use === "boolean" ? wire.single_use : undefined,
3553
- });
1728
+ }));
3554
1729
  }
3555
1730
  /** Revoke a specific qURL token on a resource. */
3556
1731
  async revokeResourceQurl(id, qurlId) {
3557
- requireResourceCrid(id, "revokeResourceQurl");
3558
- validatePathId(qurlId, "revokeResourceQurl", "qurl id", QURL_ID_PATH_OPTIONS);
1732
+ (0, client_paths_js_1.requireResourceCrid)(id, "revokeResourceQurl");
1733
+ (0, client_paths_js_1.validatePathId)(qurlId, "revokeResourceQurl", "qurl id", client_paths_js_1.QURL_ID_PATH_OPTIONS);
3559
1734
  await this.requestNoContent(`/v1/resources/${encodeURIComponent(id)}/qurls/${encodeURIComponent(qurlId)}`);
3560
1735
  }
3561
1736
  /** Update a specific qURL token on a resource. */
3562
1737
  async updateResourceQurl(id, qurlId, input, options) {
3563
- requireResourceCrid(id, "updateResourceQurl");
3564
- validatePathId(qurlId, "updateResourceQurl", "qurl id", QURL_ID_PATH_OPTIONS);
3565
- requireObjectInput(input, "updateResourceQurl");
3566
- requireNoUnknownFields(input, UPDATE_RESOURCE_QURL_FIELD_KEYS, "updateResourceQurl");
3567
- const normalized = normalizePatchFields(input, UPDATE_RESOURCE_QURL_FIELD_KEYS);
3568
- requireAtLeastOneField(normalized, UPDATE_RESOURCE_QURL_FIELD_KEYS, "updateResourceQurl");
3569
- validateResourceQurlUpdateInput(normalized);
1738
+ (0, client_paths_js_1.requireResourceCrid)(id, "updateResourceQurl");
1739
+ (0, client_paths_js_1.validatePathId)(qurlId, "updateResourceQurl", "qurl id", client_paths_js_1.QURL_ID_PATH_OPTIONS);
1740
+ (0, client_validation_js_1.requireObjectInput)(input, "updateResourceQurl");
1741
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.UPDATE_RESOURCE_QURL_FIELD_KEYS, "updateResourceQurl");
1742
+ const normalized = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.UPDATE_RESOURCE_QURL_FIELD_KEYS);
1743
+ (0, client_validation_js_1.requireAtLeastOneField)(normalized, client_input_fields_js_1.UPDATE_RESOURCE_QURL_FIELD_KEYS, "updateResourceQurl");
1744
+ (0, client_resources_js_1.validateResourceQurlUpdateInput)(normalized);
3570
1745
  return this.request("PATCH", `/v1/resources/${encodeURIComponent(id)}/qurls/${encodeURIComponent(qurlId)}`, normalized, options);
3571
1746
  }
3572
1747
  /** List every active session, following bounded service pages. */
3573
1748
  async listResourceSessions(id) {
3574
- requireResourceCrid(id, "listResourceSessions");
1749
+ (0, client_paths_js_1.requireResourceCrid)(id, "listResourceSessions");
3575
1750
  const sessions = [];
3576
1751
  let request_id;
3577
1752
  let page_size;
3578
1753
  for await (const session of this.paginateAll("listResourceSessions", {}, async ({ cursor }) => {
3579
- const { data, meta } = await this.rawRequest("GET", appendQuery(`/v1/resources/${encodeURIComponent(id)}/sessions`, { paged: "true", cursor }, ["paged", "cursor"], "listResourceSessions"));
1754
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery(`/v1/resources/${encodeURIComponent(id)}/sessions`, { paged: "true", cursor }, ["paged", "cursor"], "listResourceSessions"));
3580
1755
  if (meta?.has_more && !meta.next_cursor) {
3581
- throw unexpectedResponseError("Session page has no continuation cursor", {
1756
+ throw (0, client_errors_js_1.unexpectedResponseError)("Session page has no continuation cursor", {
3582
1757
  requestId: meta.request_id,
3583
1758
  });
3584
1759
  }
3585
1760
  request_id = meta?.request_id;
3586
1761
  page_size = meta?.page_size;
3587
- return { ...meta, sessions: data ?? [] };
1762
+ return withEnvelopeMetadata({ ...meta, sessions: data ?? [] }, meta);
3588
1763
  }, (page) => page.sessions)) {
3589
1764
  sessions.push(session);
3590
1765
  }
3591
- return { sessions, request_id, has_more: false, page_size };
1766
+ return withEnvelopeMetadata({ sessions, request_id, has_more: false, page_size }, { request_id });
3592
1767
  }
3593
1768
  /**
3594
1769
  * End every session page. This is not atomic: a failed page throws after earlier
@@ -3596,46 +1771,43 @@ class QURLClient {
3596
1771
  * A retry can close newly admitted sessions and counts only its own transitions.
3597
1772
  */
3598
1773
  async terminateAllResourceSessions(id) {
3599
- requireResourceCrid(id, "terminateAllResourceSessions");
1774
+ (0, client_paths_js_1.requireResourceCrid)(id, "terminateAllResourceSessions");
3600
1775
  const path = `/v1/resources/${encodeURIComponent(id)}/sessions`;
3601
1776
  let terminated = 0;
3602
1777
  let request_id;
3603
1778
  for await (const count of this.paginateAll("terminateAllResourceSessions", {}, async ({ cursor }) => {
3604
- const { data, meta, __http_status } = await this.rawRequest("DELETE", appendQuery(path, { paged: "true", cursor }, ["paged", "cursor"], "terminateAllResourceSessions"));
3605
- if (__http_status === 204) {
3606
- throw unexpectedResponseError(`Unexpected 204 No Content from DELETE ${path}; expected response body`, { status: 204, requestId: meta?.request_id });
1779
+ const { envelope: { data, meta }, status: httpStatus, } = await this.rawRequest("DELETE", appendQuery(path, { paged: "true", cursor }, ["paged", "cursor"], "terminateAllResourceSessions"));
1780
+ if (httpStatus === 204) {
1781
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected 204 No Content from DELETE ${path}; expected response body`, { status: 204, requestId: meta?.request_id });
3607
1782
  }
3608
1783
  if (meta?.has_more && !meta.next_cursor) {
3609
- throw unexpectedResponseError("Session page has no continuation cursor", {
1784
+ throw (0, client_errors_js_1.unexpectedResponseError)("Session page has no continuation cursor", {
3610
1785
  requestId: meta.request_id,
3611
1786
  });
3612
1787
  }
3613
- if (data?.terminated === undefined) {
3614
- this.log("terminateAllResourceSessions: missing terminated count; defaulting to zero", {
3615
- request_id: meta?.request_id,
3616
- });
3617
- }
3618
- else if (!Number.isSafeInteger(data.terminated) || data.terminated < 0) {
3619
- throw unexpectedResponseError("terminateAllResourceSessions: invalid terminated count", {
3620
- requestId: meta?.request_id,
3621
- });
1788
+ const count = data?.terminated;
1789
+ if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0) {
1790
+ throw (0, client_errors_js_1.unexpectedResponseError)("terminateAllResourceSessions: missing or invalid terminated count; inspect sessions before retrying", { requestId: meta?.request_id });
3622
1791
  }
3623
1792
  request_id = meta?.request_id;
3624
- return { ...meta, counts: [data?.terminated ?? 0] };
1793
+ return withEnvelopeMetadata({ ...meta, counts: [count] }, meta);
3625
1794
  }, (page) => page.counts)) {
3626
1795
  terminated += count;
1796
+ if (!Number.isSafeInteger(terminated)) {
1797
+ throw (0, client_errors_js_1.unexpectedResponseError)("terminateAllResourceSessions: total exceeds safe integer range; inspect sessions before retrying", { requestId: request_id });
1798
+ }
3627
1799
  }
3628
- return { terminated, request_id };
1800
+ return withEnvelopeMetadata({ terminated, request_id }, { request_id });
3629
1801
  }
3630
1802
  /** Terminate a specific resource session. */
3631
1803
  async terminateResourceSession(id, sessionId) {
3632
- requireResourceCrid(id, "terminateResourceSession");
3633
- validatePathId(sessionId, "terminateResourceSession", "session id");
1804
+ (0, client_paths_js_1.requireResourceCrid)(id, "terminateResourceSession");
1805
+ (0, client_paths_js_1.validatePathId)(sessionId, "terminateResourceSession", "session id");
3634
1806
  await this.requestNoContent(`/v1/resources/${encodeURIComponent(id)}/sessions/${encodeURIComponent(sessionId)}`);
3635
1807
  }
3636
1808
  /** List connector installations. */
3637
1809
  async listConnectorInstallations(input = {}) {
3638
- const { data, meta } = await this.rawRequest("GET", appendQuery("/v1/connectors/installations", input, CONNECTOR_INSTALLATION_LIST_PARAM_KEYS, "listConnectorInstallations"));
1810
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery("/v1/connectors/installations", input, client_input_fields_js_1.CONNECTOR_INSTALLATION_LIST_PARAM_KEYS, "listConnectorInstallations"));
3639
1811
  return pageFromMeta({ installations: data ?? [] }, meta);
3640
1812
  }
3641
1813
  /**
@@ -3645,7 +1817,7 @@ class QURLClient {
3645
1817
  * iteration begins.
3646
1818
  */
3647
1819
  listAllConnectorInstallations(input = {}) {
3648
- validateListAllInput(input, CONNECTOR_INSTALLATION_LIST_PARAM_KEYS, "listAllConnectorInstallations");
1820
+ validateListAllInput(input, client_input_fields_js_1.CONNECTOR_INSTALLATION_LIST_PARAM_KEYS, "listAllConnectorInstallations");
3649
1821
  return this.paginateAll("listAllConnectorInstallations", input, (pageInput) => this.listConnectorInstallations(pageInput), (page) => page.installations);
3650
1822
  }
3651
1823
  /** Get current-period usage. */
@@ -3662,27 +1834,27 @@ class QURLClient {
3662
1834
  }
3663
1835
  /** Update customer settings. */
3664
1836
  async updateCustomer(input, options) {
3665
- requireObjectInput(input, "updateCustomer");
3666
- requireNoUnknownFields(input, UPDATE_CUSTOMER_FIELD_KEYS, "updateCustomer");
3667
- const normalizedRecord = normalizePatchFields(input, UPDATE_CUSTOMER_FIELD_KEYS);
1837
+ (0, client_validation_js_1.requireObjectInput)(input, "updateCustomer");
1838
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.UPDATE_CUSTOMER_FIELD_KEYS, "updateCustomer");
1839
+ const normalizedRecord = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.UPDATE_CUSTOMER_FIELD_KEYS);
3668
1840
  const normalized = normalizedRecord;
3669
1841
  const spendingCap = normalized.spending_cap_cents;
3670
1842
  if (spendingCap === undefined) {
3671
- throw clientValidationError("updateCustomer: spending_cap_cents is required");
1843
+ throw (0, client_errors_js_1.clientValidationError)("updateCustomer: spending_cap_cents is required");
3672
1844
  }
3673
1845
  if (!Number.isInteger(spendingCap) || spendingCap < 0) {
3674
- const rendered = typeof spendingCap === "number" ? String(spendingCap) : describeShape(spendingCap);
3675
- throw clientValidationError(`updateCustomer: spending_cap_cents must be a non-negative integer (got ${rendered})`);
1846
+ const rendered = typeof spendingCap === "number" ? String(spendingCap) : (0, client_validation_js_1.describeShape)(spendingCap);
1847
+ throw (0, client_errors_js_1.clientValidationError)(`updateCustomer: spending_cap_cents must be a non-negative integer (got ${rendered})`);
3676
1848
  }
3677
1849
  return this.request("PATCH", "/v1/customer", normalized, options);
3678
1850
  }
3679
1851
  /** Create a Stripe checkout session. */
3680
1852
  async createBillingCheckout(input, options) {
3681
- requireObjectInput(input, "createBillingCheckout");
3682
- requireNoUnknownFields(input, CREATE_BILLING_CHECKOUT_FIELD_KEYS, "createBillingCheckout");
3683
- const normalizedRecord = normalizePatchFields(input, CREATE_BILLING_CHECKOUT_FIELD_KEYS);
1853
+ (0, client_validation_js_1.requireObjectInput)(input, "createBillingCheckout");
1854
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.CREATE_BILLING_CHECKOUT_FIELD_KEYS, "createBillingCheckout");
1855
+ const normalizedRecord = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.CREATE_BILLING_CHECKOUT_FIELD_KEYS);
3684
1856
  const normalized = normalizedRecord;
3685
- requireNonEmptyStringField(normalizedRecord, "plan", "createBillingCheckout");
1857
+ (0, client_validation_js_1.requireNonEmptyStringField)(normalizedRecord, "plan", "createBillingCheckout");
3686
1858
  return this.request("POST", "/v1/billing/checkout", normalized, options);
3687
1859
  }
3688
1860
  /** Create a Stripe billing portal session. */
@@ -3693,7 +1865,7 @@ class QURLClient {
3693
1865
  async listBillingInvoices(input = {}) {
3694
1866
  // This endpoint nests the list in `data.invoices` unlike the other list
3695
1867
  // endpoints, which return `data` as the array directly.
3696
- const { data, meta } = await this.rawRequest("GET", appendQuery("/v1/billing/invoices", input, BILLING_INVOICE_LIST_PARAM_KEYS, "listBillingInvoices"));
1868
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery("/v1/billing/invoices", input, client_input_fields_js_1.BILLING_INVOICE_LIST_PARAM_KEYS, "listBillingInvoices"));
3697
1869
  return pageFromMeta({ invoices: data?.invoices ?? [] }, meta);
3698
1870
  }
3699
1871
  /**
@@ -3703,22 +1875,22 @@ class QURLClient {
3703
1875
  * iteration begins.
3704
1876
  */
3705
1877
  listAllBillingInvoices(input = {}) {
3706
- validateListAllInput(input, BILLING_INVOICE_LIST_PARAM_KEYS, "listAllBillingInvoices");
1878
+ validateListAllInput(input, client_input_fields_js_1.BILLING_INVOICE_LIST_PARAM_KEYS, "listAllBillingInvoices");
3707
1879
  return this.paginateAll("listAllBillingInvoices", input, (pageInput) => this.listBillingInvoices(pageInput), (page) => page.invoices);
3708
1880
  }
3709
1881
  /** Register a custom domain. */
3710
1882
  async registerDomain(input, options) {
3711
- requireObjectInput(input, "registerDomain");
3712
- requireNoUnknownFields(input, REGISTER_DOMAIN_FIELD_KEYS, "registerDomain");
3713
- const normalizedRecord = normalizePatchFields(input, REGISTER_DOMAIN_FIELD_KEYS);
1883
+ (0, client_validation_js_1.requireObjectInput)(input, "registerDomain");
1884
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.REGISTER_DOMAIN_FIELD_KEYS, "registerDomain");
1885
+ const normalizedRecord = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.REGISTER_DOMAIN_FIELD_KEYS);
3714
1886
  const normalized = normalizedRecord;
3715
- requireNonEmptyStringField(normalizedRecord, "domain", "registerDomain");
3716
- requireMaxLength(normalized.domain, "domain", MAX_CUSTOM_DOMAIN);
1887
+ (0, client_validation_js_1.requireNonEmptyStringField)(normalizedRecord, "domain", "registerDomain");
1888
+ (0, client_validation_js_1.requireMaxLength)(normalized.domain, "domain", client_limits_js_1.MAX_CUSTOM_DOMAIN);
3717
1889
  return this.request("POST", "/v1/domains", normalized, options);
3718
1890
  }
3719
1891
  /** List custom domains. */
3720
1892
  async listDomains(input = {}) {
3721
- const { data, meta } = await this.rawRequest("GET", appendQuery("/v1/domains", input, DOMAIN_LIST_PARAM_KEYS, "listDomains"));
1893
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery("/v1/domains", input, client_input_fields_js_1.DOMAIN_LIST_PARAM_KEYS, "listDomains"));
3722
1894
  return pageFromMeta({ domains: data ?? [] }, meta);
3723
1895
  }
3724
1896
  /**
@@ -3728,32 +1900,32 @@ class QURLClient {
3728
1900
  * iteration begins.
3729
1901
  */
3730
1902
  listAllDomains(input = {}) {
3731
- validateListAllInput(input, DOMAIN_LIST_PARAM_KEYS, "listAllDomains");
1903
+ validateListAllInput(input, client_input_fields_js_1.DOMAIN_LIST_PARAM_KEYS, "listAllDomains");
3732
1904
  return this.paginateAll("listAllDomains", input, (pageInput) => this.listDomains(pageInput), (page) => page.domains);
3733
1905
  }
3734
1906
  /** Get custom domain status. */
3735
1907
  async getDomain(domain) {
3736
- validatePathId(domain, "getDomain", "domain");
1908
+ (0, client_paths_js_1.validatePathId)(domain, "getDomain", "domain");
3737
1909
  return this.request("GET", `/v1/domains/${encodeURIComponent(domain)}`);
3738
1910
  }
3739
1911
  /** Remove a custom domain. */
3740
1912
  async deleteDomain(domain) {
3741
- validatePathId(domain, "deleteDomain", "domain");
1913
+ (0, client_paths_js_1.validatePathId)(domain, "deleteDomain", "domain");
3742
1914
  await this.requestNoContent(`/v1/domains/${encodeURIComponent(domain)}`);
3743
1915
  }
3744
1916
  /** Trigger DNS verification for a custom domain. */
3745
1917
  async verifyDomain(domain, options) {
3746
- validatePathId(domain, "verifyDomain", "domain");
1918
+ (0, client_paths_js_1.validatePathId)(domain, "verifyDomain", "domain");
3747
1919
  return this.request("POST", `/v1/domains/${encodeURIComponent(domain)}/verify`, undefined, options);
3748
1920
  }
3749
1921
  /** Regenerate a domain verification token. */
3750
1922
  async regenerateDomainToken(domain, options) {
3751
- validatePathId(domain, "regenerateDomainToken", "domain");
1923
+ (0, client_paths_js_1.validatePathId)(domain, "regenerateDomainToken", "domain");
3752
1924
  return this.request("POST", `/v1/domains/${encodeURIComponent(domain)}/regenerate-token`, undefined, options);
3753
1925
  }
3754
1926
  /** List webhooks. */
3755
1927
  async listWebhooks(input = {}) {
3756
- const { data, meta } = await this.rawRequest("GET", appendQuery("/v1/webhooks", input, WEBHOOK_LIST_PARAM_KEYS, "listWebhooks"));
1928
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery("/v1/webhooks", input, client_input_fields_js_1.WEBHOOK_LIST_PARAM_KEYS, "listWebhooks"));
3757
1929
  return pageFromMeta({ webhooks: data ?? [] }, meta);
3758
1930
  }
3759
1931
  /**
@@ -3763,16 +1935,16 @@ class QURLClient {
3763
1935
  * iteration begins.
3764
1936
  */
3765
1937
  listAllWebhooks(input = {}) {
3766
- validateListAllInput(input, WEBHOOK_LIST_PARAM_KEYS, "listAllWebhooks");
1938
+ validateListAllInput(input, client_input_fields_js_1.WEBHOOK_LIST_PARAM_KEYS, "listAllWebhooks");
3767
1939
  return this.paginateAll("listAllWebhooks", input, (pageInput) => this.listWebhooks(pageInput), (page) => page.webhooks);
3768
1940
  }
3769
1941
  /** Create a webhook. */
3770
1942
  async createWebhook(input, options) {
3771
- requireObjectInput(input, "createWebhook");
3772
- requireNoUnknownFields(input, CREATE_WEBHOOK_FIELD_KEYS, "createWebhook");
3773
- const normalizedRecord = normalizePatchFields(input, CREATE_WEBHOOK_FIELD_KEYS);
1943
+ (0, client_validation_js_1.requireObjectInput)(input, "createWebhook");
1944
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.CREATE_WEBHOOK_FIELD_KEYS, "createWebhook");
1945
+ const normalizedRecord = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.CREATE_WEBHOOK_FIELD_KEYS);
3774
1946
  const normalized = normalizedRecord;
3775
- validateWebhookWriteFields(normalizedRecord, "createWebhook", {
1947
+ (0, client_management_js_1.validateWebhookWriteFields)(normalizedRecord, "createWebhook", {
3776
1948
  url: true,
3777
1949
  events: true,
3778
1950
  });
@@ -3784,33 +1956,33 @@ class QURLClient {
3784
1956
  }
3785
1957
  /** Get a webhook. */
3786
1958
  async getWebhook(id) {
3787
- validatePathId(id, "getWebhook");
1959
+ (0, client_paths_js_1.validatePathId)(id, "getWebhook");
3788
1960
  return this.request("GET", `/v1/webhooks/${encodeURIComponent(id)}`);
3789
1961
  }
3790
1962
  /** Update a webhook. */
3791
1963
  async updateWebhook(id, input, options) {
3792
- validatePathId(id, "updateWebhook");
3793
- requireObjectInput(input, "updateWebhook");
3794
- requireNoUnknownFields(input, UPDATE_WEBHOOK_FIELD_KEYS, "updateWebhook");
3795
- const normalized = normalizePatchFields(input, UPDATE_WEBHOOK_FIELD_KEYS);
3796
- requireAtLeastOneField(normalized, UPDATE_WEBHOOK_FIELD_KEYS, "updateWebhook");
3797
- validateWebhookWriteFields(normalized, "updateWebhook");
1964
+ (0, client_paths_js_1.validatePathId)(id, "updateWebhook");
1965
+ (0, client_validation_js_1.requireObjectInput)(input, "updateWebhook");
1966
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.UPDATE_WEBHOOK_FIELD_KEYS, "updateWebhook");
1967
+ const normalized = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.UPDATE_WEBHOOK_FIELD_KEYS);
1968
+ (0, client_validation_js_1.requireAtLeastOneField)(normalized, client_input_fields_js_1.UPDATE_WEBHOOK_FIELD_KEYS, "updateWebhook");
1969
+ (0, client_management_js_1.validateWebhookWriteFields)(normalized, "updateWebhook");
3798
1970
  return this.request("PATCH", `/v1/webhooks/${encodeURIComponent(id)}`, normalized, options);
3799
1971
  }
3800
1972
  /** Delete a webhook. */
3801
1973
  async deleteWebhook(id) {
3802
- validatePathId(id, "deleteWebhook");
1974
+ (0, client_paths_js_1.validatePathId)(id, "deleteWebhook");
3803
1975
  await this.requestNoContent(`/v1/webhooks/${encodeURIComponent(id)}`);
3804
1976
  }
3805
1977
  /** Regenerate a webhook signing secret. */
3806
1978
  async regenerateWebhookSecret(id, options) {
3807
- validatePathId(id, "regenerateWebhookSecret");
1979
+ (0, client_paths_js_1.validatePathId)(id, "regenerateWebhookSecret");
3808
1980
  return this.request("POST", `/v1/webhooks/${encodeURIComponent(id)}/secret`, undefined, options);
3809
1981
  }
3810
1982
  /** List delivery attempts for a webhook. */
3811
1983
  async listWebhookDeliveries(id, input = {}) {
3812
- validatePathId(id, "listWebhookDeliveries");
3813
- const { data, meta } = await this.rawRequest("GET", appendQuery(`/v1/webhooks/${encodeURIComponent(id)}/deliveries`, input, WEBHOOK_DELIVERY_LIST_PARAM_KEYS, "listWebhookDeliveries"));
1984
+ (0, client_paths_js_1.validatePathId)(id, "listWebhookDeliveries");
1985
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery(`/v1/webhooks/${encodeURIComponent(id)}/deliveries`, input, client_input_fields_js_1.WEBHOOK_DELIVERY_LIST_PARAM_KEYS, "listWebhookDeliveries"));
3814
1986
  return pageFromMeta({ deliveries: data ?? [] }, meta);
3815
1987
  }
3816
1988
  /**
@@ -3820,20 +1992,101 @@ class QURLClient {
3820
1992
  * iteration begins.
3821
1993
  */
3822
1994
  listAllWebhookDeliveries(id, input = {}) {
3823
- validatePathId(id, "listAllWebhookDeliveries");
3824
- validateListAllInput(input, WEBHOOK_DELIVERY_LIST_PARAM_KEYS, "listAllWebhookDeliveries");
1995
+ (0, client_paths_js_1.validatePathId)(id, "listAllWebhookDeliveries");
1996
+ validateListAllInput(input, client_input_fields_js_1.WEBHOOK_DELIVERY_LIST_PARAM_KEYS, "listAllWebhookDeliveries");
3825
1997
  return this.paginateAll("listAllWebhookDeliveries", input, (pageInput) => this.listWebhookDeliveries(id, pageInput), (page) => page.deliveries);
3826
1998
  }
1999
+ /**
2000
+ * Bind an external identity using the provider's trusted OAuth JWT as the
2001
+ * client's bearer credential. Persist the request key before calling, then
2002
+ * securely store the returned one-time plaintext key without logging it.
2003
+ * Retry a lost response within 24 hours with the same key and body.
2004
+ */
2005
+ async createExternalIdentityBinding(input, options) {
2006
+ const method = "createExternalIdentityBinding";
2007
+ (0, client_validation_js_1.requireObjectInput)(input, method);
2008
+ (0, client_validation_js_1.requireNoUnknownFields)(input, ["provider", "external_id", "display_name", "rotate_existing"], method);
2009
+ if (!["slack", "discord", "teams"].includes(input.provider)) {
2010
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: provider must be slack, discord, or teams`);
2011
+ }
2012
+ if (typeof input.external_id !== "string" ||
2013
+ input.external_id.length > 256 ||
2014
+ !/^[\x21-\x22\x24-\x7e]+$/.test(input.external_id)) {
2015
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: external_id must contain 1-256 visible ASCII characters without #`);
2016
+ }
2017
+ if (input.display_name !== undefined &&
2018
+ (typeof input.display_name !== "string" ||
2019
+ [...input.display_name].length > 100 ||
2020
+ /[\uD800-\uDFFF]/u.test(input.display_name))) {
2021
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: display_name must contain at most 100 Unicode characters`);
2022
+ }
2023
+ (0, client_validation_js_1.requireBooleanIfPresent)(input.rotate_existing, `${method}.rotate_existing`);
2024
+ validateRequestOptions(options);
2025
+ if (!options?.idempotencyKey || options.idempotencyKey.length < 32) {
2026
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: a persisted idempotencyKey of 32-256 characters is required`);
2027
+ }
2028
+ const body = {
2029
+ provider: input.provider,
2030
+ external_id: input.external_id,
2031
+ ...(input.display_name !== undefined ? { display_name: input.display_name } : {}),
2032
+ ...(input.rotate_existing !== undefined ? { rotate_existing: input.rotate_existing } : {}),
2033
+ };
2034
+ const raw = await this.rawRequest("POST", "/v1/external-identity-bindings", body, {
2035
+ requestOptions: options,
2036
+ generateIdempotencyKey: false,
2037
+ });
2038
+ // This operation returns a flat document, unlike the data/meta endpoints.
2039
+ const result = raw.envelope;
2040
+ const expectedScopes = body.provider === "discord"
2041
+ ? ["qurl:read", "qurl:write"]
2042
+ : ["qurl:read", "qurl:write", "qurl:agent"];
2043
+ if (raw.status !== 201 ||
2044
+ typeof result.binding_id !== "string" ||
2045
+ !/^eib_[A-Za-z0-9]{11}$/.test(result.binding_id) ||
2046
+ result.provider !== body.provider ||
2047
+ result.external_id !== body.external_id ||
2048
+ !result.api_key ||
2049
+ typeof result.api_key !== "object" ||
2050
+ Array.isArray(result.api_key) ||
2051
+ typeof result.api_key.key_id !== "string" ||
2052
+ !/^key_[A-Za-z0-9]{12}$/.test(result.api_key.key_id) ||
2053
+ typeof result.api_key.plaintext !== "string" ||
2054
+ !/^lv_(?:live|test)_[A-Za-z0-9_-]{43}$/.test(result.api_key.plaintext) ||
2055
+ result.api_key.key_prefix !== result.api_key.plaintext.slice(0, 12) ||
2056
+ !Array.isArray(result.scopes) ||
2057
+ result.scopes.length !== expectedScopes.length ||
2058
+ !expectedScopes.every((scope) => result.scopes.includes(scope)) ||
2059
+ !(0, client_delegated_js_1.isUtcTimestamp)(result.created_at) ||
2060
+ (result.display_name !== undefined &&
2061
+ (typeof result.display_name !== "string" ||
2062
+ [...result.display_name].length > 100 ||
2063
+ /[\uD800-\uDFFF]/u.test(result.display_name)))) {
2064
+ throw (0, client_errors_js_1.unexpectedResponseError)(`${method}: invalid binding response; reconcile with the same request key before retrying`);
2065
+ }
2066
+ return withEnvelopeMetadata({
2067
+ binding_id: result.binding_id,
2068
+ provider: result.provider,
2069
+ external_id: result.external_id,
2070
+ ...(result.display_name !== undefined ? { display_name: result.display_name } : {}),
2071
+ api_key: {
2072
+ key_id: result.api_key.key_id,
2073
+ key_prefix: result.api_key.key_prefix,
2074
+ plaintext: result.api_key.plaintext,
2075
+ },
2076
+ scopes: result.scopes,
2077
+ created_at: result.created_at,
2078
+ }, raw.envelope.meta);
2079
+ }
3827
2080
  /** Create a new API key. */
3828
2081
  async createApiKey(input, options) {
3829
- requireObjectInput(input, "createApiKey");
3830
- requireNoUnknownFields(input, CREATE_API_KEY_FIELD_KEYS, "createApiKey");
3831
- const normalizedRecord = normalizePatchFields(input, CREATE_API_KEY_FIELD_KEYS);
2082
+ (0, client_validation_js_1.requireObjectInput)(input, "createApiKey");
2083
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.CREATE_API_KEY_FIELD_KEYS, "createApiKey");
2084
+ const normalizedRecord = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.CREATE_API_KEY_FIELD_KEYS);
3832
2085
  // `kind` is required by the server and has no default there. Filling it
3833
2086
  // in keeps the common durable-key call site unchanged.
3834
2087
  normalizedRecord.kind ??= "api_key";
3835
2088
  if (normalizedRecord.kind !== "api_key" && normalizedRecord.kind !== "enrollment_token") {
3836
- throw clientValidationError("createApiKey: kind must be 'api_key' or 'enrollment_token'");
2089
+ throw (0, client_errors_js_1.clientValidationError)("createApiKey: kind must be 'api_key' or 'enrollment_token'");
3837
2090
  }
3838
2091
  // `scopes` and `target`/`claims` are mutually exclusive by kind, and the
3839
2092
  // server rejects either mismatch with 400 invalid_input. Fail both
@@ -3841,20 +2094,20 @@ class QURLClient {
3841
2094
  const isEnrollmentToken = normalizedRecord.kind === "enrollment_token";
3842
2095
  if (isEnrollmentToken) {
3843
2096
  if (normalizedRecord.scopes !== undefined) {
3844
- throw clientValidationError("createApiKey: scopes is not accepted for kind 'enrollment_token'; " +
2097
+ throw (0, client_errors_js_1.clientValidationError)("createApiKey: scopes is not accepted for kind 'enrollment_token'; " +
3845
2098
  "the server assigns them from target");
3846
2099
  }
3847
- validateEnrollmentTokenFields(normalizedRecord, "createApiKey");
2100
+ (0, client_management_js_1.validateEnrollmentTokenFields)(normalizedRecord, "createApiKey");
3848
2101
  }
3849
2102
  else {
3850
2103
  for (const field of ["target", "claims", "expires_in"]) {
3851
2104
  if (normalizedRecord[field] !== undefined) {
3852
- throw clientValidationError(`createApiKey: ${field} is only accepted for kind 'enrollment_token'`);
2105
+ throw (0, client_errors_js_1.clientValidationError)(`createApiKey: ${field} is only accepted for kind 'enrollment_token'`);
3853
2106
  }
3854
2107
  }
3855
2108
  }
3856
2109
  const normalized = normalizedRecord;
3857
- validateApiKeyWriteFields(normalizedRecord, "createApiKey", {
2110
+ (0, client_management_js_1.validateApiKeyWriteFields)(normalizedRecord, "createApiKey", {
3858
2111
  name: true,
3859
2112
  scopes: !isEnrollmentToken,
3860
2113
  });
@@ -3862,7 +2115,7 @@ class QURLClient {
3862
2115
  }
3863
2116
  /** List API keys. */
3864
2117
  async listApiKeys(input = {}) {
3865
- const { data, meta } = await this.rawRequest("GET", appendQuery("/v1/api-keys", input, API_KEY_LIST_PARAM_KEYS, "listApiKeys"));
2118
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", appendQuery("/v1/api-keys", input, client_input_fields_js_1.API_KEY_LIST_PARAM_KEYS, "listApiKeys"));
3866
2119
  return pageFromMeta({ api_keys: data ?? [] }, meta);
3867
2120
  }
3868
2121
  /**
@@ -3872,40 +2125,40 @@ class QURLClient {
3872
2125
  * iteration begins.
3873
2126
  */
3874
2127
  listAllApiKeys(input = {}) {
3875
- validateListAllInput(input, API_KEY_LIST_PARAM_KEYS, "listAllApiKeys");
2128
+ validateListAllInput(input, client_input_fields_js_1.API_KEY_LIST_PARAM_KEYS, "listAllApiKeys");
3876
2129
  return this.paginateAll("listAllApiKeys", input, (pageInput) => this.listApiKeys(pageInput), (page) => page.api_keys);
3877
2130
  }
3878
2131
  /** Update an API key. */
3879
2132
  async updateApiKey(keyId, input, options) {
3880
- validatePathId(keyId, "updateApiKey");
3881
- requireObjectInput(input, "updateApiKey");
3882
- requireNoUnknownFields(input, UPDATE_API_KEY_FIELD_KEYS, "updateApiKey");
3883
- const normalized = normalizePatchFields(input, UPDATE_API_KEY_FIELD_KEYS);
3884
- requireAtLeastOneField(normalized, UPDATE_API_KEY_FIELD_KEYS, "updateApiKey");
3885
- validateApiKeyWriteFields(normalized, "updateApiKey");
2133
+ (0, client_paths_js_1.validatePathId)(keyId, "updateApiKey");
2134
+ (0, client_validation_js_1.requireObjectInput)(input, "updateApiKey");
2135
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.UPDATE_API_KEY_FIELD_KEYS, "updateApiKey");
2136
+ const normalized = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.UPDATE_API_KEY_FIELD_KEYS);
2137
+ (0, client_validation_js_1.requireAtLeastOneField)(normalized, client_input_fields_js_1.UPDATE_API_KEY_FIELD_KEYS, "updateApiKey");
2138
+ (0, client_management_js_1.validateApiKeyWriteFields)(normalized, "updateApiKey");
3886
2139
  return this.request("PATCH", `/v1/api-keys/${encodeURIComponent(keyId)}`, normalized, options);
3887
2140
  }
3888
2141
  /** Revoke an API key. */
3889
2142
  async revokeApiKey(keyId) {
3890
- validatePathId(keyId, "revokeApiKey");
2143
+ (0, client_paths_js_1.validatePathId)(keyId, "revokeApiKey");
3891
2144
  await this.requestNoContent(`/v1/api-keys/${encodeURIComponent(keyId)}`);
3892
2145
  }
3893
2146
  /** Redeem an access code. */
3894
2147
  async redeemAccessCode(input, options) {
3895
- requireObjectInput(input, "redeemAccessCode");
3896
- requireNoUnknownFields(input, REDEEM_ACCESS_CODE_FIELD_KEYS, "redeemAccessCode");
3897
- const normalizedRecord = normalizePatchFields(input, REDEEM_ACCESS_CODE_FIELD_KEYS);
2148
+ (0, client_validation_js_1.requireObjectInput)(input, "redeemAccessCode");
2149
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.REDEEM_ACCESS_CODE_FIELD_KEYS, "redeemAccessCode");
2150
+ const normalizedRecord = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.REDEEM_ACCESS_CODE_FIELD_KEYS);
3898
2151
  const normalized = normalizedRecord;
3899
- requireNonEmptyStringField(normalizedRecord, "code", "redeemAccessCode");
2152
+ (0, client_validation_js_1.requireNonEmptyStringField)(normalizedRecord, "code", "redeemAccessCode");
3900
2153
  return this.request("POST", "/v1/access-codes/redeem", normalized, options);
3901
2154
  }
3902
2155
  /** Create an access code. */
3903
2156
  async createAccessCode(input, options) {
3904
- requireObjectInput(input, "createAccessCode");
3905
- requireNoUnknownFields(input, CREATE_ACCESS_CODE_FIELD_KEYS, "createAccessCode");
3906
- const normalizedRecord = normalizePatchFields(input, CREATE_ACCESS_CODE_FIELD_KEYS);
2157
+ (0, client_validation_js_1.requireObjectInput)(input, "createAccessCode");
2158
+ (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.CREATE_ACCESS_CODE_FIELD_KEYS, "createAccessCode");
2159
+ const normalizedRecord = (0, client_validation_js_1.normalizePatchFields)(input, client_input_fields_js_1.CREATE_ACCESS_CODE_FIELD_KEYS);
3907
2160
  const normalized = normalizedRecord;
3908
- requireResourceCrid(normalizedRecord.resource_id, "createAccessCode", "resource_id");
2161
+ (0, client_paths_js_1.requireResourceCrid)(normalizedRecord.resource_id, "createAccessCode", "resource_id");
3909
2162
  return this.request("POST", "/v1/access-codes", normalized, options);
3910
2163
  }
3911
2164
  /**
@@ -3916,36 +2169,36 @@ class QURLClient {
3916
2169
  * this page and emits a debug log rather than pretending it fetched all pages.
3917
2170
  */
3918
2171
  async listAccessCodes() {
3919
- const { data, meta } = await this.rawRequest("GET", "/v1/access-codes");
2172
+ const { envelope: { data, meta }, } = await this.rawRequest("GET", "/v1/access-codes");
3920
2173
  if (meta?.has_more || meta?.next_cursor) {
3921
2174
  this.log("listAccessCodes: pagination metadata surfaced on unpaginated endpoint", {
3922
2175
  has_more: meta.has_more,
3923
2176
  next_cursor: meta.next_cursor,
3924
2177
  });
3925
2178
  }
3926
- return {
2179
+ return withEnvelopeMetadata({
3927
2180
  access_codes: data ?? [],
3928
2181
  request_id: meta?.request_id,
3929
2182
  has_more: false,
3930
2183
  page_size: meta?.page_size,
3931
- };
2184
+ }, meta);
3932
2185
  }
3933
2186
  /** Revoke an access code. */
3934
2187
  async revokeAccessCode(id) {
3935
- validatePathId(id, "revokeAccessCode");
2188
+ (0, client_paths_js_1.validatePathId)(id, "revokeAccessCode");
3936
2189
  await this.requestNoContent(`/v1/access-codes/${encodeURIComponent(id)}`);
3937
2190
  }
3938
2191
  // --- Internal HTTP plumbing ---
3939
2192
  async request(method, path, body, requestOptions) {
3940
2193
  // List methods call rawRequest directly so 204 can degrade to an empty page.
3941
2194
  // Body-returning endpoints declare 200 JSON today, so this path fails closed.
3942
- const { data, __http_status } = await this.rawRequest(method, path, body, {
2195
+ const { envelope: { data, meta }, status: httpStatus, } = await this.rawRequest(method, path, body, {
3943
2196
  requestOptions,
3944
2197
  });
3945
- if (__http_status === 204) {
3946
- throw unexpectedResponseError(`Unexpected 204 No Content from ${method} ${path}; expected response body`);
2198
+ if (httpStatus === 204) {
2199
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected 204 No Content from ${method} ${path}; expected response body`);
3947
2200
  }
3948
- return data;
2201
+ return withEnvelopeMetadata(data, meta);
3949
2202
  }
3950
2203
  /**
3951
2204
  * Require the service's exact 204 response with no response bytes exposed by
@@ -3956,10 +2209,10 @@ class QURLClient {
3956
2209
  const response = await this.rawRequest("DELETE", path, undefined, {
3957
2210
  allowEmptySuccessBody: true,
3958
2211
  });
3959
- if (response.__http_status !== 204 || response.__http_body_empty !== true) {
3960
- const status = response.__http_status ?? 0;
3961
- const bodyShape = response.__http_body_empty ? "with an empty body" : "with response bytes";
3962
- throw unexpectedResponseError(`Unexpected response from DELETE; expected empty HTTP 204, received HTTP ${status} ${bodyShape}. ` +
2212
+ if (response.status !== 204 || response.bodyEmpty !== true) {
2213
+ const status = response.status ?? 0;
2214
+ const bodyShape = response.bodyEmpty ? "with an empty body" : "with response bytes";
2215
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Unexpected response from DELETE; expected empty HTTP 204, received HTTP ${status} ${bodyShape}. ` +
3963
2216
  "The delete may already have been applied; reconcile resource state before retrying.", { status });
3964
2217
  }
3965
2218
  }
@@ -4027,7 +2280,8 @@ class QURLClient {
4027
2280
  // request bound: roughly `timeout * (maxRetries + 1) +
4028
2281
  // sum(retryDelay)`.
4029
2282
  onDispatch?.();
4030
- response = await this.fetchFn(url, {
2283
+ // Native edge fetch requires the global receiver, not this client.
2284
+ response = await this.fetchFn.call(globalThis, url, {
4031
2285
  method,
4032
2286
  headers,
4033
2287
  body: serializedBody,
@@ -4038,13 +2292,13 @@ class QURLClient {
4038
2292
  catch (err) {
4039
2293
  lastError = this.classifyFetchError(err, requestSignal);
4040
2294
  this.log(`${method} ${url} ${lastError instanceof errors_js_1.TimeoutError ? "timed out" : "network error"}`, {
4041
- error: boundedErrorSnippet(lastError.message),
2295
+ error: (0, client_errors_js_1.boundedErrorSnippet)(lastError.message),
4042
2296
  });
4043
2297
  // An injected fetch can abort or impose a deadline for its own reason.
4044
2298
  // That is not this SDK's timeout. Replaying it can defeat caller-side
4045
2299
  // cancellation or deadline policy.
4046
2300
  if (retryFetchFailure &&
4047
- !isIndependentAbort(err, requestSignal) &&
2301
+ !(0, client_errors_js_1.isIndependentAbort)(err, requestSignal) &&
4048
2302
  attempt < this.maxRetries) {
4049
2303
  continue;
4050
2304
  }
@@ -4069,23 +2323,23 @@ class QURLClient {
4069
2323
  : REDIRECT_RESPONSE_STATUSES.has(response.status)
4070
2324
  ? `HTTP ${response.status}`
4071
2325
  : "opaque";
4072
- throw unexpectedResponseError(`Refused ${redirectKind} redirect response for ${method}`, {
2326
+ throw (0, client_errors_js_1.unexpectedResponseError)(`Refused ${redirectKind} redirect response for ${method}`, {
4073
2327
  status: response.status,
4074
2328
  });
4075
2329
  }
4076
2330
  let responseBody;
4077
2331
  try {
4078
- responseBody = await readBoundedResponseBody(response);
2332
+ responseBody = await readBoundedResponseBody(response, this.maxResponseBodyBytes);
4079
2333
  }
4080
2334
  catch (err) {
4081
2335
  if (err instanceof ResponseBodyTooLargeError) {
4082
2336
  this.log(`rejected oversized response body from ${response.status}`, {
4083
2337
  status: response.status,
4084
2338
  });
4085
- const detail = `Response body exceeds ${MAX_RESPONSE_BODY_BYTES}-byte limit on HTTP ${response.status}; ` +
2339
+ const detail = `Response body exceeds ${this.maxResponseBodyBytes}-byte limit on HTTP ${response.status}; ` +
4086
2340
  "request a smaller page when listing collections";
4087
2341
  if (response.ok) {
4088
- throw unexpectedResponseError(detail, { status: response.status });
2342
+ throw (0, client_errors_js_1.unexpectedResponseError)(detail, { status: response.status });
4089
2343
  }
4090
2344
  const bodyError = (0, errors_js_1.createError)({
4091
2345
  status: response.status,
@@ -4093,7 +2347,7 @@ class QURLClient {
4093
2347
  // the real HTTP status class (429/5xx) just like other unreadable
4094
2348
  // error bodies instead of reclassifying it as client validation.
4095
2349
  code: errors_js_1.ERROR_CODE_UNKNOWN,
4096
- title: boundedErrorSnippet(response.statusText) || `HTTP ${response.status}`,
2350
+ title: (0, client_errors_js_1.boundedErrorSnippet)(response.statusText) || `HTTP ${response.status}`,
4097
2351
  detail,
4098
2352
  retry_after: this.parseRetryAfter(response),
4099
2353
  });
@@ -4115,12 +2369,12 @@ class QURLClient {
4115
2369
  if (response.ok) {
4116
2370
  // A successful status with an invalid wire body is a deterministic
4117
2371
  // response-contract failure. Do not replay a GET or a mutation.
4118
- throw unexpectedResponseError(detail, { status: response.status });
2372
+ throw (0, client_errors_js_1.unexpectedResponseError)(detail, { status: response.status });
4119
2373
  }
4120
2374
  const encodingError = (0, errors_js_1.createError)({
4121
2375
  status: response.status,
4122
2376
  code: errors_js_1.ERROR_CODE_UNKNOWN,
4123
- title: boundedErrorSnippet(response.statusText) || `HTTP ${response.status}`,
2377
+ title: (0, client_errors_js_1.boundedErrorSnippet)(response.statusText) || `HTTP ${response.status}`,
4124
2378
  detail,
4125
2379
  retry_after: this.parseRetryAfter(response),
4126
2380
  });
@@ -4144,7 +2398,7 @@ class QURLClient {
4144
2398
  // path retries. Mutations are never replayed after a body transport
4145
2399
  // failure because the operation may have applied.
4146
2400
  if (method === "GET" &&
4147
- !isIndependentAbort(err, requestSignal) &&
2401
+ !(0, client_errors_js_1.isIndependentAbort)(err, requestSignal) &&
4148
2402
  (response.ok || retryable.has(response.status)) &&
4149
2403
  attempt < this.maxRetries) {
4150
2404
  lastError = transportReadError;
@@ -4161,11 +2415,11 @@ class QURLClient {
4161
2415
  ? err.message
4162
2416
  : `Failed to read response body on HTTP ${response.status}`;
4163
2417
  const readError = response.ok
4164
- ? unexpectedResponseError(detail, { status: response.status })
2418
+ ? (0, client_errors_js_1.unexpectedResponseError)(detail, { status: response.status })
4165
2419
  : (0, errors_js_1.createError)({
4166
2420
  status: response.status,
4167
2421
  code: errors_js_1.ERROR_CODE_UNKNOWN,
4168
- title: boundedErrorSnippet(response.statusText) || `HTTP ${response.status}`,
2422
+ title: (0, client_errors_js_1.boundedErrorSnippet)(response.statusText) || `HTTP ${response.status}`,
4169
2423
  detail,
4170
2424
  retry_after: this.parseRetryAfter(response),
4171
2425
  });
@@ -4173,7 +2427,7 @@ class QURLClient {
4173
2427
  // example a BigInt returned by json()) and non-Error throws. Do not
4174
2428
  // replay them: another attempt will see the same local contract bug.
4175
2429
  throw err instanceof ResponseBodyMaterializationError
4176
- ? withErrorCause(readError, err)
2430
+ ? (0, client_errors_js_1.withErrorCause)(readError, err)
4177
2431
  : readError;
4178
2432
  }
4179
2433
  // `response.ok` is true for the entire 200-299 range, so partial-
@@ -4199,10 +2453,10 @@ class QURLClient {
4199
2453
  });
4200
2454
  }
4201
2455
  return {
4202
- data: undefined,
4203
- __http_status: response.status,
4204
- __http_body_empty: responseBody.length === 0,
4205
- __http_headers: captureResponseHeaders ? response.headers : undefined,
2456
+ envelope: { data: undefined },
2457
+ status: response.status,
2458
+ bodyEmpty: responseBody.length === 0,
2459
+ headers: captureResponseHeaders ? response.headers : undefined,
4206
2460
  };
4207
2461
  }
4208
2462
  // Exact no-content helpers must inspect both axes of the success
@@ -4212,20 +2466,20 @@ class QURLClient {
4212
2466
  (isPassthrough && allowEmptyPassthroughBody)) &&
4213
2467
  responseBody.length === 0) {
4214
2468
  return {
4215
- data: undefined,
4216
- __http_status: response.status,
4217
- __http_body_empty: true,
4218
- __http_headers: captureResponseHeaders ? response.headers : undefined,
2469
+ envelope: { data: undefined },
2470
+ status: response.status,
2471
+ bodyEmpty: true,
2472
+ headers: captureResponseHeaders ? response.headers : undefined,
4219
2473
  };
4220
2474
  }
4221
2475
  try {
4222
2476
  const json = JSON.parse(responseBody);
4223
2477
  return {
4224
- ...json,
4225
- __http_status: response.status,
2478
+ envelope: { ...json },
2479
+ status: response.status,
4226
2480
  // JSON.parse("") throws, so parsed JSON is necessarily non-empty.
4227
- __http_body_empty: false,
4228
- __http_headers: captureResponseHeaders ? response.headers : undefined,
2481
+ bodyEmpty: false,
2482
+ headers: captureResponseHeaders ? response.headers : undefined,
4229
2483
  };
4230
2484
  }
4231
2485
  catch {
@@ -4236,12 +2490,12 @@ class QURLClient {
4236
2490
  // by-class don't miss the contract failure.
4237
2491
  this.log(`non-JSON body on ${isPassthrough ? "passthrough" : "success"} response ${response.status}`, {
4238
2492
  status: response.status,
4239
- content_type: boundedErrorSnippet(response.headers.get("content-type")),
2493
+ content_type: (0, client_errors_js_1.boundedErrorSnippet)(response.headers.get("content-type")),
4240
2494
  });
4241
2495
  throw (0, errors_js_1.createError)({
4242
2496
  status: response.status,
4243
2497
  code: errors_js_1.ERROR_CODE_UNEXPECTED_RESPONSE,
4244
- title: boundedErrorSnippet(response.statusText) || `HTTP ${response.status}`,
2498
+ title: (0, client_errors_js_1.boundedErrorSnippet)(response.statusText) || `HTTP ${response.status}`,
4245
2499
  detail: `Expected JSON response body on HTTP ${response.status} but received non-JSON content`,
4246
2500
  });
4247
2501
  }
@@ -4272,13 +2526,13 @@ class QURLClient {
4272
2526
  // 3. err.title (RFC 7807 required field)
4273
2527
  // 4. HTTP status (final safety net)
4274
2528
  // This prevents `"Title (403): undefined"` when the API omits detail.
4275
- const detail = boundedErrorSnippet(err.detail) ??
4276
- boundedErrorSnippet(err.message) ??
4277
- boundedErrorSnippet(err.title) ??
2529
+ const detail = (0, client_errors_js_1.boundedErrorSnippet)(err.detail) ??
2530
+ (0, client_errors_js_1.boundedErrorSnippet)(err.message) ??
2531
+ (0, client_errors_js_1.boundedErrorSnippet)(err.title) ??
4278
2532
  `HTTP ${response.status}`;
4279
2533
  // HTTP/2 omits reason-phrases — `statusText` may be "".
4280
- const title = boundedErrorSnippet(err.title) ??
4281
- boundedErrorSnippet(response.statusText) ??
2534
+ const title = (0, client_errors_js_1.boundedErrorSnippet)(err.title) ??
2535
+ (0, client_errors_js_1.boundedErrorSnippet)(response.statusText) ??
4282
2536
  `HTTP ${response.status}`;
4283
2537
  // The transport status is authoritative for error classification. A
4284
2538
  // conflicting or malformed RFC 7807 body must not turn a real 5xx into
@@ -4286,13 +2540,14 @@ class QURLClient {
4286
2540
  const status = response.status;
4287
2541
  return {
4288
2542
  status,
4289
- code: boundedErrorIdentifier(err.code) ?? errors_js_1.ERROR_CODE_UNKNOWN,
2543
+ code: (0, client_errors_js_1.boundedErrorIdentifier)(err.code) ?? errors_js_1.ERROR_CODE_UNKNOWN,
4290
2544
  title,
4291
2545
  detail,
4292
- type: boundedErrorIdentifier(err.type),
4293
- instance: boundedErrorIdentifier(err.instance),
4294
- invalid_fields: boundedInvalidFields(err.invalid_fields),
4295
- request_id: boundedErrorIdentifier(json.meta?.request_id),
2546
+ type: (0, client_errors_js_1.boundedErrorIdentifier)(err.type),
2547
+ instance: (0, client_errors_js_1.boundedErrorIdentifier)(err.instance),
2548
+ invalid_fields: (0, client_errors_js_1.boundedInvalidFields)(err.invalid_fields),
2549
+ request_id: (0, client_errors_js_1.boundedErrorIdentifier)(json.meta?.request_id),
2550
+ tombstone: response.status === 410 ? (0, client_errors_js_1.boundedTombstone)(json.meta?.tombstone) : undefined,
4296
2551
  retry_after: this.parseRetryAfter(response),
4297
2552
  };
4298
2553
  }
@@ -4302,7 +2557,7 @@ class QURLClient {
4302
2557
  // status-only safety net below.
4303
2558
  this.log(`unexpected error response shape from ${response.status}`, {
4304
2559
  status: response.status,
4305
- body_keys: typeof json === "object" && json !== null ? boundedObjectKeys(json) : undefined,
2560
+ body_keys: typeof json === "object" && json !== null ? (0, client_errors_js_1.boundedObjectKeys)(json) : undefined,
4306
2561
  });
4307
2562
  }
4308
2563
  catch {
@@ -4310,10 +2565,10 @@ class QURLClient {
4310
2565
  // malformed envelope, and fall through to the status-only safety net.
4311
2566
  this.log(`non-JSON error response from ${response.status}`, {
4312
2567
  status: response.status,
4313
- content_type: boundedErrorSnippet(response.headers.get("content-type")),
2568
+ content_type: (0, client_errors_js_1.boundedErrorSnippet)(response.headers.get("content-type")),
4314
2569
  });
4315
2570
  }
4316
- const statusText = boundedErrorSnippet(response.statusText) || `HTTP ${response.status}`;
2571
+ const statusText = (0, client_errors_js_1.boundedErrorSnippet)(response.statusText) || `HTTP ${response.status}`;
4317
2572
  return {
4318
2573
  status: response.status,
4319
2574
  code: errors_js_1.ERROR_CODE_UNKNOWN,
@@ -4339,8 +2594,8 @@ class QURLClient {
4339
2594
  const trimmed = header.trim();
4340
2595
  if (/^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/.test(trimmed)) {
4341
2596
  const seconds = Math.ceil((Date.parse(trimmed) - Date.now()) / 1000);
4342
- return Number.isSafeInteger(seconds) && seconds > 0 && seconds <= RETRY_AFTER_PARSE_LIMIT_S
4343
- ? seconds
2597
+ return Number.isSafeInteger(seconds)
2598
+ ? Math.min(Math.max(0, seconds), client_limits_js_1.RETRY_AFTER_PARSE_LIMIT_S)
4344
2599
  : undefined;
4345
2600
  }
4346
2601
  if (!/^\d+$/.test(trimmed)) {
@@ -4355,10 +2610,10 @@ class QURLClient {
4355
2610
  // `*1000` ms-conversion in retryDelay before the hard cap clamps
4356
2611
  // them — fail fast instead, with a debug breadcrumb. Cutoff matches
4357
2612
  // the hard cap so a clean cap-bounded value still parses.
4358
- if (value > RETRY_AFTER_PARSE_LIMIT_S) {
2613
+ if (value > client_limits_js_1.RETRY_AFTER_PARSE_LIMIT_S) {
4359
2614
  this.log("Retry-After header exceeded parse limit, using exponential backoff", {
4360
2615
  header_value: header.slice(0, 100),
4361
- limit_seconds: RETRY_AFTER_PARSE_LIMIT_S,
2616
+ limit_seconds: client_limits_js_1.RETRY_AFTER_PARSE_LIMIT_S,
4362
2617
  });
4363
2618
  return undefined;
4364
2619
  }
@@ -4380,24 +2635,24 @@ class QURLClient {
4380
2635
  retryDelay(attempt, lastError) {
4381
2636
  if (lastError instanceof errors_js_1.QURLError && lastError.retryAfter !== undefined) {
4382
2637
  const requestedMs = lastError.retryAfter * 1000;
4383
- if (requestedMs > RETRY_AFTER_HARD_CAP_MS) {
2638
+ if (requestedMs > client_limits_js_1.RETRY_AFTER_HARD_CAP_MS) {
4384
2639
  this.log("Retry-After exceeded hard cap, truncating", {
4385
2640
  requested_seconds: lastError.retryAfter,
4386
- capped_ms: RETRY_AFTER_HARD_CAP_MS,
2641
+ capped_ms: client_limits_js_1.RETRY_AFTER_HARD_CAP_MS,
4387
2642
  });
4388
- return RETRY_AFTER_HARD_CAP_MS;
2643
+ return client_limits_js_1.RETRY_AFTER_HARD_CAP_MS;
4389
2644
  }
4390
2645
  return requestedMs;
4391
2646
  }
4392
- const base = RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1);
2647
+ const base = client_limits_js_1.RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1);
4393
2648
  const jitter = Math.random() * base * 0.5;
4394
- return Math.min(base + jitter, RETRY_MAX_DELAY_MS);
2649
+ return Math.min(base + jitter, client_limits_js_1.RETRY_MAX_DELAY_MS);
4395
2650
  }
4396
2651
  classifyFetchError(err, requestSignal) {
4397
2652
  // AbortSignal.timeout is the only timeout source owned by this SDK. Keep
4398
2653
  // this check tied to that signal instead of trusting an injected fetch's
4399
2654
  // AbortError or TimeoutError name.
4400
- if (isSdkTimeout(err, requestSignal)) {
2655
+ if ((0, client_errors_js_1.isSdkTimeout)(err, requestSignal)) {
4401
2656
  return new errors_js_1.TimeoutError("Request timed out", { cause: err });
4402
2657
  }
4403
2658
  const cause = err instanceof Error ? err : undefined;
@@ -4409,8 +2664,8 @@ class QURLClient {
4409
2664
  if (!(err instanceof Error))
4410
2665
  return undefined;
4411
2666
  if (!response.ok) {
4412
- const statusText = boundedErrorSnippet(response.statusText) || `HTTP ${response.status}`;
4413
- return withErrorCause((0, errors_js_1.createError)({
2667
+ const statusText = (0, client_errors_js_1.boundedErrorSnippet)(response.statusText) || `HTTP ${response.status}`;
2668
+ return (0, client_errors_js_1.withErrorCause)((0, errors_js_1.createError)({
4414
2669
  status: response.status,
4415
2670
  code: errors_js_1.ERROR_CODE_UNKNOWN,
4416
2671
  title: statusText,
@@ -4418,100 +2673,18 @@ class QURLClient {
4418
2673
  retry_after: this.parseRetryAfter(response),
4419
2674
  }), err);
4420
2675
  }
4421
- if (isSdkTimeout(err, requestSignal)) {
2676
+ if ((0, client_errors_js_1.isSdkTimeout)(err, requestSignal)) {
4422
2677
  return new errors_js_1.TimeoutError(`Request timed out while reading response body after HTTP ${response.status}`, { cause: err });
4423
2678
  }
4424
2679
  return new errors_js_1.NetworkError(`Network failure while reading response body after HTTP ${response.status}`, { cause: err });
4425
2680
  }
4426
2681
  }
4427
2682
  exports.QURLClient = QURLClient;
4428
- /**
4429
- * A LayerV-protected resource, bound to the client that produced it.
4430
- *
4431
- * Obtained from {@link QURLClient.protectUrl} or {@link QURLClient.resourceByCrid} —
4432
- * not constructed directly. Mint short-lived access links for the resource
4433
- * with {@link createPortal} (qurl-go: `qurl.Resource`).
4434
- */
4435
- class ProtectedResource {
4436
- // A true private field (not a module-scope WeakMap registry, which
4437
- // CONTRIBUTING.md's dual-build rule bars) so the binding stays on the
4438
- // instance and out of Object.keys/JSON.stringify output.
4439
- #client;
4440
- /** The public resource CRID. */
4441
- crid;
4442
- /** The private URL protected by this resource, when known. */
4443
- targetUrl;
4444
- /**
4445
- * Full server-populated {@link Resource} metadata when the handle came
4446
- * from an API response; `undefined` for {@link QURLClient.resourceByCrid}
4447
- * handles and for the one-call {@link QURLClient.createPortalForUrl} path.
4448
- */
4449
- details;
4450
- constructor(client, id, targetUrl, details) {
4451
- this.#client = client;
4452
- this.crid = id;
4453
- this.targetUrl = targetUrl;
4454
- this.details = details;
4455
- }
4456
- /**
4457
- * Whether `resource` was produced by `client`. A static because the
4458
- * binding lives in a private field that only this class can read; used
4459
- * by {@link QURLClient.createPortal} to refuse handles bound to a
4460
- * different client.
4461
- */
4462
- static isBoundTo(resource, client) {
4463
- return resource.#client === client;
4464
- }
4465
- /**
4466
- * Mint a short-lived qURL link (a portal) for this resource. See
4467
- * {@link QURLClient.createPortal} for the option reference.
4468
- */
4469
- async createPortal(opts = {}, requestOptions) {
4470
- return this.#client.createPortal(this, opts, requestOptions);
4471
- }
4472
- }
4473
- exports.ProtectedResource = ProtectedResource;
4474
- const CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN = Symbol("validated ConnectorResource");
4475
- /**
4476
- * A validated qURL Connector resource bound to the client that loaded it.
4477
- *
4478
- * Resource identity, reverse-routing identity, and NHP admission identity are
4479
- * distinct server-issued values. Callers must consume each field verbatim and
4480
- * must never derive one from another.
4481
- */
4482
- class ConnectorResource {
4483
- #client;
4484
- resourcePublicKey;
4485
- /** Required public identifier, verified against the returned resource key. */
4486
- crid;
4487
- connectorRoutingId;
4488
- knockResourceId;
4489
- slug;
4490
- alias;
4491
- /** @internal Instances are returned by QURLClient after wire validation. */
4492
- constructor(client, details, validationToken) {
4493
- if (validationToken !== CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN) {
4494
- throw clientValidationError("ConnectorResource cannot be constructed directly; load it through QURLClient");
4495
- }
4496
- this.#client = client;
4497
- this.resourcePublicKey = details.resource_id;
4498
- this.crid = details.crid;
4499
- this.connectorRoutingId = details.connector_routing_id;
4500
- this.knockResourceId = details.knock_resource_id;
4501
- this.slug = details.slug;
4502
- this.alias = details.alias ?? undefined;
4503
- }
4504
- /** Mint a short-lived portal for this Connector resource. */
4505
- async createPortal(opts = {}, requestOptions) {
4506
- return this.#client.createPortal(this.crid, opts, requestOptions);
4507
- }
4508
- }
4509
- exports.ConnectorResource = ConnectorResource;
4510
- function sameConsumerPolicy(actual, expected, foundExisting) {
4511
- return (actual?.mode === expected.mode &&
4512
- (actual?.version === expected.version ||
4513
- (foundExisting &&
4514
- Number.isSafeInteger(actual?.version) &&
4515
- Number(actual?.version) > expected.version)) &&
4516
- actual?.contract_id === expected.contract_id);
4517
- }
2683
+ var client_management_js_2 = require("./client-management.js");
2684
+ Object.defineProperty(exports, "isApiKeyRequestScope", { enumerable: true, get: function () { return client_management_js_2.isApiKeyRequestScope; } });
2685
+ var client_portals_js_2 = require("./client-portals.js");
2686
+ Object.defineProperty(exports, "SIGNED_FRAGMENT_RE", { enumerable: true, get: function () { return client_portals_js_2.SIGNED_FRAGMENT_RE; } });
2687
+ var client_portals_js_3 = require("./client-portals.js");
2688
+ Object.defineProperty(exports, "ProtectedResource", { enumerable: true, get: function () { return client_portals_js_3.ProtectedResource; } });
2689
+ var client_connectors_js_2 = require("./client-connectors.js");
2690
+ Object.defineProperty(exports, "ConnectorResource", { enumerable: true, get: function () { return client_connectors_js_2.ConnectorResource; } });