@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
@@ -0,0 +1,331 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateCreateInput = validateCreateInput;
4
+ exports.validateQurlTokenOptions = validateQurlTokenOptions;
5
+ exports.validateResourceQurlUpdateInput = validateResourceQurlUpdateInput;
6
+ exports.validateResourceWriteFields = validateResourceWriteFields;
7
+ exports.batchItemResultValidationReason = batchItemResultValidationReason;
8
+ exports.sameConsumerPolicy = sameConsumerPolicy;
9
+ const target_path_js_1 = require("./target-path.js");
10
+ const errors_js_1 = require("./errors.js");
11
+ const client_errors_js_1 = require("./client-errors.js");
12
+ const client_validation_js_1 = require("./client-validation.js");
13
+ const client_input_fields_js_1 = require("./client-input-fields.js");
14
+ const client_limits_js_1 = require("./client-limits.js");
15
+ function validateCreateInput(input) {
16
+ // Untyped-JS guard: surface as ValidationError instead of raw TypeError.
17
+ if (typeof input !== "object" || input === null) {
18
+ throw (0, client_errors_js_1.clientValidationError)(`create: input must be an object (got ${input === null ? "null" : typeof input})`);
19
+ }
20
+ // Collect-all: run every validator and aggregate the messages so a
21
+ // caller fixing multiple bad fields sees them all at once instead
22
+ // of fix-re-run-repeat. Matches the documented batchCreate UX
23
+ // ("All failures are collected into a single ValidationError")
24
+ // and mirrors the requireValidTags collect-all pattern.
25
+ const errors = [];
26
+ const collect = (fn) => {
27
+ try {
28
+ fn();
29
+ }
30
+ catch (err) {
31
+ if (err instanceof errors_js_1.ValidationError) {
32
+ errors.push(err.detail);
33
+ return;
34
+ }
35
+ throw err;
36
+ }
37
+ };
38
+ collect(() => (0, client_validation_js_1.requireNoUnknownFields)(input, client_input_fields_js_1.CREATE_FIELD_KEYS, "create"));
39
+ const resourceType = input.type;
40
+ if (resourceType !== undefined && typeof resourceType !== "string") {
41
+ errors.push(`type: must be a string (got ${resourceType === null ? "null" : typeof resourceType})`);
42
+ }
43
+ collect(() => {
44
+ if (input.target_url === undefined) {
45
+ if (resourceType === undefined || resourceType === "url") {
46
+ throw (0, client_errors_js_1.clientValidationError)("target_url: is required for url qURLs");
47
+ }
48
+ return;
49
+ }
50
+ (0, client_validation_js_1.requireValidTargetUrl)(input.target_url);
51
+ });
52
+ collect(() => (0, client_validation_js_1.requireResourceNameIfPresent)(input.name));
53
+ collect(() => (0, client_validation_js_1.requireMaxLength)(input.label, "label", client_limits_js_1.MAX_LABEL));
54
+ collect(() => (0, client_validation_js_1.requireNonEmptyIfPresent)(input.label, "label"));
55
+ collect(() => (0, client_validation_js_1.requireMaxLength)(input.custom_domain, "custom_domain", client_limits_js_1.MAX_CUSTOM_DOMAIN));
56
+ collect(() => (0, client_validation_js_1.requireNonEmptyIfPresent)(input.custom_domain, "custom_domain"));
57
+ collect(() => (0, client_validation_js_1.requireBooleanIfPresent)(input.private, "private"));
58
+ collect(() => (0, client_validation_js_1.requireBooleanIfPresent)(input.one_time_use, "one_time_use"));
59
+ collect(() => (0, client_validation_js_1.requireMaxSessionsInRange)(input.max_sessions));
60
+ collect(() => (0, client_validation_js_1.requireNonEmptyIfPresent)(input.expires_in, "expires_in"));
61
+ collect(() => (0, client_validation_js_1.requireNonEmptyIfPresent)(input.session_duration, "session_duration"));
62
+ collect(() => (0, client_validation_js_1.requireValidAccessPolicy)(input.access_policy));
63
+ if (errors.length > 0) {
64
+ // Inner `; ` is contractual (paired with batchCreate's outer ` | `).
65
+ // Future field-level validators must not aggregate via `; ` internally.
66
+ throw (0, client_errors_js_1.clientValidationError)(errors.join("; "));
67
+ }
68
+ // Intentional omission: duration *grammar* (`expires_in`,
69
+ // `session_duration`) is server-authoritative. Empty strings are
70
+ // rejected up front (see collect() calls above) but unit/whitespace
71
+ // edge cases ("24hh", "1w 3d") surface as a clean server 400 — a
72
+ // client-side regex would just create a drift surface against the
73
+ // server's rego/duration parser.
74
+ //
75
+ // `access_policy` contents (CIDRs, ISO-3166 codes, regex patterns)
76
+ // are pass-through for the same reason. The shape-guard above only
77
+ // catches structural mistakes (string-instead-of-array on list fields)
78
+ // an untyped-JS caller is likely to make.
79
+ }
80
+ function validateQurlTokenOptions(input) {
81
+ if (input === undefined)
82
+ return;
83
+ (0, client_validation_js_1.requireMaxLength)(input.label, "label", client_limits_js_1.MAX_LABEL);
84
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.label, "label");
85
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.expires_in, "expires_in");
86
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.session_duration, "session_duration");
87
+ (0, target_path_js_1.requireCanonicalTargetPath)(input.target_path, "target_path");
88
+ (0, client_validation_js_1.requireBooleanIfPresent)(input.one_time_use, "one_time_use");
89
+ (0, client_validation_js_1.requireMaxSessionsInRange)(input.max_sessions);
90
+ (0, client_validation_js_1.requireValidAccessPolicy)(input.access_policy);
91
+ }
92
+ function validateResourceQurlUpdateInput(input) {
93
+ (0, client_validation_js_1.requireMaxLength)(input.label, "label", client_limits_js_1.MAX_LABEL);
94
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.label, "label");
95
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.extend_by, "extend_by");
96
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.expires_at, "expires_at");
97
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.session_duration, "session_duration");
98
+ (0, client_validation_js_1.requireMaxSessionsInRange)(input.max_sessions);
99
+ (0, client_validation_js_1.requireValidAccessPolicy)(input.access_policy);
100
+ if (input.extend_by !== undefined && input.expires_at !== undefined) {
101
+ throw (0, client_errors_js_1.clientValidationError)("updateResourceQurl: `extend_by` and `expires_at` are mutually exclusive — provide at most one");
102
+ }
103
+ }
104
+ function validateResourceWriteFields(input, options = {}) {
105
+ const errors = [];
106
+ const collect = (fn) => {
107
+ try {
108
+ fn();
109
+ }
110
+ catch (err) {
111
+ if (err instanceof errors_js_1.ValidationError) {
112
+ errors.push(err.detail);
113
+ return;
114
+ }
115
+ throw err;
116
+ }
117
+ };
118
+ collect(() => (0, client_validation_js_1.requireResourceNameIfPresent)(input.name));
119
+ collect(() => {
120
+ if (input.private !== undefined && typeof input.private !== "boolean") {
121
+ throw (0, client_errors_js_1.clientValidationError)("private: must be a boolean");
122
+ }
123
+ if (input.consumer_access !== undefined) {
124
+ const policy = input.consumer_access;
125
+ (0, client_validation_js_1.requireObjectInput)(policy, "consumer_access");
126
+ const p = policy;
127
+ (0, client_validation_js_1.requireNoUnknownFields)(p, ["mode", "version", "contract_id", "payment"], "consumer_access");
128
+ if (!["keys", "invite_only", "email_capture", "review", "contract"].includes(p.mode) ||
129
+ !Number.isSafeInteger(p.version) ||
130
+ p.version < 1) {
131
+ throw (0, client_errors_js_1.clientValidationError)("consumer_access: invalid mode or version");
132
+ }
133
+ if (p.mode === "contract") {
134
+ if (typeof p.contract_id !== "string" ||
135
+ !p.contract_id.trim() ||
136
+ client_errors_js_1.TEXT_ENCODER.encode(p.contract_id).length > client_limits_js_1.MAX_CONSUMER_CONTRACT_ID_BYTES) {
137
+ throw (0, client_errors_js_1.clientValidationError)("consumer_access: contract mode requires a bounded contract_id");
138
+ }
139
+ }
140
+ else if (p.contract_id !== undefined) {
141
+ throw (0, client_errors_js_1.clientValidationError)("consumer_access: contract_id requires contract mode");
142
+ }
143
+ if (p.payment !== undefined) {
144
+ (0, client_validation_js_1.requireObjectInput)(p.payment, "consumer_access.payment");
145
+ const payment = p.payment;
146
+ (0, client_validation_js_1.requireNoUnknownFields)(payment, ["account_id", "price_id", "access_seconds"], "consumer_access.payment");
147
+ if (p.mode !== "contract" ||
148
+ typeof payment.account_id !== "string" ||
149
+ !/^acct_[A-Za-z0-9]{1,128}$/.test(payment.account_id) ||
150
+ typeof payment.price_id !== "string" ||
151
+ !/^price_[A-Za-z0-9]{1,128}$/.test(payment.price_id) ||
152
+ !Number.isSafeInteger(payment.access_seconds) ||
153
+ Number(payment.access_seconds) < 0 ||
154
+ Number(payment.access_seconds) > 315360000)
155
+ throw (0, client_errors_js_1.clientValidationError)("consumer_access.payment: invalid Stripe contract");
156
+ }
157
+ if (input.private === false)
158
+ throw (0, client_errors_js_1.clientValidationError)("consumer_access: requires a private resource");
159
+ if (options.requireUrlTarget) {
160
+ if (p.version !== 1 || input.private !== true)
161
+ throw (0, client_errors_js_1.clientValidationError)("consumer_access: creation requires private true and version one");
162
+ }
163
+ else if (options.allowAliasClear) {
164
+ const expected = input.expected_consumer_access_version;
165
+ if (!Number.isSafeInteger(expected) ||
166
+ expected < 0 ||
167
+ p.version !== expected + 1 ||
168
+ Object.keys(input).some((key) => key !== "consumer_access" && key !== "expected_consumer_access_version")) {
169
+ throw (0, client_errors_js_1.clientValidationError)("consumer_access: update requires its expected version and no metadata changes");
170
+ }
171
+ }
172
+ }
173
+ else if (input.expected_consumer_access_version !== undefined) {
174
+ throw (0, client_errors_js_1.clientValidationError)("expected_consumer_access_version: requires consumer_access");
175
+ }
176
+ });
177
+ collect(() => {
178
+ const fields = ["allowed_device_keys_add", "allowed_device_keys_remove"];
179
+ for (const field of fields) {
180
+ const keys = input[field];
181
+ if (keys === undefined)
182
+ continue;
183
+ (0, client_validation_js_1.requireStringArrayElements)(keys, field, "updateResource");
184
+ if (keys.length > client_limits_js_1.MAX_ALLOWED_DEVICE_KEYS || new Set(keys).size !== keys.length) {
185
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: must contain at most ${client_limits_js_1.MAX_ALLOWED_DEVICE_KEYS} unique keys`);
186
+ }
187
+ for (const key of keys) {
188
+ let canonical = false;
189
+ try {
190
+ canonical = key.length === 44 && atob(key).length === 32 && btoa(atob(key)) === key;
191
+ }
192
+ catch {
193
+ /* Invalid base64 is rejected below. */
194
+ }
195
+ if (!canonical) {
196
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: keys must be canonical padded base64 of 32 bytes`);
197
+ }
198
+ }
199
+ if (input.custom_domain !== undefined) {
200
+ throw (0, client_errors_js_1.clientValidationError)(`${field}: update separately from custom_domain`);
201
+ }
202
+ }
203
+ const added = new Set(input.allowed_device_keys_add);
204
+ if (input.allowed_device_keys_remove?.some((key) => added.has(key))) {
205
+ throw (0, client_errors_js_1.clientValidationError)("allowed device key additions and removals must not overlap");
206
+ }
207
+ });
208
+ const resourceType = input.type;
209
+ if (resourceType !== undefined && typeof resourceType !== "string") {
210
+ errors.push(`type: must be a string (got ${resourceType === null ? "null" : typeof resourceType})`);
211
+ }
212
+ collect(() => {
213
+ if (input.target_url === undefined) {
214
+ if (options.requireUrlTarget && (resourceType === undefined || resourceType === "url")) {
215
+ throw (0, client_errors_js_1.clientValidationError)("target_url: is required for url resources");
216
+ }
217
+ return;
218
+ }
219
+ (0, client_validation_js_1.requireValidTargetUrl)(input.target_url);
220
+ });
221
+ collect(() => (0, client_validation_js_1.requireMaxLength)(input.description, "description", client_limits_js_1.MAX_DESCRIPTION));
222
+ collect(() => (0, client_validation_js_1.requireMaxLength)(input.custom_domain, "custom_domain", client_limits_js_1.MAX_CUSTOM_DOMAIN));
223
+ collect(() => {
224
+ if (options.allowCustomDomainClear && input.custom_domain === "")
225
+ return;
226
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.custom_domain, "custom_domain");
227
+ });
228
+ collect(() => (0, client_validation_js_1.requireValidTags)(input.tags));
229
+ collect(() => {
230
+ if (input.alias !== undefined) {
231
+ if (input.alias === null) {
232
+ if (!options.allowAliasClear) {
233
+ throw (0, client_errors_js_1.clientValidationError)("alias: must be a string (got null)");
234
+ }
235
+ }
236
+ else {
237
+ (0, client_validation_js_1.requireMaxLength)(input.alias, "alias", client_limits_js_1.MAX_ALIAS);
238
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.alias, "alias");
239
+ }
240
+ }
241
+ });
242
+ collect(() => {
243
+ if (input.slug === undefined)
244
+ return;
245
+ (0, client_validation_js_1.requireMaxLength)(input.slug, "slug", client_limits_js_1.MAX_SLUG);
246
+ (0, client_validation_js_1.requireNonEmptyIfPresent)(input.slug, "slug");
247
+ });
248
+ collect(() => {
249
+ if (options.validateFindOrCreate &&
250
+ input.find_or_create !== undefined &&
251
+ typeof input.find_or_create !== "boolean") {
252
+ throw (0, client_errors_js_1.clientValidationError)(`find_or_create: must be a boolean (got ${(0, client_validation_js_1.describeShape)(input.find_or_create)})`);
253
+ }
254
+ });
255
+ collect(() => {
256
+ if (options.validatePreserveHost &&
257
+ input.preserve_host !== undefined &&
258
+ typeof input.preserve_host !== "boolean") {
259
+ throw (0, client_errors_js_1.clientValidationError)(`preserve_host: must be a boolean (got ${(0, client_validation_js_1.describeShape)(input.preserve_host)})`);
260
+ }
261
+ });
262
+ if (errors.length > 0) {
263
+ throw (0, client_errors_js_1.clientValidationError)(errors.join("; "));
264
+ }
265
+ }
266
+ /**
267
+ * Per-entry shape guard for {@link BatchItemResult}. Verifies every
268
+ * non-optional field on the branch of the discriminated union selected
269
+ * by the `success` discriminant. Protects consumers who narrow on
270
+ * `success` and then access `resource_id` / `qurl_link` / `error` as
271
+ * guaranteed-present — if the API ever omits one, we trip the guard
272
+ * rather than returning `undefined` where the type says `string`.
273
+ *
274
+ * Returns `null` when the entry is valid, or a human-readable reason
275
+ * string describing the *specific* field that's missing or wrong. The
276
+ * reason never echoes the entry's values — only field names and shape
277
+ * descriptors — so the caller can safely surface it in error messages
278
+ * that may end up in observability pipelines. Contrast with the outer
279
+ * guard's intentional silence about response-body content for the
280
+ * same info-leak reason.
281
+ */
282
+ function batchItemResultValidationReason(entry) {
283
+ if (!entry || typeof entry !== "object")
284
+ return "not an object";
285
+ const e = entry;
286
+ // `Number.isInteger` rejects NaN, Infinity, and floats; combined
287
+ // with `>= 0`, the check rules out pathological proxy bodies that
288
+ // would attribute to a useless index. `index` is informational, so
289
+ // this isn't a correctness hazard, but tightening the constraint
290
+ // matches the same posture as the count integer guards above.
291
+ if (!Number.isInteger(e.index) || e.index < 0) {
292
+ return "missing/invalid 'index' (expected non-negative integer)";
293
+ }
294
+ if (e.success === true) {
295
+ if (typeof e.resource_id !== "string") {
296
+ return "success-branch missing required field 'resource_id' (expected string)";
297
+ }
298
+ if (typeof e.qurl_link !== "string") {
299
+ return "success-branch missing required field 'qurl_link' (expected string)";
300
+ }
301
+ if (typeof e.qurl_site !== "string") {
302
+ return "success-branch missing required field 'qurl_site' (expected string)";
303
+ }
304
+ return null;
305
+ }
306
+ if (e.success === false) {
307
+ if (!e.error || typeof e.error !== "object") {
308
+ return "failure-branch missing required field 'error' (expected object)";
309
+ }
310
+ const err = e.error;
311
+ if (typeof err.code !== "string") {
312
+ return "failure-branch missing required field 'error.code' (expected string)";
313
+ }
314
+ if (typeof err.message !== "string") {
315
+ return "failure-branch missing required field 'error.message' (expected string)";
316
+ }
317
+ return null;
318
+ }
319
+ return "missing/invalid 'success' discriminant (expected boolean)";
320
+ }
321
+ function sameConsumerPolicy(actual, expected, foundExisting) {
322
+ return (actual?.mode === expected.mode &&
323
+ (actual?.version === expected.version ||
324
+ (foundExisting &&
325
+ Number.isSafeInteger(actual?.version) &&
326
+ Number(actual?.version) > expected.version)) &&
327
+ actual?.contract_id === expected.contract_id &&
328
+ actual?.payment?.account_id === expected.payment?.account_id &&
329
+ actual?.payment?.price_id === expected.payment?.price_id &&
330
+ actual?.payment?.access_seconds === expected.payment?.access_seconds);
331
+ }
@@ -0,0 +1,35 @@
1
+ import type { AccessPolicy } from "./types.js";
2
+ export declare function requireMaxLength(value: string | undefined, field: string, max: number): void;
3
+ export declare function requireMaxUtf8Bytes(value: string | undefined, field: string, max: number): void;
4
+ /** Mirror qurl-service ValidatePreviewName: 100 code points; no Cc, Cf, U+2028/9 or padding. */
5
+ export declare function requireResourceNameIfPresent(value: unknown, field?: string): void;
6
+ export declare function requireNonEmptyIfPresent(value: unknown, field: string): void;
7
+ export declare function requireMaxSessionsInRange(value: number | undefined, field?: string): void;
8
+ export declare function requireBooleanIfPresent(value: unknown, field: string): void;
9
+ export declare function requireListLimitInRange(value: unknown, methodName: string): void;
10
+ export declare function containsControlCharacter(value: string): boolean;
11
+ /** Match the API's tag grammar, which permits embedded spaces; values stay in JSON bodies. */
12
+ export declare function requireValidTags(tags: string[] | null | undefined): void;
13
+ export declare function describeShape(value: unknown): string;
14
+ export declare function requireValidAccessPolicy(policy: AccessPolicy | null | undefined): void;
15
+ export declare function requireValidHttpUrl(value: unknown, field: string): void;
16
+ export declare function requireValidTargetUrl(target_url: unknown): void;
17
+ export declare function requireObjectInput(input: unknown, method: string): asserts input is Record<string, unknown>;
18
+ export declare function requireNonEmptyStringField(input: Record<string, unknown>, field: string, method: string): void;
19
+ export declare function requireNonEmptyArrayField(input: Record<string, unknown>, field: string, method: string): void;
20
+ export declare function requireStringArrayElements(value: unknown, field: string, method: string): asserts value is string[];
21
+ export declare function requireAtLeastOneField(input: Record<string, unknown>, fields: readonly string[], method: string): void;
22
+ export declare function requireNoUnknownFields(input: Record<string, unknown>, fields: readonly string[], method: string): void;
23
+ export declare function normalizePatchFields(input: Record<string, unknown>, fields: readonly string[], options?: {
24
+ preserveNullFields?: readonly string[];
25
+ }): Record<string, unknown>;
26
+ export declare function requireConsumerDecision(id: string, version: number, allowNew?: boolean): void;
27
+ /**
28
+ * Portal-verb target URL validation (qurl-go `ProtectURL` parity). On top of
29
+ * the shared scheme/length check, the URL must carry a host and must parse,
30
+ * and embedded credentials (userinfo) are rejected. The added checks never
31
+ * echo the URL — a userinfo URL contains credentials that must stay out of
32
+ * logs and error messages. The REST-shaped compatibility layer keeps its
33
+ * original prefix-only check so its accepted inputs don't change.
34
+ */
35
+ export declare function requirePortalTargetUrl(targetUrl: unknown): void;