@layervai/qurl 3.1.1 → 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
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireMaxLength = requireMaxLength;
4
+ exports.requireMaxUtf8Bytes = requireMaxUtf8Bytes;
5
+ exports.requireResourceNameIfPresent = requireResourceNameIfPresent;
6
+ exports.requireNonEmptyIfPresent = requireNonEmptyIfPresent;
7
+ exports.requireMaxSessionsInRange = requireMaxSessionsInRange;
8
+ exports.requireBooleanIfPresent = requireBooleanIfPresent;
9
+ exports.requireListLimitInRange = requireListLimitInRange;
10
+ exports.containsControlCharacter = containsControlCharacter;
11
+ exports.requireValidTags = requireValidTags;
12
+ exports.describeShape = describeShape;
13
+ exports.requireValidAccessPolicy = requireValidAccessPolicy;
14
+ exports.requireValidHttpUrl = requireValidHttpUrl;
15
+ exports.requireValidTargetUrl = requireValidTargetUrl;
16
+ exports.requireObjectInput = requireObjectInput;
17
+ exports.requireNonEmptyStringField = requireNonEmptyStringField;
18
+ exports.requireNonEmptyArrayField = requireNonEmptyArrayField;
19
+ exports.requireStringArrayElements = requireStringArrayElements;
20
+ exports.requireAtLeastOneField = requireAtLeastOneField;
21
+ exports.requireNoUnknownFields = requireNoUnknownFields;
22
+ exports.normalizePatchFields = normalizePatchFields;
23
+ exports.requireConsumerDecision = requireConsumerDecision;
24
+ exports.requirePortalTargetUrl = requirePortalTargetUrl;
25
+ const client_errors_js_1 = require("./client-errors.js");
26
+ const client_limits_js_1 = require("./client-limits.js");
27
+ // CreateQurlRequest.target_url pattern is loose (just a URI) but
28
+ // UpdateQurlRequest.tags pattern is specific — enforce it here.
29
+ const TAG_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9 _-]*$/;
30
+ function requireMaxLength(value, field, max) {
31
+ if (value === undefined)
32
+ return;
33
+ // Untyped-JS safety: a non-string `value` would silently pass since
34
+ // `(42).length === undefined > max` is `false` and the bad value would
35
+ // sail through to the wire. Match the typeof guard pattern used by
36
+ // requireValidTags / requireValidTargetUrl.
37
+ if (typeof value !== "string") {
38
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be a string (got ${value === null ? "null" : typeof value})`);
39
+ }
40
+ if (value.length > max) {
41
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be ${max} characters or fewer (got ${value.length})`);
42
+ }
43
+ }
44
+ function requireMaxUtf8Bytes(value, field, max) {
45
+ if (value === undefined)
46
+ return;
47
+ if (typeof value !== "string") {
48
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be a string (got ${value === null ? "null" : typeof value})`);
49
+ }
50
+ const bytes = client_errors_js_1.TEXT_ENCODER.encode(value).byteLength;
51
+ if (bytes > max) {
52
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be ${max} UTF-8 bytes or fewer (got ${bytes})`);
53
+ }
54
+ }
55
+ /** Mirror qurl-service ValidatePreviewName: 100 code points; no Cc, Cf, U+2028/9 or padding. */
56
+ function requireResourceNameIfPresent(value, field = "name") {
57
+ if (value === undefined)
58
+ return;
59
+ if (typeof value !== "string" || value.trim() === "") {
60
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be a non-empty string`);
61
+ }
62
+ if ([...value].length > client_limits_js_1.MAX_RESOURCE_NAME) {
63
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be ${client_limits_js_1.MAX_RESOURCE_NAME} characters or fewer`);
64
+ }
65
+ if (value.trim() !== value || /[\p{Cc}\p{Cf}\u2028\u2029]/u.test(value)) {
66
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be one line without hidden characters or surrounding whitespace`);
67
+ }
68
+ }
69
+ function requireNonEmptyIfPresent(value, field) {
70
+ // Reject `""` so uninitialized form state doesn't reach the wire.
71
+ if (value === "") {
72
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must not be an empty string`);
73
+ }
74
+ }
75
+ function requireMaxSessionsInRange(value, field = "max_sessions") {
76
+ if (value === undefined)
77
+ return;
78
+ if (!Number.isInteger(value) || value < 0 || value > client_limits_js_1.MAX_MAX_SESSIONS) {
79
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be an integer between 0 and ${client_limits_js_1.MAX_MAX_SESSIONS} (got ${value})`);
80
+ }
81
+ }
82
+ function requireBooleanIfPresent(value, field) {
83
+ if (value === undefined)
84
+ return;
85
+ if (typeof value !== "boolean") {
86
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be a boolean (got ${describeShape(value)})`);
87
+ }
88
+ }
89
+ function requireListLimitInRange(value, methodName) {
90
+ if (value === undefined || value === null)
91
+ return;
92
+ // All current paginated OpenAPI query params share 1..100. If a future
93
+ // endpoint diverges, add a per-endpoint bound rather than changing this
94
+ // shared guard silently.
95
+ if (typeof value !== "number" ||
96
+ !Number.isInteger(value) ||
97
+ value < 1 ||
98
+ value > client_limits_js_1.MAX_LIST_LIMIT) {
99
+ const rendered = typeof value === "string" ? JSON.stringify(value) : String(value);
100
+ throw (0, client_errors_js_1.clientValidationError)(`${methodName}: limit: must be an integer between 1 and ${client_limits_js_1.MAX_LIST_LIMIT} (got ${rendered})`);
101
+ }
102
+ }
103
+ function containsControlCharacter(value) {
104
+ return Array.from(value).some((character) => {
105
+ const codePoint = character.codePointAt(0);
106
+ return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);
107
+ });
108
+ }
109
+ /** Match the API's tag grammar, which permits embedded spaces; values stay in JSON bodies. */
110
+ function requireValidTags(tags) {
111
+ // null tolerance for untyped-JS callers — treated as "don't touch."
112
+ // Duplicates pass through to the server (authoritative on dedupe policy).
113
+ if (tags === undefined || tags === null)
114
+ return;
115
+ if (!Array.isArray(tags)) {
116
+ throw (0, client_errors_js_1.clientValidationError)(`tags: must be an array of strings (got ${typeof tags})`);
117
+ }
118
+ if (tags.length > client_limits_js_1.MAX_TAGS) {
119
+ throw (0, client_errors_js_1.clientValidationError)(`tags: max ${client_limits_js_1.MAX_TAGS} items allowed (got ${tags.length})`);
120
+ }
121
+ const errors = [];
122
+ for (let i = 0; i < tags.length; i++) {
123
+ const tag = tags[i];
124
+ // Per-element type guard for untyped-JS callers. Without this, a
125
+ // non-string element would silently pass (`.length` on a number is
126
+ // `undefined`; `TAG_PATTERN.test(42)` coerces to `"42"`) or TypeError
127
+ // (`null.length`).
128
+ if (typeof tag !== "string") {
129
+ errors.push(`tags[${i}]: must be a string (got ${tag === null ? "null" : typeof tag})`);
130
+ continue;
131
+ }
132
+ if (tag.length < 1 || tag.length > client_limits_js_1.MAX_TAG_LENGTH) {
133
+ errors.push(`tags[${i}]: must be 1-${client_limits_js_1.MAX_TAG_LENGTH} characters (got ${tag.length})`);
134
+ continue;
135
+ }
136
+ if (!TAG_PATTERN.test(tag)) {
137
+ errors.push(`tags[${i}]: must start with an alphanumeric and contain only letters, numbers, spaces, underscores, or hyphens`);
138
+ }
139
+ }
140
+ if (errors.length > 0) {
141
+ throw (0, client_errors_js_1.clientValidationError)(errors.join("; "));
142
+ }
143
+ }
144
+ function describeShape(value) {
145
+ if (value === null)
146
+ return "null";
147
+ if (Array.isArray(value))
148
+ return "array";
149
+ return typeof value;
150
+ }
151
+ const ACCESS_POLICY_LIST_FIELDS = [
152
+ "ip_allowlist",
153
+ "ip_denylist",
154
+ "geo_allowlist",
155
+ "geo_denylist",
156
+ ];
157
+ const ACCESS_POLICY_STRING_FIELDS = [
158
+ "user_agent_allow_regex",
159
+ "user_agent_deny_regex",
160
+ ];
161
+ const AI_AGENT_POLICY_LIST_FIELDS = [
162
+ "deny_categories",
163
+ "allow_categories",
164
+ ];
165
+ function requireValidAccessPolicy(policy) {
166
+ // null/undefined → "don't touch". Server is authoritative on CIDR /
167
+ // ISO-3166 / regex grammar; this guard catches only the shape errors
168
+ // an untyped-JS caller is likely to make (e.g. `geo_allowlist: "US"`
169
+ // instead of `["US"]`) so they get a structured ValidationError
170
+ // instead of a server 400 they have to debug.
171
+ if (policy === undefined || policy === null)
172
+ return;
173
+ if (typeof policy !== "object" || Array.isArray(policy)) {
174
+ throw (0, client_errors_js_1.clientValidationError)(`access_policy: must be an object (got ${describeShape(policy)})`);
175
+ }
176
+ for (const field of ACCESS_POLICY_LIST_FIELDS) {
177
+ const value = policy[field];
178
+ if (value === undefined)
179
+ continue;
180
+ if (!Array.isArray(value)) {
181
+ throw (0, client_errors_js_1.clientValidationError)(`access_policy.${field}: must be an array (got ${describeShape(value)})`);
182
+ }
183
+ }
184
+ for (const field of ACCESS_POLICY_STRING_FIELDS) {
185
+ const value = policy[field];
186
+ if (value === undefined)
187
+ continue;
188
+ if (typeof value !== "string") {
189
+ throw (0, client_errors_js_1.clientValidationError)(`access_policy.${field}: must be a string (got ${describeShape(value)})`);
190
+ }
191
+ }
192
+ const aip = policy.ai_agent_policy;
193
+ if (aip !== undefined) {
194
+ if (aip === null || typeof aip !== "object" || Array.isArray(aip)) {
195
+ throw (0, client_errors_js_1.clientValidationError)(`access_policy.ai_agent_policy: must be an object (got ${describeShape(aip)})`);
196
+ }
197
+ if (aip.block_all !== undefined && typeof aip.block_all !== "boolean") {
198
+ throw (0, client_errors_js_1.clientValidationError)(`access_policy.ai_agent_policy.block_all: must be a boolean (got ${describeShape(aip.block_all)})`);
199
+ }
200
+ for (const field of AI_AGENT_POLICY_LIST_FIELDS) {
201
+ const value = aip[field];
202
+ if (value === undefined)
203
+ continue;
204
+ if (!Array.isArray(value)) {
205
+ throw (0, client_errors_js_1.clientValidationError)(`access_policy.ai_agent_policy.${field}: must be an array (got ${describeShape(value)})`);
206
+ }
207
+ }
208
+ }
209
+ }
210
+ // `format: uri` in the OpenAPI spec allows schemes the SDK doesn't
211
+ // usefully support (`ftp://`, `file://`, `javascript:`, …). This is a
212
+ // cheap client-side sanity check — the server is still the
213
+ // authoritative validator (e.g. it rejects localhost, cloud metadata,
214
+ // and private-range hosts; the SDK doesn't need to duplicate that).
215
+ const ALLOWED_URL_SCHEMES = ["http://", "https://"];
216
+ function requireValidHttpUrl(value, field) {
217
+ // Three checks in priority order: type guard, scheme, length.
218
+ // Stop at the first failure so a non-string URL doesn't
219
+ // produce both "must be a string" AND "must be ≤ 2048 characters"
220
+ // (the length check on a non-string would also typeof-fail and
221
+ // duplicate the type message). The collect-all loop in
222
+ // validateCreateInput runs each FIELD's validator independently — within
223
+ // a single URL field itself we want fail-fast.
224
+ if (typeof value !== "string") {
225
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be a string (got ${value === null ? "null" : typeof value})`);
226
+ }
227
+ if (!ALLOWED_URL_SCHEMES.some((scheme) => value.startsWith(scheme))) {
228
+ // Truncate to keep the error message compact and avoid
229
+ // pathologically long schemes from filling logs.
230
+ const repr = JSON.stringify(value).slice(0, 40);
231
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must start with http:// or https:// (got ${repr})`);
232
+ }
233
+ if (value.length > client_limits_js_1.MAX_TARGET_URL) {
234
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must be ${client_limits_js_1.MAX_TARGET_URL} characters or fewer (got ${value.length})`);
235
+ }
236
+ }
237
+ function requireValidTargetUrl(target_url) {
238
+ requireValidHttpUrl(target_url, "target_url");
239
+ }
240
+ function requireObjectInput(input, method) {
241
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
242
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: input must be an object (got ${input === null ? "null" : typeof input})`);
243
+ }
244
+ }
245
+ function requireNonEmptyStringField(input, field, method) {
246
+ const value = input[field];
247
+ if (typeof value !== "string" || value.length === 0) {
248
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: ${field} must be a non-empty string`);
249
+ }
250
+ }
251
+ function requireNonEmptyArrayField(input, field, method) {
252
+ const value = input[field];
253
+ if (!Array.isArray(value) || value.length === 0) {
254
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: ${field} must be a non-empty array`);
255
+ }
256
+ }
257
+ function requireStringArrayElements(value, field, method) {
258
+ if (!Array.isArray(value)) {
259
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: ${field} must be a non-empty array`);
260
+ }
261
+ value.forEach((entry, index) => {
262
+ if (typeof entry !== "string" || entry.length === 0) {
263
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: ${field}[${index}] must be a non-empty string`);
264
+ }
265
+ });
266
+ }
267
+ function requireAtLeastOneField(input, fields, method) {
268
+ if (!fields.some((field) => input[field] !== undefined)) {
269
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: at least one field (${fields.join(", ")}) must be provided`);
270
+ }
271
+ }
272
+ function requireNoUnknownFields(input, fields, method) {
273
+ const unknown = Object.keys(input).filter((field) => !fields.includes(field));
274
+ if (unknown.length > 0) {
275
+ const rendered = unknown.map((field) => `"${field}"`).join(", ");
276
+ const label = unknown.length === 1 ? "unknown field" : "unknown fields";
277
+ throw (0, client_errors_js_1.clientValidationError)(`${method}: ${label} ${rendered}`);
278
+ }
279
+ }
280
+ function normalizePatchFields(input, fields, options = {}) {
281
+ const preserveNull = new Set(options.preserveNullFields ?? []);
282
+ const normalized = {};
283
+ for (const key of fields) {
284
+ const value = input[key];
285
+ if (value !== undefined && (value !== null || preserveNull.has(key))) {
286
+ normalized[key] = value;
287
+ }
288
+ }
289
+ return normalized;
290
+ }
291
+ function requireConsumerDecision(id, version, allowNew = false) {
292
+ if (typeof id !== "string" ||
293
+ (!/^[a-f0-9]{64}$/.test(id) && !(allowNew && id === "new")) ||
294
+ !Number.isSafeInteger(version) ||
295
+ version < (allowNew ? 0 : 1))
296
+ throw (0, client_errors_js_1.clientValidationError)("consumer decision: invalid id or expected version");
297
+ }
298
+ /**
299
+ * Portal-verb target URL validation (qurl-go `ProtectURL` parity). On top of
300
+ * the shared scheme/length check, the URL must carry a host and must parse,
301
+ * and embedded credentials (userinfo) are rejected. The added checks never
302
+ * echo the URL — a userinfo URL contains credentials that must stay out of
303
+ * logs and error messages. The REST-shaped compatibility layer keeps its
304
+ * original prefix-only check so its accepted inputs don't change.
305
+ */
306
+ function requirePortalTargetUrl(targetUrl) {
307
+ requireValidTargetUrl(targetUrl);
308
+ const url = targetUrl; // requireValidTargetUrl only returns for http(s) strings
309
+ // WHATWG URL normalizes away an empty authority ("https:///x" parses
310
+ // with host "x"), so the host-presence check runs on the raw string,
311
+ // mirroring qurl-go's url.Parse which rejects authority-less URLs.
312
+ const afterScheme = url.slice(url.indexOf("://") + 3);
313
+ if (afterScheme === "" || "/?#".includes(afterScheme[0])) {
314
+ throw (0, client_errors_js_1.clientValidationError)("target_url: must include a host");
315
+ }
316
+ let parsed;
317
+ try {
318
+ parsed = new URL(url);
319
+ }
320
+ catch {
321
+ throw (0, client_errors_js_1.clientValidationError)("target_url: malformed URL");
322
+ }
323
+ if (parsed.username !== "" || parsed.password !== "") {
324
+ throw (0, client_errors_js_1.clientValidationError)("target_url: must not include embedded credentials (userinfo)");
325
+ }
326
+ }
@@ -1,21 +1,15 @@
1
+ import type { AccessCodeListOutput, ApiKey, ApiKeyListOutput, BatchCreateInput, BatchCreateOutput, BillingInvoiceListOutput, CallerIdentity, CheckoutSession, ClientOptions, ConnectorInstallation, ConnectorInstallationListOutput, ConsumerAccessPolicy, ConsumerGrant, ConsumerGrantApprovalInput, ConsumerGrantListOutput, ConsumerInvitation, ConsumerInvitationListOutput, ConsumerListInput, CreateAccessCodeInput, CreateAccessCodeOutput, CreateApiKeyInput, CreateApiKeyOutput, CreateBillingCheckoutInput, CreateDelegatedQurlBatchInput, CreateDelegatedQurlBatchOptions, CreateExternalIdentityBindingInput, CreateExternalIdentityBindingOutput, CreateInput, CreateOutput, CreatePortalOptions, CreateQurlForResourceInput, CreateResourceInput, CreateWebhookInput, Customer, DelegatedQurl, DelegatedQurlBatchAccepted, Domain, DomainListOutput, DomainVerifyResult, ExtendInput, GetDelegatedQurlBatchOptions, GetDelegatedQurlBatchOutput, Invoice, ListApiKeysInput, ListBillingInvoicesInput, ListConnectorInstallationsInput, ListDomainsInput, ListInput, ListOutput, ListWebhookDeliveriesInput, ListWebhooksInput, MintInput, MintOutput, Portal, PortalSession, ProtectUrlOptions, QURL, Quota, QurlSummary, RedeemAccessCodeInput, RedeemAccessCodeOutput, RegisterDomainInput, RequestOptions, ResolveInput, ResolveOutput, Resource, ResourceDetail, ResourceHandle, ResourceListInput, ResourceListOutput, SessionListOutput, SessionTerminateOutput, ShareResourceOptions, UpdateApiKeyInput, UpdateCustomerInput, UpdateInput, UpdateResourceInput, UpdateResourceQurlInput, UpdateWebhookInput, UsageCurrentPeriod, UsageDaily, Webhook, WebhookDelivery, WebhookDeliveryListOutput, WebhookEventTypeInfo, WebhookListOutput, WebhookWithSecret } from "./types.js";
1
2
  import { ShareLink } from "./share.js";
2
- import type { ConsumerAccessPolicy, ConsumerInvitation, ConsumerGrant, ConsumerGrantApprovalInput, ConsumerListInput, ConsumerInvitationListOutput, ConsumerGrantListOutput, AccessCodeListOutput, ApiKey, ApiKeyListOutput, ApiKeyRequestScope, BillingInvoiceListOutput, BatchCreateInput, BatchCreateOutput, ClientOptions, ConnectorInstallation, ConnectorInstallationListOutput, CreateAccessCodeInput, CreateAccessCodeOutput, CreateApiKeyInput, CreateApiKeyOutput, CreateBillingCheckoutInput, CreateDelegatedQurlBatchInput, CreateDelegatedQurlBatchOptions, CreateInput, CreateOutput, CreatePortalOptions, CreateQurlForResourceInput, CreateResourceInput, CreateWebhookInput, Customer, CheckoutSession, Domain, DomainListOutput, DomainVerifyResult, DelegatedQurl, DelegatedQurlBatchAccepted, GetDelegatedQurlBatchOptions, GetDelegatedQurlBatchOutput, ExtendInput, Invoice, ListInput, ListOutput, ListApiKeysInput, ListBillingInvoicesInput, ListConnectorInstallationsInput, ListDomainsInput, ListWebhookDeliveriesInput, ListWebhooksInput, MintInput, MintOutput, Portal, PortalSession, ProtectUrlOptions, QURL, QurlSummary, Quota, RedeemAccessCodeInput, RedeemAccessCodeOutput, RegisterDomainInput, Resource, ResourceDetail, ResourceHandle, ResourceListInput, ResourceListOutput, RequestOptions, ResolveInput, ResolveOutput, SessionListOutput, SessionTerminateOutput, ShareResourceOptions, UpdateApiKeyInput, UpdateCustomerInput, UpdateInput, UpdateResourceInput, UpdateResourceQurlInput, UpdateWebhookInput, UsageCurrentPeriod, UsageDaily, Webhook, WebhookDelivery, WebhookDeliveryListOutput, WebhookEventTypeInfo, WebhookListOutput, WebhookWithSecret } from "./types.js";
3
- /** Narrow a response scope before reusing it in a create/update request. */
4
- export declare function isApiKeyRequestScope(scope: string): scope is ApiKeyRequestScope;
5
- /**
6
- * Shape of qurl-go's offline signed-fragment links: three or more
7
- * dot-separated base64url parts (<version>.<claims>.<secret>.<sig>).
8
- * Detected only to give those links a precise error in extractAccessToken.
9
- *
10
- * @internal Exported for in-package tests; not re-exported from index.ts.
11
- */
12
- export declare const SIGNED_FRAGMENT_RE: RegExp;
3
+ import { ProtectedResource } from "./client-portals.js";
4
+ import { ConnectorResource } from "./client-connectors.js";
5
+ import type { EnsureConnectorResourceResult } from "./client-connectors.js";
13
6
  /** qURL API client. */
14
7
  export declare class QURLClient {
15
8
  private readonly baseUrl;
16
9
  private readonly apiKey;
17
10
  private readonly fetchFn;
18
11
  private readonly maxRetries;
12
+ private readonly maxResponseBodyBytes;
19
13
  private readonly timeout;
20
14
  private readonly userAgent;
21
15
  private readonly debugFn;
@@ -68,7 +62,9 @@ export declare class QURLClient {
68
62
  * {@link ProtectedResource.createPortal}:
69
63
  *
70
64
  * ```ts
71
- * const resource = await client.protectUrl("https://internal.example.com/dashboard");
65
+ * const resource = await client.protectUrl("https://internal.example.com/dashboard", {
66
+ * name: "Team dashboard",
67
+ * });
72
68
  * const portal = await resource.createPortal({ validFor: "5m" });
73
69
  * share(portal.link);
74
70
  * ```
@@ -95,8 +91,18 @@ export declare class QURLClient {
95
91
  * need the full resource details.
96
92
  */
97
93
  resourceByCrid(id: string): ProtectedResource;
98
- /** Find or create the active qURL Connector resource for an immutable slug. */
99
- ensureConnectorResource(slug: string, requestOptions?: RequestOptions, consumerAccess?: ConsumerAccessPolicy): Promise<EnsureConnectorResourceResult>;
94
+ /**
95
+ * Find or create the active qURL Connector resource for an immutable slug.
96
+ *
97
+ * `name` is the public name recipients see. The service requires it when the
98
+ * slug is new (`400 name_required` otherwise) and ignores it on reuse.
99
+ *
100
+ * @param slug - Immutable owner-scoped Connector slug.
101
+ * @param requestOptions - Per-call request options.
102
+ * @param consumerAccess - Private consumer policy for a new resource.
103
+ * @param name - Public resource name; required when the slug is new.
104
+ */
105
+ ensureConnectorResource(slug: string, requestOptions?: RequestOptions, consumerAccess?: ConsumerAccessPolicy, name?: string): Promise<EnsureConnectorResourceResult>;
100
106
  /** Fetch a qURL Connector resource by its CRID. */
101
107
  getConnectorResource(crid: string): Promise<ConnectorResource>;
102
108
  /** Fetch the single active qURL Connector resource for an immutable slug. */
@@ -184,6 +190,8 @@ export declare class QURLClient {
184
190
  * populated `BatchCreateOutput` body in this case; whitelisted via
185
191
  * `passthroughStatuses` so the structured per-item errors aren't
186
192
  * swallowed by the generic error path.
193
+ * HTTP 400 is the documented all-item validation failure. HTTP 422 is
194
+ * not a batch passthrough status and follows normal error handling.
187
195
  *
188
196
  * Callers that only catch thrown errors will **silently miss partial
189
197
  * failures**. Always branch on `result.failed > 0` before treating the
@@ -336,6 +344,8 @@ export declare class QURLClient {
336
344
  * Accepts a plain token string or a `ResolveInput` object.
337
345
  */
338
346
  resolve(input: ResolveInput | string, options?: RequestOptions): Promise<ResolveOutput>;
347
+ /** Get the caller's account and credential identity. */
348
+ getIdentity(): Promise<CallerIdentity>;
339
349
  /** Get quota and usage information. */
340
350
  getQuota(): Promise<Quota>;
341
351
  /** List resources from the `/v1/resources` API. */
@@ -358,6 +368,15 @@ export declare class QURLClient {
358
368
  getResource(id: string): Promise<ResourceDetail>;
359
369
  /** Update resource metadata. */
360
370
  updateResource(id: string, input: UpdateResourceInput, options?: RequestOptions): Promise<Resource>;
371
+ /** Start Stripe Connect onboarding for this publisher. LayerV collects 3%. */
372
+ connectPublisherStripe(): Promise<{
373
+ url: string;
374
+ }>;
375
+ /** Read this publisher's connected account and the fixed application fee. */
376
+ publisherStripeAccount(): Promise<{
377
+ account_id: string;
378
+ layerv_fee_percent: number;
379
+ }>;
361
380
  /** Send a publisher-only invitation. Mail delivery does not prove enrollment. */
362
381
  inviteConsumer(id: string, email: string): Promise<ConsumerInvitation>;
363
382
  listConsumerInvitations(id: string, input?: ConsumerListInput): Promise<ConsumerInvitationListOutput>;
@@ -482,6 +501,13 @@ export declare class QURLClient {
482
501
  * iteration begins.
483
502
  */
484
503
  listAllWebhookDeliveries(id: string, input?: Omit<ListWebhookDeliveriesInput, "cursor">): AsyncGenerator<WebhookDelivery, void, undefined>;
504
+ /**
505
+ * Bind an external identity using the provider's trusted OAuth JWT as the
506
+ * client's bearer credential. Persist the request key before calling, then
507
+ * securely store the returned one-time plaintext key without logging it.
508
+ * Retry a lost response within 24 hours with the same key and body.
509
+ */
510
+ createExternalIdentityBinding(input: CreateExternalIdentityBindingInput, options: RequestOptions): Promise<CreateExternalIdentityBindingOutput>;
485
511
  /** Create a new API key. */
486
512
  createApiKey(input: CreateApiKeyInput, options?: RequestOptions): Promise<CreateApiKeyOutput>;
487
513
  /** List API keys. */
@@ -547,64 +573,8 @@ export declare class QURLClient {
547
573
  private classifyFetchError;
548
574
  private classifyResponseReadError;
549
575
  }
550
- /**
551
- * A LayerV-protected resource, bound to the client that produced it.
552
- *
553
- * Obtained from {@link QURLClient.protectUrl} or {@link QURLClient.resourceByCrid} —
554
- * not constructed directly. Mint short-lived access links for the resource
555
- * with {@link createPortal} (qurl-go: `qurl.Resource`).
556
- */
557
- export declare class ProtectedResource {
558
- #private;
559
- /** The public resource CRID. */
560
- readonly crid: string;
561
- /** The private URL protected by this resource, when known. */
562
- readonly targetUrl?: string;
563
- /**
564
- * Full server-populated {@link Resource} metadata when the handle came
565
- * from an API response; `undefined` for {@link QURLClient.resourceByCrid}
566
- * handles and for the one-call {@link QURLClient.createPortalForUrl} path.
567
- */
568
- readonly details?: Resource;
569
- constructor(client: QURLClient, id: string, targetUrl?: string, details?: Resource);
570
- /**
571
- * Whether `resource` was produced by `client`. A static because the
572
- * binding lives in a private field that only this class can read; used
573
- * by {@link QURLClient.createPortal} to refuse handles bound to a
574
- * different client.
575
- */
576
- static isBoundTo(resource: ProtectedResource, client: QURLClient): boolean;
577
- /**
578
- * Mint a short-lived qURL link (a portal) for this resource. See
579
- * {@link QURLClient.createPortal} for the option reference.
580
- */
581
- createPortal(opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
582
- }
583
- /** Result of idempotently ensuring a qURL Connector resource. */
584
- export interface EnsureConnectorResourceResult {
585
- resource: ConnectorResource;
586
- foundExisting: boolean;
587
- }
588
- declare const CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN: unique symbol;
589
- /**
590
- * A validated qURL Connector resource bound to the client that loaded it.
591
- *
592
- * Resource identity, reverse-routing identity, and NHP admission identity are
593
- * distinct server-issued values. Callers must consume each field verbatim and
594
- * must never derive one from another.
595
- */
596
- export declare class ConnectorResource {
597
- #private;
598
- readonly resourcePublicKey: string;
599
- /** Required public identifier, verified against the returned resource key. */
600
- readonly crid: string;
601
- readonly connectorRoutingId: string;
602
- readonly knockResourceId: string;
603
- readonly slug: string;
604
- readonly alias?: string;
605
- /** @internal Instances are returned by QURLClient after wire validation. */
606
- constructor(client: QURLClient, details: Resource, validationToken: typeof CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN);
607
- /** Mint a short-lived portal for this Connector resource. */
608
- createPortal(opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
609
- }
610
- export {};
576
+ export { isApiKeyRequestScope } from "./client-management.js";
577
+ export { SIGNED_FRAGMENT_RE } from "./client-portals.js";
578
+ export { ProtectedResource } from "./client-portals.js";
579
+ export type { EnsureConnectorResourceResult } from "./client-connectors.js";
580
+ export { ConnectorResource } from "./client-connectors.js";