@layervai/qurl 0.4.0 → 0.6.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 +174 -61
- package/dist/cjs/client.d.ts +46 -20
- package/dist/cjs/client.js +338 -54
- package/dist/cjs/errors.d.ts +24 -3
- package/dist/cjs/errors.js +34 -4
- package/dist/cjs/index.d.ts +3 -2
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/node/deployment.d.ts +1 -1
- package/dist/cjs/node/deployment.js +2 -2
- package/dist/cjs/node/native-udp.d.ts +2 -4
- package/dist/cjs/node/native-udp.js +8 -8
- package/dist/cjs/node/portal-opener.d.ts +54 -23
- package/dist/cjs/node/portal-opener.js +643 -340
- package/dist/cjs/node.d.ts +2 -2
- package/dist/cjs/node.js +8 -1
- package/dist/cjs/types.d.ts +18 -2
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +46 -20
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +337 -54
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/errors.d.ts +24 -3
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +32 -3
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node/deployment.d.ts +1 -1
- package/dist/esm/node/deployment.d.ts.map +1 -1
- package/dist/esm/node/deployment.js +2 -2
- package/dist/esm/node/deployment.js.map +1 -1
- package/dist/esm/node/native-udp.d.ts +2 -4
- package/dist/esm/node/native-udp.d.ts.map +1 -1
- package/dist/esm/node/native-udp.js +5 -5
- package/dist/esm/node/native-udp.js.map +1 -1
- package/dist/esm/node/portal-opener.d.ts +54 -23
- package/dist/esm/node/portal-opener.d.ts.map +1 -1
- package/dist/esm/node/portal-opener.js +636 -340
- package/dist/esm/node/portal-opener.js.map +1 -1
- package/dist/esm/node.d.ts +2 -2
- package/dist/esm/node.d.ts.map +1 -1
- package/dist/esm/node.js +1 -1
- package/dist/esm/node.js.map +1 -1
- package/dist/esm/types.d.ts +18 -2
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -39,7 +39,9 @@ endpoint in front of the same service:
|
|
|
39
39
|
npm install @layervai/qurl
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
Requires Node.js
|
|
42
|
+
Requires Node.js 22.12+ and has **no runtime dependencies**. Both
|
|
43
|
+
`import { QURLClient } from '@layervai/qurl'` (ESM) and
|
|
44
|
+
`const { QURLClient } = require('@layervai/qurl')` (CJS) work.
|
|
43
45
|
|
|
44
46
|
## Quickstart
|
|
45
47
|
|
|
@@ -66,14 +68,60 @@ same URL again returns the existing resource. `validFor` accepts a duration
|
|
|
66
68
|
string (`'5m'`, `'24h'`) or a number of milliseconds (whole seconds, at least
|
|
67
69
|
one minute); prefer short portal lifetimes.
|
|
68
70
|
|
|
69
|
-
If qURL Connector
|
|
70
|
-
of calling `protectUrl`:
|
|
71
|
+
If qURL Connector protects the service, address its management-plane resource
|
|
72
|
+
by immutable slug instead of calling `protectUrl`:
|
|
71
73
|
|
|
72
74
|
```typescript
|
|
73
|
-
const resource = await client.
|
|
74
|
-
|
|
75
|
+
const { resource, foundExisting } = await client.ensureConnectorResource(
|
|
76
|
+
'prod-dashboard',
|
|
77
|
+
{ idempotencyKey: 'connector-bootstrap-prod-dashboard' },
|
|
78
|
+
);
|
|
79
|
+
console.log(foundExisting ? 'Using existing connector resource' : 'Created connector resource');
|
|
80
|
+
const portal = await resource.createPortal({
|
|
81
|
+
validFor: '5m',
|
|
82
|
+
targetPath: '/api/detect/eib_example',
|
|
83
|
+
});
|
|
75
84
|
```
|
|
76
85
|
|
|
86
|
+
`targetPath` is available only when minting for an existing resource. The SDK
|
|
87
|
+
checks the non-empty 2048-byte boundary; the API remains authoritative for the
|
|
88
|
+
path grammar and the tunnel-only resource gate. `createPortalForUrl` rejects
|
|
89
|
+
this option because it creates a URL resource.
|
|
90
|
+
|
|
91
|
+
`resource.resourceId`, `resource.connectorRoutingId`, and
|
|
92
|
+
`resource.knockResourceId` are three distinct server-issued values for public
|
|
93
|
+
identity, reverse routing, and NHP admission. Consume each verbatim; never
|
|
94
|
+
derive or substitute one for another. Use `getConnectorResource(resourceId)`
|
|
95
|
+
or `getConnectorResourceBySlug(slug)` for read-only lookup and
|
|
96
|
+
`deleteConnectorResource(resourceId)` to revoke it. This replaces the old
|
|
97
|
+
alias-based `connectorResource(connectorId)` method.
|
|
98
|
+
`ConnectorResource` instances cannot be constructed directly; the client
|
|
99
|
+
returns them only after validating the complete response contract.
|
|
100
|
+
|
|
101
|
+
`ensureConnectorResource` and `deleteConnectorResource` throw
|
|
102
|
+
`ConnectorResourceOutcomeUnknownError` when a dispatched mutation may have
|
|
103
|
+
committed but its response cannot prove the result. Reconcile by immutable slug
|
|
104
|
+
or resource ID before deciding whether to retry. The wrapper deliberately uses
|
|
105
|
+
`status: 0`; the original typed error is available as `cause`, including its
|
|
106
|
+
observed HTTP status.
|
|
107
|
+
|
|
108
|
+
Connector lifecycle calls make one HTTP attempt. The SDK does not pace or
|
|
109
|
+
replay them; the caller controls any retry after it reconciles state.
|
|
110
|
+
|
|
111
|
+
`ensureConnectorResource` does not generate an idempotency key because the slug
|
|
112
|
+
operation is already idempotent. If you supply an `idempotencyKey`, the SDK
|
|
113
|
+
forwards it, and you must reuse it on any deliberate retry. A 409
|
|
114
|
+
`bootstrap_key_consumed` response is outcome-unknown because resource binding
|
|
115
|
+
can finish before bootstrap-key consumption fails. The original bootstrap key
|
|
116
|
+
is terminal: do not retry it. Obtain a new bootstrap key and use normal
|
|
117
|
+
owner-authenticated lookup by immutable slug to reconcile the resource first.
|
|
118
|
+
|
|
119
|
+
The API does not apply idempotency replay to DELETE, so after an outcome-unknown
|
|
120
|
+
`deleteConnectorResource` call, reconcile by resource ID before issuing a
|
|
121
|
+
deliberate retry. A valid exact-201 resource missing only `meta.found_existing`
|
|
122
|
+
is known to have selected that row but still fails as an unwrapped
|
|
123
|
+
`unexpected_response` because required ensure metadata is absent.
|
|
124
|
+
|
|
77
125
|
If you persist the resource id, future calls do not need to recreate the
|
|
78
126
|
handle (no API call is made until you mint):
|
|
79
127
|
|
|
@@ -102,11 +150,15 @@ const portal = await resource.createPortal({
|
|
|
102
150
|
label: 'Alice from Acme',
|
|
103
151
|
oneTimeUse: true,
|
|
104
152
|
maxSessions: 1,
|
|
153
|
+
targetPath: '/api/detect/eib_example',
|
|
105
154
|
});
|
|
106
155
|
```
|
|
107
156
|
|
|
157
|
+
`createPortal` sends `{}` when no options are set because the service requires
|
|
158
|
+
a JSON object at the wire level.
|
|
159
|
+
|
|
108
160
|
The Node-only `@layervai/qurl/node` entry opens received qv2 links with native
|
|
109
|
-
NHP
|
|
161
|
+
NHP UDP. It has no relay or HTTP-resolve fallback. Connector assignment and
|
|
110
162
|
registration are separate producer operations and are not part of the portal
|
|
111
163
|
opener.
|
|
112
164
|
|
|
@@ -133,10 +185,15 @@ back, it throws instead of returning an empty handle.
|
|
|
133
185
|
Use the Node subpath when a service receives a qv2 link and must keep one NHP
|
|
134
186
|
session ready for a low-latency private request. Construct and start one opener
|
|
135
187
|
for that link during setup. `start()` sends the native UDP knock and schedules
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
expired.
|
|
188
|
+
background renewal before the admission expires. A transient renewal failure
|
|
189
|
+
retries after 500 ms, then 1 second, then at most every 2 seconds while the old
|
|
190
|
+
admission remains valid. `fetch()` never opens or renews a session, and it never
|
|
191
|
+
sleeps. It fails if the cached admission has expired.
|
|
192
|
+
|
|
193
|
+
Renewal waits at least 5 seconds after a successful open. If a server grants a
|
|
194
|
+
session that expires before that safe renewal point, the grant expires without
|
|
195
|
+
a background attempt and `start()` is the explicit recovery path. Production
|
|
196
|
+
admissions should be longer than this minimum gap.
|
|
140
197
|
|
|
141
198
|
```javascript
|
|
142
199
|
const { createPortalOpener } = require('@layervai/qurl/node');
|
|
@@ -144,7 +201,6 @@ const { createPortalOpener } = require('@layervai/qurl/node');
|
|
|
144
201
|
async function uploadPrivateObject(uploadBody) {
|
|
145
202
|
const opener = createPortalOpener({
|
|
146
203
|
qurl: process.env.PRIVATE_UPLOAD_QURL,
|
|
147
|
-
transport: 'native-only',
|
|
148
204
|
});
|
|
149
205
|
try {
|
|
150
206
|
await opener.start();
|
|
@@ -162,65 +218,120 @@ async function uploadPrivateObject(uploadBody) {
|
|
|
162
218
|
}
|
|
163
219
|
```
|
|
164
220
|
|
|
165
|
-
|
|
221
|
+
For a service whose ACK target is a base path, append only trusted raw path
|
|
222
|
+
segments with `fetchDescendant()`. The opener rejects empty, dot, and delimiter
|
|
223
|
+
segments and escapes each accepted segment before it sends the request:
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
const response = await opener.fetchDescendant(
|
|
227
|
+
['eib_example'],
|
|
228
|
+
(authenticatedTarget) => ({ method: 'POST' }),
|
|
229
|
+
{ redirects: 'error' },
|
|
230
|
+
);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The request builder receives a copy of the selected authenticated target. The
|
|
166
234
|
opener ignores mutations to that copy and sends the initial request only to the
|
|
167
|
-
fixed ACK URL
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
235
|
+
fixed ACK URL or the validated descendant. A descendant call accepts raw path
|
|
236
|
+
segments, not a URL or path string. It preserves the ACK target query and
|
|
237
|
+
escapes each segment separately. It adds the private `qurl_vsession` cookie,
|
|
238
|
+
replaces a caller-supplied cookie with that name, and preserves other valid
|
|
239
|
+
cookies, including duplicate `Cookie` entries that Node joins with semicolons.
|
|
240
|
+
It does not accept a caller URL, and it rejects a caller-supplied `Host` header
|
|
241
|
+
so Fetch derives the authority from the pinned target. Both `fetch()` and
|
|
242
|
+
`fetchDescendant()` use only the cached NHP 1.1 admission. Use
|
|
243
|
+
`redirects: 'error'` to keep a descendant request at its initial target. The
|
|
244
|
+
default `follow` mode can move outside that path subtree, but only within the
|
|
245
|
+
authenticated origin. Also use `redirects: 'error'` for a request whose
|
|
246
|
+
signature binds its method, target, timestamp, or nonce. This mode closes a
|
|
247
|
+
redirect response and does not replay the request. Follow mode permits at most
|
|
248
|
+
10 requests, including the initial request, and uses the standard 301/302/303
|
|
249
|
+
method rewrite rules. As in Go, a 3xx response with no `Location` header, or a
|
|
250
|
+
307/308 response whose streaming body cannot be replayed, is returned to the
|
|
251
|
+
caller without a follow-up request. The caller then owns that response body and
|
|
252
|
+
must consume or cancel it. Local admission expiry is checked before the first
|
|
253
|
+
request; the protected service remains authoritative while a permitted redirect
|
|
254
|
+
chain is in progress.
|
|
255
|
+
|
|
256
|
+
When `fetch()` rejects before it sends a direct `RequestInit`, it releases a
|
|
257
|
+
caller-owned streaming body. It cancels a web `ReadableStream`, destroys a Node
|
|
258
|
+
`Readable`, or closes another iterator when that body supports the operation. A
|
|
259
|
+
request builder remains lazy and is not called until a grant is ready.
|
|
260
|
+
|
|
261
|
+
Each open has a whole-operation deadline that covers DNS and UDP.
|
|
262
|
+
`openTimeoutMs` sets this ceiling; the default is 15 seconds and the maximum is
|
|
263
|
+
60 seconds. Native DNS or address attempts can fail before this ceiling, so a
|
|
264
|
+
larger value does not extend their internal timeouts. A renewal also stops at
|
|
265
|
+
the old grant's expiry. Pass an abort signal to `start()` when lifecycle code
|
|
266
|
+
needs a shorter deadline. Concurrent `start()` calls share one attempt. The
|
|
267
|
+
first caller's signal owns that attempt, so its abort fails all waiters but does
|
|
268
|
+
not count as an open failure in health. A cold cancellation restores `new`; a
|
|
269
|
+
recovery cancellation preserves its prior `degraded` health. A later waiter's
|
|
270
|
+
abort stops only that wait.
|
|
187
271
|
Content requests use the caller's `RequestInit.signal`; `fetch()` does not add
|
|
188
|
-
an independent application-request deadline.
|
|
272
|
+
an independent application-request deadline. The signal stays active while the
|
|
273
|
+
returned response body is read. Set the opener's optional `fetch` when the
|
|
274
|
+
protected request must use a custom Fetch implementation. Native NHP opening
|
|
275
|
+
never uses this function. The custom function receives the `qurl_vsession`
|
|
276
|
+
bearer cookie and is inside the credential boundary. It must implement standard
|
|
277
|
+
Fetch signal behavior and honor `redirect: 'manual'`. If it needs a receiver,
|
|
278
|
+
pass it already bound; the SDK invokes it with the standard global Fetch
|
|
279
|
+
receiver. An unfollowed synthetic response can leave `Response.url` empty.
|
|
280
|
+
Otherwise, it must report
|
|
281
|
+
`Response.url` and `Response.redirected` accurately. It must not log or forward
|
|
282
|
+
protected request headers.
|
|
189
283
|
|
|
190
284
|
TypeScript consumers of `@layervai/qurl/node` must provide Node and Fetch API
|
|
191
285
|
declarations, for example current `@types/node`, or a configuration that includes
|
|
192
|
-
the `DOM` library for Fetch types.
|
|
286
|
+
the `DOM` library for Fetch types. The native opener requires Node 22.12 or later.
|
|
287
|
+
This floor ensures that composite request signals are reclaimed on long-lived
|
|
288
|
+
openers.
|
|
193
289
|
|
|
194
290
|
Native opening requires public deployment trust. Set `QURL_DEPLOYMENT` to one
|
|
195
291
|
strict JSON object or to a path that contains that object. The object must have
|
|
196
292
|
trusted P-256 issuer keys and native cell host, UDP port 443, and X25519 public
|
|
197
|
-
key entries. A configured path must be a regular file no larger than 1 MiB.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
`
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
293
|
+
key entries. A configured path must be a regular file no larger than 1 MiB.
|
|
294
|
+
Construction does no I/O. `start()` resolves and validates the deployment, and
|
|
295
|
+
the first successful open pins that resolved trust for renewal and recovery.
|
|
296
|
+
The opener does not perform discovery. It fails before DNS if the verified link
|
|
297
|
+
names an unknown cell.
|
|
298
|
+
|
|
299
|
+
Use `opener.health()` for the local `new`, `starting`, `ready`, `degraded`, or
|
|
300
|
+
`closed` state. It reports absolute expiry, renewal, and last-success times, a
|
|
301
|
+
secret-free failure class, and the number of consecutive unsuccessful open
|
|
302
|
+
attempts. A renewal window can contain more than one such attempt. The
|
|
303
|
+
`starting` state is only for the first cold open; an explicit recovery from a
|
|
304
|
+
failure reports `degraded` until it succeeds. Date fields describe the most
|
|
305
|
+
recent successful grant, so use `ready` as the authority for current usability.
|
|
306
|
+
A transient renewal failure keeps the state `ready` while the prior admission
|
|
307
|
+
is usable. A changed authenticated target is not retried. It keeps the prior
|
|
308
|
+
admission until expiry. During that time, `start()` remains idempotent because
|
|
309
|
+
the old admission is still usable; `health().lastFailureClass` reports the
|
|
310
|
+
target change. After expiry, an explicit `start()` attempts recovery. If the
|
|
311
|
+
target change is intentional and permanent, create a new opener for the new
|
|
312
|
+
qURL; the old opener stays bound to its first authenticated target. Other
|
|
313
|
+
failures retry through the remaining admission window. This does not put an
|
|
314
|
+
open or sleep on the `fetch()` path.
|
|
315
|
+
|
|
316
|
+
Close cancels and waits for an active NHP open, then wipes the mutable
|
|
317
|
+
private-key, visitor-secret, and session-token buffers. Close also aborts a
|
|
318
|
+
protected request and its returned response body, and prevents another redirect
|
|
319
|
+
leg from starting. JavaScript can create immutable string copies during JSON
|
|
320
|
+
and HTTP processing, so the SDK cannot promise full memory zeroization before garbage
|
|
321
|
+
collection. The opener retains the immutable qURL string until close so it can
|
|
322
|
+
verify each renewal. Never log the qURL, ACK body, request cookies, or request
|
|
323
|
+
headers.
|
|
324
|
+
|
|
325
|
+
A cold `start()` failure leaves health at `degraded`. Catch `PortalBusyError`
|
|
326
|
+
for an authenticated COOKIE busy response and `PortalInvalidReplyError` for a
|
|
327
|
+
malformed authenticated reply. An open deadline throws `PortalOpenTimeoutError`.
|
|
328
|
+
The thrown typed error is the cold-start
|
|
329
|
+
diagnostic; health does not expose its message or secret values.
|
|
219
330
|
|
|
220
331
|
Local qv2 verification checks the signed bytes, trust, and clock-free claim
|
|
221
332
|
ordering. As in the Go SDK, it does not compare `nbf` or `exp` with the local
|
|
222
333
|
clock. The authenticated NHP open is the authoritative live validity check.
|
|
223
|
-
The opener also follows the Go NHP
|
|
334
|
+
The opener also follows the Go NHP COOKIE rule: an authenticated COOKIE is
|
|
224
335
|
a busy result and does not use ACK counter correlation or local clock skew
|
|
225
336
|
checks. Replaying it can only force another busy result; it cannot grant access.
|
|
226
337
|
|
|
@@ -264,9 +375,11 @@ console.log(`Access granted to ${access.target_url} for ${access.access_grant?.e
|
|
|
264
375
|
| Method | Description |
|
|
265
376
|
|--------|-------------|
|
|
266
377
|
| `protectUrl(targetUrl, opts?)` | Protect a private URL → portal-minting `ProtectedResource` handle |
|
|
267
|
-
| `resource.createPortal(opts?)` / `createPortal(resourceOrId, opts?)` | Mint a short-lived portal link
|
|
268
|
-
| `createPortalForUrl(targetUrl, opts?)` | Protect + mint
|
|
269
|
-
| `
|
|
378
|
+
| `resource.createPortal(opts?)` / `createPortal(resourceOrId, opts?)` | Mint a short-lived portal link; existing resources can set `targetPath` |
|
|
379
|
+
| `createPortalForUrl(targetUrl, opts?)` | Protect + mint a URL resource; rejects `targetPath` |
|
|
380
|
+
| `ensureConnectorResource(slug, requestOptions?)` | Find or create an active Connector resource by immutable slug |
|
|
381
|
+
| `getConnectorResource(resourceId)` / `getConnectorResourceBySlug(slug)` | Load a validated Connector resource by immutable identity |
|
|
382
|
+
| `deleteConnectorResource(resourceId)` | Revoke a Connector resource by immutable public resource ID |
|
|
270
383
|
| `resourceById(id)` | Handle from a stored resource id (no API call) |
|
|
271
384
|
| `enterPortal(linkOrToken)` | Open a qURL link programmatically → `ResourceHandle` |
|
|
272
385
|
|
|
@@ -379,7 +492,7 @@ try {
|
|
|
379
492
|
Client-detected failures use `status: 0` with a discriminating `code`:
|
|
380
493
|
`"client_validation"` for bad input caught before a request, and — on the
|
|
381
494
|
portal surface — `"resource_not_found"` / `"ambiguous_resource"` when
|
|
382
|
-
`
|
|
495
|
+
`getConnectorResourceBySlug` cannot resolve a slug to exactly one resource,
|
|
383
496
|
and `"unexpected_response"` when a response is missing required fields (e.g.
|
|
384
497
|
`enterPortal` failing closed on a grant with no resource URL).
|
|
385
498
|
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -92,23 +92,19 @@ export declare class QURLClient {
|
|
|
92
92
|
* need the full resource details.
|
|
93
93
|
*/
|
|
94
94
|
resourceById(id: string): ProtectedResource;
|
|
95
|
+
/** Find or create the active qURL Connector resource for an immutable slug. */
|
|
96
|
+
ensureConnectorResource(slug: string, requestOptions?: RequestOptions): Promise<EnsureConnectorResourceResult>;
|
|
97
|
+
/** Fetch a qURL Connector resource by its immutable public resource ID. */
|
|
98
|
+
getConnectorResource(resourceId: string): Promise<ConnectorResource>;
|
|
99
|
+
/** Fetch the single active qURL Connector resource for an immutable slug. */
|
|
100
|
+
getConnectorResourceBySlug(slug: string): Promise<ConnectorResource>;
|
|
95
101
|
/**
|
|
96
|
-
*
|
|
102
|
+
* Revoke a qURL Connector resource by immutable public resource ID.
|
|
97
103
|
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* resource slug LayerV stores for that connector; the LayerV API performs
|
|
101
|
-
* the slug lookup, and the SDK confirms the returned alias matches
|
|
102
|
-
* `connectorId` before binding the handle (qurl-go:
|
|
103
|
-
* `Client.ConnectorResource`).
|
|
104
|
-
*
|
|
105
|
-
* Throws {@link NotFoundError} (`status: 0`, `code: "resource_not_found"`)
|
|
106
|
-
* when no resource exists for the connector id, {@link QURLError}
|
|
107
|
-
* (`code: "ambiguous_resource"`) when the lookup returns more than one
|
|
108
|
-
* resource, and {@link ValidationError} (`code: "unexpected_response"`)
|
|
109
|
-
* when the returned resource's alias is missing or does not match.
|
|
104
|
+
* The API does not apply Idempotency-Key replay to DELETE. After an
|
|
105
|
+
* outcome-unknown failure, reconcile by ID before issuing a deliberate retry.
|
|
110
106
|
*/
|
|
111
|
-
|
|
107
|
+
deleteConnectorResource(resourceId: string): Promise<void>;
|
|
112
108
|
/**
|
|
113
109
|
* Mint a short-lived qURL link (a portal) for a protected resource.
|
|
114
110
|
*
|
|
@@ -116,14 +112,15 @@ export declare class QURLClient {
|
|
|
116
112
|
* reach one private resource. Recipients open `portal.link` directly and
|
|
117
113
|
* need no LayerV credentials. Prefer short lifetimes such as
|
|
118
114
|
* `{ validFor: "5m" }`. Accepts a {@link ProtectedResource} handle or a
|
|
119
|
-
* resource
|
|
115
|
+
* public resource ID string. REST-shaped equivalents: {@link mintLink} /
|
|
120
116
|
* {@link createQurlForResource}.
|
|
121
117
|
*
|
|
122
118
|
* Duration options take a string (`"5m"`, `"24h"`; server-validated) or a
|
|
123
119
|
* number of milliseconds with qurl-go's client-side guardrails: whole
|
|
124
120
|
* seconds only, at least one minute for `validFor` and one second for
|
|
125
121
|
* `sessionDuration`. `maxSessions: 0` is sent explicitly and means
|
|
126
|
-
* unlimited; blank labels are rejected.
|
|
122
|
+
* unlimited; blank labels are rejected. `targetPath` is valid only for an
|
|
123
|
+
* existing resource. The API owns its full grammar and tunnel-only gate.
|
|
127
124
|
*/
|
|
128
125
|
createPortal(resource: ProtectedResource | string, opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
|
|
129
126
|
/**
|
|
@@ -135,7 +132,9 @@ export declare class QURLClient {
|
|
|
135
132
|
* {@link protectUrl} when you need the full server-populated resource
|
|
136
133
|
* metadata. Portal options match {@link createPortal}; `targetUrl` gets
|
|
137
134
|
* the same malformed-URL and embedded-credentials rejection as
|
|
138
|
-
* {@link protectUrl}.
|
|
135
|
+
* {@link protectUrl}. `targetPath` is rejected because this call creates a
|
|
136
|
+
* URL resource instead of addressing an existing resource. REST-shaped
|
|
137
|
+
* equivalent: {@link create}.
|
|
139
138
|
*/
|
|
140
139
|
createPortalForUrl(targetUrl: string, opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<{
|
|
141
140
|
portal: Portal;
|
|
@@ -506,14 +505,13 @@ export declare class QURLClient {
|
|
|
506
505
|
/**
|
|
507
506
|
* A LayerV-protected resource, bound to the client that produced it.
|
|
508
507
|
*
|
|
509
|
-
* Obtained from {@link QURLClient.protectUrl}
|
|
510
|
-
* {@link QURLClient.connectorResource}, or {@link QURLClient.resourceById} —
|
|
508
|
+
* Obtained from {@link QURLClient.protectUrl} or {@link QURLClient.resourceById} —
|
|
511
509
|
* not constructed directly. Mint short-lived access links for the resource
|
|
512
510
|
* with {@link createPortal} (qurl-go: `qurl.Resource`).
|
|
513
511
|
*/
|
|
514
512
|
export declare class ProtectedResource {
|
|
515
513
|
#private;
|
|
516
|
-
/** The LayerV resource
|
|
514
|
+
/** The LayerV public resource ID. */
|
|
517
515
|
readonly id: string;
|
|
518
516
|
/** The private URL protected by this resource, when known. */
|
|
519
517
|
readonly targetUrl?: string;
|
|
@@ -537,3 +535,31 @@ export declare class ProtectedResource {
|
|
|
537
535
|
*/
|
|
538
536
|
createPortal(opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
|
|
539
537
|
}
|
|
538
|
+
/** Result of idempotently ensuring a qURL Connector resource. */
|
|
539
|
+
export interface EnsureConnectorResourceResult {
|
|
540
|
+
resource: ConnectorResource;
|
|
541
|
+
foundExisting: boolean;
|
|
542
|
+
}
|
|
543
|
+
declare const CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN: unique symbol;
|
|
544
|
+
/**
|
|
545
|
+
* A validated qURL Connector resource bound to the client that loaded it.
|
|
546
|
+
*
|
|
547
|
+
* Resource identity, reverse-routing identity, and NHP admission identity are
|
|
548
|
+
* distinct server-issued values. Callers must consume each field verbatim and
|
|
549
|
+
* must never derive one from another.
|
|
550
|
+
*/
|
|
551
|
+
export declare class ConnectorResource {
|
|
552
|
+
#private;
|
|
553
|
+
readonly resourceId: string;
|
|
554
|
+
/** Producer-supplied CRID carried verbatim; verify it against a trusted key before trust. */
|
|
555
|
+
readonly crid?: string;
|
|
556
|
+
readonly connectorRoutingId: string;
|
|
557
|
+
readonly knockResourceId: string;
|
|
558
|
+
readonly slug: string;
|
|
559
|
+
readonly alias?: string;
|
|
560
|
+
/** @internal Instances are returned by QURLClient after wire validation. */
|
|
561
|
+
constructor(client: QURLClient, details: Resource, validationToken: typeof CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN);
|
|
562
|
+
/** Mint a short-lived portal for this Connector resource. */
|
|
563
|
+
createPortal(opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
|
|
564
|
+
}
|
|
565
|
+
export {};
|