@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.
- package/README.md +259 -46
- package/dist/cjs/client-connectors.d.ts +44 -0
- package/dist/cjs/client-connectors.js +189 -0
- package/dist/cjs/client-delegated.d.ts +30 -0
- package/dist/cjs/client-delegated.js +312 -0
- package/dist/cjs/client-errors.d.ts +44 -0
- package/dist/cjs/client-errors.js +219 -0
- package/dist/cjs/client-http-types.d.ts +37 -0
- package/dist/cjs/client-http-types.js +2 -0
- package/dist/cjs/client-input-fields.d.ts +53 -0
- package/dist/cjs/client-input-fields.js +262 -0
- package/dist/cjs/client-limits.d.ts +36 -0
- package/dist/cjs/client-limits.js +61 -0
- package/dist/cjs/client-management.d.ts +12 -0
- package/dist/cjs/client-management.js +119 -0
- package/dist/cjs/client-paths.d.ts +21 -0
- package/dist/cjs/client-paths.js +143 -0
- package/dist/cjs/client-portals.d.ts +82 -0
- package/dist/cjs/client-portals.js +244 -0
- package/dist/cjs/client-resources.d.ts +29 -0
- package/dist/cjs/client-resources.js +331 -0
- package/dist/cjs/client-validation.d.ts +35 -0
- package/dist/cjs/client-validation.js +326 -0
- package/dist/cjs/client.d.ts +45 -75
- package/dist/cjs/client.js +621 -2448
- package/dist/cjs/errors.d.ts +9 -1
- package/dist/cjs/errors.js +10 -1
- package/dist/cjs/index.d.ts +4 -2
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/response-metadata.d.ts +13 -0
- package/dist/cjs/response-metadata.js +38 -0
- package/dist/cjs/share.js +2 -0
- package/dist/cjs/target-path.d.ts +2 -0
- package/dist/cjs/target-path.js +34 -0
- package/dist/cjs/types.d.ts +83 -7
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client-connectors.d.ts +44 -0
- package/dist/esm/client-connectors.js +180 -0
- package/dist/esm/client-delegated.d.ts +30 -0
- package/dist/esm/client-delegated.js +295 -0
- package/dist/esm/client-errors.d.ts +44 -0
- package/dist/esm/client-errors.js +206 -0
- package/dist/esm/client-http-types.d.ts +37 -0
- package/dist/esm/client-http-types.js +1 -0
- package/dist/esm/client-input-fields.d.ts +53 -0
- package/dist/esm/client-input-fields.js +258 -0
- package/dist/esm/client-limits.d.ts +36 -0
- package/dist/esm/client-limits.js +58 -0
- package/dist/esm/client-management.d.ts +12 -0
- package/dist/esm/client-management.js +113 -0
- package/dist/esm/client-paths.d.ts +21 -0
- package/dist/esm/client-paths.js +138 -0
- package/dist/esm/client-portals.d.ts +82 -0
- package/dist/esm/client-portals.js +236 -0
- package/dist/esm/client-resources.d.ts +29 -0
- package/dist/esm/client-resources.js +323 -0
- package/dist/esm/client-validation.d.ts +35 -0
- package/dist/esm/client-validation.js +302 -0
- package/dist/esm/client.d.ts +45 -76
- package/dist/esm/client.js +318 -2147
- package/dist/esm/crid.d.ts +0 -1
- package/dist/esm/crid.js +0 -1
- package/dist/esm/errors.d.ts +9 -2
- package/dist/esm/errors.js +9 -1
- package/dist/esm/index.d.ts +4 -3
- package/dist/esm/index.js +2 -2
- package/dist/esm/internal.d.ts +0 -1
- package/dist/esm/internal.js +0 -1
- package/dist/esm/node/agent-runtime.d.ts +0 -1
- package/dist/esm/node/agent-runtime.js +0 -1
- package/dist/esm/node/agent-state.d.ts +0 -1
- package/dist/esm/node/agent-state.js +0 -1
- package/dist/esm/node/agent-transport.d.ts +0 -1
- package/dist/esm/node/agent-transport.js +0 -1
- package/dist/esm/node/deployment.d.ts +0 -1
- package/dist/esm/node/deployment.js +0 -1
- package/dist/esm/node/file-agent-state.d.ts +0 -1
- package/dist/esm/node/file-agent-state.js +0 -1
- package/dist/esm/node/native-udp.d.ts +0 -1
- package/dist/esm/node/native-udp.js +0 -1
- package/dist/esm/node/nhp-wire.d.ts +0 -1
- package/dist/esm/node/nhp-wire.js +0 -1
- package/dist/esm/node/portal-opener.d.ts +0 -1
- package/dist/esm/node/portal-opener.js +0 -1
- package/dist/esm/node/provider.d.ts +0 -1
- package/dist/esm/node/provider.js +0 -1
- package/dist/esm/node/qv2.d.ts +0 -1
- package/dist/esm/node/qv2.js +0 -1
- package/dist/esm/node/relay.d.ts +0 -1
- package/dist/esm/node/relay.js +0 -1
- package/dist/esm/node/sealed-agent-state.d.ts +0 -1
- package/dist/esm/node/sealed-agent-state.js +0 -1
- package/dist/esm/node/strict-json.d.ts +0 -1
- package/dist/esm/node/strict-json.js +0 -1
- package/dist/esm/node.d.ts +0 -1
- package/dist/esm/node.js +0 -1
- package/dist/esm/response-metadata.d.ts +13 -0
- package/dist/esm/response-metadata.js +33 -0
- package/dist/esm/share.d.ts +0 -1
- package/dist/esm/share.js +2 -1
- package/dist/esm/target-path.d.ts +2 -0
- package/dist/esm/target-path.js +30 -0
- package/dist/esm/types.d.ts +83 -8
- package/dist/esm/types.js +0 -1
- package/dist/esm/version.d.ts +1 -2
- package/dist/esm/version.js +1 -2
- package/package.json +9 -7
- package/dist/esm/client.d.ts.map +0 -1
- package/dist/esm/client.js.map +0 -1
- package/dist/esm/crid.d.ts.map +0 -1
- package/dist/esm/crid.js.map +0 -1
- package/dist/esm/errors.d.ts.map +0 -1
- package/dist/esm/errors.js.map +0 -1
- package/dist/esm/index.d.ts.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/internal.d.ts.map +0 -1
- package/dist/esm/internal.js.map +0 -1
- package/dist/esm/node/agent-runtime.d.ts.map +0 -1
- package/dist/esm/node/agent-runtime.js.map +0 -1
- package/dist/esm/node/agent-state.d.ts.map +0 -1
- package/dist/esm/node/agent-state.js.map +0 -1
- package/dist/esm/node/agent-transport.d.ts.map +0 -1
- package/dist/esm/node/agent-transport.js.map +0 -1
- package/dist/esm/node/deployment.d.ts.map +0 -1
- package/dist/esm/node/deployment.js.map +0 -1
- package/dist/esm/node/file-agent-state.d.ts.map +0 -1
- package/dist/esm/node/file-agent-state.js.map +0 -1
- package/dist/esm/node/native-udp.d.ts.map +0 -1
- package/dist/esm/node/native-udp.js.map +0 -1
- package/dist/esm/node/nhp-wire.d.ts.map +0 -1
- package/dist/esm/node/nhp-wire.js.map +0 -1
- package/dist/esm/node/portal-opener.d.ts.map +0 -1
- package/dist/esm/node/portal-opener.js.map +0 -1
- package/dist/esm/node/provider.d.ts.map +0 -1
- package/dist/esm/node/provider.js.map +0 -1
- package/dist/esm/node/qv2.d.ts.map +0 -1
- package/dist/esm/node/qv2.js.map +0 -1
- package/dist/esm/node/relay.d.ts.map +0 -1
- package/dist/esm/node/relay.js.map +0 -1
- package/dist/esm/node/sealed-agent-state.d.ts.map +0 -1
- package/dist/esm/node/sealed-agent-state.js.map +0 -1
- package/dist/esm/node/strict-json.d.ts.map +0 -1
- package/dist/esm/node/strict-json.js.map +0 -1
- package/dist/esm/node.d.ts.map +0 -1
- package/dist/esm/node.js.map +0 -1
- package/dist/esm/share.d.ts.map +0 -1
- package/dist/esm/share.js.map +0 -1
- package/dist/esm/types.d.ts.map +0 -1
- package/dist/esm/types.js.map +0 -1
- package/dist/esm/version.d.ts.map +0 -1
- 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
|
+
}
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -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
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
/**
|
|
99
|
-
|
|
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
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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";
|