@layervai/qurl 0.5.0 → 0.7.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 +160 -11
- package/dist/cjs/client.d.ts +75 -39
- package/dist/cjs/client.js +672 -152
- package/dist/cjs/crid.d.ts +2 -0
- package/dist/cjs/crid.js +53 -0
- package/dist/cjs/errors.d.ts +34 -5
- package/dist/cjs/errors.js +44 -6
- package/dist/cjs/index.d.ts +6 -3
- package/dist/cjs/index.js +13 -1
- package/dist/cjs/share.d.ts +56 -0
- package/dist/cjs/share.js +113 -0
- package/dist/cjs/types.d.ts +84 -13
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +75 -39
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +670 -152
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/crid.d.ts +3 -0
- package/dist/esm/crid.d.ts.map +1 -0
- package/dist/esm/crid.js +50 -0
- package/dist/esm/crid.js.map +1 -0
- package/dist/esm/errors.d.ts +34 -5
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +42 -5
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +6 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/share.d.ts +57 -0
- package/dist/esm/share.d.ts.map +1 -0
- package/dist/esm/share.js +109 -0
- package/dist/esm/share.js.map +1 -0
- package/dist/esm/types.d.ts +84 -13
- 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 +2 -2
package/README.md
CHANGED
|
@@ -68,11 +68,15 @@ same URL again returns the existing resource. `validFor` accepts a duration
|
|
|
68
68
|
string (`'5m'`, `'24h'`) or a number of milliseconds (whole seconds, at least
|
|
69
69
|
one minute); prefer short portal lifetimes.
|
|
70
70
|
|
|
71
|
-
If qURL Connector
|
|
72
|
-
of calling `protectUrl`:
|
|
71
|
+
If qURL Connector protects the service, address its management-plane resource
|
|
72
|
+
by immutable slug instead of calling `protectUrl`:
|
|
73
73
|
|
|
74
74
|
```typescript
|
|
75
|
-
const resource = await client.
|
|
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');
|
|
76
80
|
const portal = await resource.createPortal({
|
|
77
81
|
validFor: '5m',
|
|
78
82
|
targetPath: '/api/detect/eib_example',
|
|
@@ -84,12 +88,52 @@ checks the non-empty 2048-byte boundary; the API remains authoritative for the
|
|
|
84
88
|
path grammar and the tunnel-only resource gate. `createPortalForUrl` rejects
|
|
85
89
|
this option because it creates a URL resource.
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
`resource.crid` is the required management identifier. Connector lookup, delete,
|
|
92
|
+
and portal minting use it with no public-key or private-ID fallback. The SDK
|
|
93
|
+
checks that the returned public key matches the CRID.
|
|
94
|
+
|
|
95
|
+
`resource.resourcePublicKey`, `resource.connectorRoutingId`, and
|
|
96
|
+
`resource.knockResourceId` are three distinct server-issued values for verification, reverse routing, and NHP admission. Consume each verbatim; never
|
|
97
|
+
derive or substitute one for another. Use `getConnectorResource(crid)`
|
|
98
|
+
or `getConnectorResourceBySlug(slug)` for read-only lookup and
|
|
99
|
+
`deleteConnectorResource(crid)` to revoke it. This replaces the old
|
|
100
|
+
alias-based `connectorResource(connectorId)` method.
|
|
101
|
+
`ConnectorResource` instances cannot be constructed directly; the client
|
|
102
|
+
returns them only after validating the complete response contract.
|
|
103
|
+
|
|
104
|
+
`ensureConnectorResource` and `deleteConnectorResource` throw
|
|
105
|
+
`ConnectorResourceOutcomeUnknownError` when a dispatched mutation may have
|
|
106
|
+
committed but its response cannot prove the result. Reconcile by immutable slug
|
|
107
|
+
or CRID before deciding whether to retry. The wrapper deliberately uses
|
|
108
|
+
`status: 0`; the original typed error is available as `cause`, including its
|
|
109
|
+
observed HTTP status.
|
|
110
|
+
|
|
111
|
+
Connector lifecycle calls make one HTTP attempt. The SDK does not pace or
|
|
112
|
+
replay them; the caller controls any retry after it reconciles state.
|
|
113
|
+
|
|
114
|
+
`ensureConnectorResource` does not generate an idempotency key because the slug
|
|
115
|
+
operation is already idempotent. If you supply an `idempotencyKey`, the SDK
|
|
116
|
+
forwards it, and you must reuse it on any deliberate retry. A 409
|
|
117
|
+
`bootstrap_key_consumed` response is outcome-unknown because resource binding
|
|
118
|
+
can finish before bootstrap-key consumption fails. The original bootstrap key
|
|
119
|
+
is terminal: do not retry it. Obtain a new bootstrap key and use normal
|
|
120
|
+
owner-authenticated lookup by immutable slug to reconcile the resource first.
|
|
121
|
+
|
|
122
|
+
The API does not apply idempotency replay to DELETE, so after an outcome-unknown
|
|
123
|
+
`deleteConnectorResource` call, reconcile by CRID before issuing a
|
|
124
|
+
deliberate retry. A valid exact-201 resource missing only `meta.found_existing`
|
|
125
|
+
is known to have selected that row but still fails as an unwrapped
|
|
126
|
+
`unexpected_response` because required ensure metadata is absent.
|
|
127
|
+
|
|
128
|
+
If you persist the CRID, future calls do not need to recreate the
|
|
88
129
|
handle (no API call is made until you mint):
|
|
89
130
|
|
|
90
131
|
```typescript
|
|
91
|
-
|
|
92
|
-
|
|
132
|
+
async function createPortalForStoredResource(storedCrid: string) {
|
|
133
|
+
// Pass the CRID previously returned by the API.
|
|
134
|
+
const resource = client.resourceByCrid(storedCrid);
|
|
135
|
+
return resource.createPortal({ validFor: '1h' });
|
|
136
|
+
}
|
|
93
137
|
```
|
|
94
138
|
|
|
95
139
|
For one-off scripts, `client.createPortalForUrl` combines the two API calls
|
|
@@ -116,11 +160,105 @@ const portal = await resource.createPortal({
|
|
|
116
160
|
});
|
|
117
161
|
```
|
|
118
162
|
|
|
163
|
+
`createPortal` sends `{}` when no options are set because the service requires
|
|
164
|
+
a JSON object at the wire level.
|
|
165
|
+
|
|
119
166
|
The Node-only `@layervai/qurl/node` entry opens received qv2 links with native
|
|
120
167
|
NHP UDP. It has no relay or HTTP-resolve fallback. Connector assignment and
|
|
121
168
|
registration are separate producer operations and are not part of the portal
|
|
122
169
|
opener.
|
|
123
170
|
|
|
171
|
+
If you already hold a CRID, mint a fresh share link directly:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const share = await client.shareResource(crid, { ttlSeconds: 300 });
|
|
175
|
+
// Optional: verify the response CRID against DER SPKI bytes you already trust
|
|
176
|
+
// before the secret leaves this process.
|
|
177
|
+
await share.verifyCrid(resourcePublicKeyDer);
|
|
178
|
+
await deliverToRecipient(share.link); // Secret; returned once and not retrievable
|
|
179
|
+
console.log(share.qurlId); // Safe handle for revoking only this link later
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`shareResource` returns the current share-safe `#qv2t1...` link. Recipients
|
|
183
|
+
open that URL in the qURL browser flow. For native TypeScript opening, pass
|
|
184
|
+
`share.link` to `createPortalOpener` from `@layervai/qurl/node` with trusted
|
|
185
|
+
`QURL_DEPLOYMENT` configuration. See the native opener example above.
|
|
186
|
+
`verifyCrid` does not open the link or make a network request.
|
|
187
|
+
|
|
188
|
+
`verifyCrid` verifies that the response CRID derives from the trusted resource
|
|
189
|
+
key; it does not independently bind the secret link fragment to that key.
|
|
190
|
+
It intentionally follows qurl-go's environment-agnostic key-match rule: CRID
|
|
191
|
+
version zero is rejected as reserved, while every other structurally valid
|
|
192
|
+
version is accepted and environment classification is not part of key-to-digest
|
|
193
|
+
verification. It compares all 32 digest bytes for a full CRID and all 24 for
|
|
194
|
+
a truncated CRID; the response determines the digest width. CRIDs use their canonical lowercase base32 spelling; uppercase or
|
|
195
|
+
mixed-case spellings fail closed. Binary inputs of any length are hashed;
|
|
196
|
+
`invalid_crid_key` is reserved for non-binary or unreadable (detached) runtime
|
|
197
|
+
values, and a readable binary key that does not match reports `crid_mismatch`.
|
|
198
|
+
Use `error instanceof CRIDVerificationError` (or `error.status === 0`) to
|
|
199
|
+
distinguish these local verification failures from a server error that happens
|
|
200
|
+
to use the same problem code.
|
|
201
|
+
`qurlId` and `singleUse` are undefined when omitted by the service.
|
|
202
|
+
`ShareLink` keeps `.link` directly readable but redacts it from JSON, Node
|
|
203
|
+
inspection, and object spread to reduce accidental credential logging. Read
|
|
204
|
+
`.link` directly before delivery; do not spread or clone a `ShareLink`, because
|
|
205
|
+
those operations deliberately omit the credential. Browser developer consoles
|
|
206
|
+
can still display non-enumerable properties, so do not log the object there.
|
|
207
|
+
The constructor snapshots a caller-provided expiry, and the `ShareLink`
|
|
208
|
+
properties are frozen. Each `expiresAt` read returns a defensive `Date` copy;
|
|
209
|
+
changing that copy does not change the share metadata.
|
|
210
|
+
|
|
211
|
+
Omitting `ttlSeconds` matches qurl-go's zero-value behavior and requests the
|
|
212
|
+
platform default. TypeScript additionally rejects an explicit zero so a
|
|
213
|
+
computed countdown cannot silently become a longer-lived default credential.
|
|
214
|
+
A response can report `expiresInSeconds: 0`; do not reuse that value as a
|
|
215
|
+
request TTL. Expiry parsing uses the SDK's shared API date parser.
|
|
216
|
+
|
|
217
|
+
The service chooses single-use policy; this matches the Go SDK options.
|
|
218
|
+
|
|
219
|
+
qURL Connector assignment and registration use native UDP through
|
|
220
|
+
`qurl-connector` and `qurl-go`. This package does not expose an HTTP enrollment API.
|
|
221
|
+
Like the Go SDK, credential minting uses HTTPS and token consumption uses
|
|
222
|
+
native UDP. The Go SDK currently exposes minting through its restricted
|
|
223
|
+
`RegisteredAgentResourceHTTPDoer` bridge; this SDK uses `createApiKey`.
|
|
224
|
+
The service controls which credential kinds each caller can mint.
|
|
225
|
+
It can mint the one-shot credential consumed by that native enrollment flow:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const enrollment = await client.createApiKey({
|
|
229
|
+
kind: 'enrollment_token',
|
|
230
|
+
name: 'prod-dashboard enrollment',
|
|
231
|
+
target: 'connector',
|
|
232
|
+
claims: [{ type: 'connector', id: 'prod-dashboard' }],
|
|
233
|
+
expires_in: '15m',
|
|
234
|
+
});
|
|
235
|
+
if (!enrollment.api_key) throw new Error('Enrollment response omitted its one-time token');
|
|
236
|
+
await deliverEnrollmentTokenSecurely(enrollment.api_key);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Durable `api_key` credentials require explicit scopes and do not accept
|
|
240
|
+
`expires_in`; enrollment tokens derive their scopes from `target`/`claims` and
|
|
241
|
+
expire within 24 hours. A connector claim's `id` is its immutable connector
|
|
242
|
+
slug. Request enums are validated against the current service contract;
|
|
243
|
+
response types remain additive for forward-compatible reads, and new request
|
|
244
|
+
enum values require a matching SDK release. Use
|
|
245
|
+
`isApiKeyRequestScope(scope)` to validate response scopes before writing them
|
|
246
|
+
back. Reject unknown scopes; do not filter them out, which can remove permissions.
|
|
247
|
+
When changing only a name, omit `scopes` from `updateApiKey`.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { isApiKeyRequestScope } from '@layervai/qurl';
|
|
251
|
+
|
|
252
|
+
const scopes = key.scopes;
|
|
253
|
+
if (!scopes?.length || !scopes.every(isApiKeyRequestScope)) {
|
|
254
|
+
throw new Error('Cannot reuse these scopes; check the current SDK contract');
|
|
255
|
+
}
|
|
256
|
+
await client.updateApiKey(keyId, { scopes });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
This credential surface requires the kind-first qurl-service contract at or
|
|
260
|
+
after commit `047cf31e1cdf545e3060e0f9294d738a19fb997b`.
|
|
261
|
+
|
|
124
262
|
## Opening Portals
|
|
125
263
|
|
|
126
264
|
Most recipients open qURL links directly and do not use this SDK at all. If
|
|
@@ -336,8 +474,10 @@ console.log(`Access granted to ${access.target_url} for ${access.access_grant?.e
|
|
|
336
474
|
| `protectUrl(targetUrl, opts?)` | Protect a private URL → portal-minting `ProtectedResource` handle |
|
|
337
475
|
| `resource.createPortal(opts?)` / `createPortal(resourceOrId, opts?)` | Mint a short-lived portal link; existing resources can set `targetPath` |
|
|
338
476
|
| `createPortalForUrl(targetUrl, opts?)` | Protect + mint a URL resource; rejects `targetPath` |
|
|
339
|
-
| `
|
|
340
|
-
| `
|
|
477
|
+
| `ensureConnectorResource(slug, requestOptions?)` | Find or create an active Connector resource by immutable slug |
|
|
478
|
+
| `getConnectorResource(crid)` / `getConnectorResourceBySlug(slug)` | Load a validated Connector resource by immutable identity |
|
|
479
|
+
| `deleteConnectorResource(crid)` | Revoke a Connector resource by CRID |
|
|
480
|
+
| `resourceByCrid(crid)` | Handle from a stored CRID (no API call) |
|
|
341
481
|
| `enterPortal(linkOrToken)` | Open a qURL link programmatically → `ResourceHandle` |
|
|
342
482
|
|
|
343
483
|
### REST-shaped methods
|
|
@@ -399,7 +539,7 @@ if (result.failed > 0) {
|
|
|
399
539
|
|
|
400
540
|
Non-400 errors (401, 403, 429, 5xx, and unexpected 400 body shapes) still throw the appropriate `QURLError` subclass.
|
|
401
541
|
|
|
402
|
-
**Slimmer per-item shape** — `BatchItemSuccess` returns `{ resource_id, qurl_link, qurl_site, expires_at? }` per item. Unlike single `client.create()`, the batch response intentionally **omits `qurl_id` and `label`** to keep the payload compact. If you migrate a per-item `create()` loop to `batchCreate` and rely on `qurl_id` for downstream addressing, fetch each via `client.get(
|
|
542
|
+
**Slimmer per-item shape** — `BatchItemSuccess` returns `{ crid, resource_id, qurl_link, qurl_site, expires_at? }` per item. Unlike single `client.create()`, the batch response intentionally **omits `qurl_id` and `label`** to keep the payload compact. If you migrate a per-item `create()` loop to `batchCreate` and rely on `qurl_id` for downstream addressing, check `item.crid` is present, then fetch each via `client.get(item.crid)` after the batch (or stay on the single-create path).
|
|
403
543
|
|
|
404
544
|
**Result ordering** — `result.results` is **not** guaranteed to be sorted by `index`. Each entry's `index` field carries the position in the original `items` array, so build per-input-position state by keying on `r.index` (e.g., `for (const r of result.results) { byInputIndex[r.index] = r; }`) rather than relying on iteration order.
|
|
405
545
|
|
|
@@ -449,7 +589,7 @@ try {
|
|
|
449
589
|
Client-detected failures use `status: 0` with a discriminating `code`:
|
|
450
590
|
`"client_validation"` for bad input caught before a request, and — on the
|
|
451
591
|
portal surface — `"resource_not_found"` / `"ambiguous_resource"` when
|
|
452
|
-
`
|
|
592
|
+
`getConnectorResourceBySlug` cannot resolve a slug to exactly one resource,
|
|
453
593
|
and `"unexpected_response"` when a response is missing required fields (e.g.
|
|
454
594
|
`enterPortal` failing closed on a grant with no resource URL).
|
|
455
595
|
|
|
@@ -461,7 +601,8 @@ const page = await client.list({ limit: 10, status: 'active' });
|
|
|
461
601
|
|
|
462
602
|
// Auto-paginate through all results
|
|
463
603
|
for await (const qurl of client.listAll({ status: 'active' })) {
|
|
464
|
-
|
|
604
|
+
if (!qurl.crid) throw new Error("The API response has no resource CRID");
|
|
605
|
+
console.log(qurl.crid);
|
|
465
606
|
}
|
|
466
607
|
```
|
|
467
608
|
|
|
@@ -545,6 +686,10 @@ SDK-generated keys require `globalThis.crypto.getRandomValues`, which is availab
|
|
|
545
686
|
## Security Notes
|
|
546
687
|
|
|
547
688
|
- Treat API keys and qURL links like credentials. Do not log them.
|
|
689
|
+
- Resource/qURL path arguments reject pasted access tokens and full URLs before
|
|
690
|
+
dispatch without echoing the caller input. Pass the opaque identifier returned
|
|
691
|
+
by the API (public resource key or CRID); never derive it from a secret qURL
|
|
692
|
+
link. Private `r_` storage IDs are not public API identifiers.
|
|
548
693
|
- SDK API requests use manual redirect handling. Redirect-capable HTTP statuses
|
|
549
694
|
(300, 301, 302, 303, 305, 307, 308), filtered `opaqueredirect` responses in
|
|
550
695
|
browsers and Node native fetch, and responses a custom fetch reports as
|
|
@@ -608,3 +753,7 @@ When upgrading, check the release notes for migration guidance — recent breaki
|
|
|
608
753
|
## License
|
|
609
754
|
|
|
610
755
|
MIT
|
|
756
|
+
|
|
757
|
+
Resource verification requires Web Crypto (`crypto.subtle`). In browsers, use
|
|
758
|
+
an HTTPS page or localhost. Raw REST response types keep `crid` optional;
|
|
759
|
+
resource handles and portals require a validated CRID.
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ShareLink } from "./share.js";
|
|
2
|
+
import type { AccessCodeListOutput, ApiKey, ApiKeyListOutput, ApiKeyRequestScope, BillingInvoiceListOutput, BatchCreateInput, BatchCreateOutput, ClientOptions, ConnectorInstallation, ConnectorInstallationListOutput, CreateAccessCodeInput, CreateAccessCodeOutput, CreateApiKeyInput, CreateApiKeyOutput, CreateBillingCheckoutInput, CreateInput, CreateOutput, CreatePortalOptions, CreateQurlForResourceInput, CreateResourceInput, CreateWebhookInput, Customer, CheckoutSession, Domain, DomainListOutput, DomainVerifyResult, ExtendInput, Invoice, ListInput, ListOutput, ListApiKeysInput, ListBillingInvoicesInput, ListConnectorInstallationsInput, ListDomainsInput, ListWebhookDeliveriesInput, ListWebhooksInput, MintInput, MintOutput, Portal, PortalSession, ProtectUrlOptions, QURL, QurlSummary, Quota, RedeemAccessCodeInput, RedeemAccessCodeOutput, RegisterDomainInput, Resource, ResourceDetail, ResourceHandle, ResourceListInput, ResourceListOutput, RequestOptions, ResolveInput, ResolveOutput, SessionListOutput, SessionTerminateOutput, ShareResourceOptions, UpdateApiKeyInput, UpdateCustomerInput, UpdateInput, UpdateResourceInput, UpdateResourceQurlInput, UpdateWebhookInput, UsageCurrentPeriod, UsageDaily, Webhook, WebhookDelivery, WebhookDeliveryListOutput, WebhookEventTypeInfo, WebhookListOutput, WebhookWithSecret } from "./types.js";
|
|
3
|
+
/** Narrow a response scope before reusing it in a create/update request. */
|
|
4
|
+
export declare function isApiKeyRequestScope(scope: string): scope is ApiKeyRequestScope;
|
|
2
5
|
/**
|
|
3
6
|
* Shape of qurl-go's offline signed-fragment links: three or more
|
|
4
7
|
* dot-separated base64url parts (<version>.<claims>.<secret>.<sig>).
|
|
@@ -77,38 +80,34 @@ export declare class QURLClient {
|
|
|
77
80
|
*/
|
|
78
81
|
protectUrl(targetUrl: string, opts?: ProtectUrlOptions, requestOptions?: RequestOptions): Promise<ProtectedResource>;
|
|
79
82
|
/**
|
|
80
|
-
* Return a portal-minting handle for a stored
|
|
83
|
+
* Return a portal-minting handle for a stored CRID, without an API
|
|
81
84
|
* call.
|
|
82
85
|
*
|
|
83
|
-
* Use when you persisted a LayerV
|
|
84
|
-
* portals for it (qurl-go: `Client.
|
|
86
|
+
* Use when you persisted a LayerV CRID and want to mint more
|
|
87
|
+
* portals for it (qurl-go: `Client.ResourceByCRID`):
|
|
85
88
|
*
|
|
86
89
|
* ```ts
|
|
87
|
-
* const resource = client.
|
|
90
|
+
* const resource = client.resourceByCrid(storedCrid);
|
|
88
91
|
* const portal = await resource.createPortal({ validFor: "1h" });
|
|
89
92
|
* ```
|
|
90
93
|
*
|
|
91
94
|
* The handle carries no server metadata; use {@link getResource} when you
|
|
92
95
|
* need the full resource details.
|
|
93
96
|
*/
|
|
94
|
-
|
|
97
|
+
resourceByCrid(id: string): ProtectedResource;
|
|
98
|
+
/** Find or create the active qURL Connector resource for an immutable slug. */
|
|
99
|
+
ensureConnectorResource(slug: string, requestOptions?: RequestOptions): Promise<EnsureConnectorResourceResult>;
|
|
100
|
+
/** Fetch a qURL Connector resource by its CRID. */
|
|
101
|
+
getConnectorResource(crid: string): Promise<ConnectorResource>;
|
|
102
|
+
/** Fetch the single active qURL Connector resource for an immutable slug. */
|
|
103
|
+
getConnectorResourceBySlug(slug: string): Promise<ConnectorResource>;
|
|
95
104
|
/**
|
|
96
|
-
*
|
|
105
|
+
* Revoke a qURL Connector resource by CRID.
|
|
97
106
|
*
|
|
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.
|
|
107
|
+
* The API does not apply Idempotency-Key replay to DELETE. After an
|
|
108
|
+
* outcome-unknown failure, reconcile by ID before issuing a deliberate retry.
|
|
110
109
|
*/
|
|
111
|
-
|
|
110
|
+
deleteConnectorResource(crid: string): Promise<void>;
|
|
112
111
|
/**
|
|
113
112
|
* Mint a short-lived qURL link (a portal) for a protected resource.
|
|
114
113
|
*
|
|
@@ -116,7 +115,7 @@ export declare class QURLClient {
|
|
|
116
115
|
* reach one private resource. Recipients open `portal.link` directly and
|
|
117
116
|
* need no LayerV credentials. Prefer short lifetimes such as
|
|
118
117
|
* `{ validFor: "5m" }`. Accepts a {@link ProtectedResource} handle or a
|
|
119
|
-
*
|
|
118
|
+
* CRID string. REST-shaped equivalents: {@link mintLink} /
|
|
120
119
|
* {@link createQurlForResource}.
|
|
121
120
|
*
|
|
122
121
|
* Duration options take a string (`"5m"`, `"24h"`; server-validated) or a
|
|
@@ -132,7 +131,7 @@ export declare class QURLClient {
|
|
|
132
131
|
*
|
|
133
132
|
* Convenience for one-off scripts (qurl-go: `Client.CreatePortalForURL`).
|
|
134
133
|
* The returned resource handle is reusable for minting more portals but
|
|
135
|
-
* carries
|
|
134
|
+
* carries the CRID, verification key, and the caller-supplied target URL; use
|
|
136
135
|
* {@link protectUrl} when you need the full server-populated resource
|
|
137
136
|
* metadata. Portal options match {@link createPortal}; `targetUrl` gets
|
|
138
137
|
* the same malformed-URL and embedded-credentials rejection as
|
|
@@ -230,8 +229,9 @@ export declare class QURLClient {
|
|
|
230
229
|
/**
|
|
231
230
|
* Get a qURL resource and its access tokens.
|
|
232
231
|
*
|
|
233
|
-
* Accepts
|
|
234
|
-
* prefix); the API resolves
|
|
232
|
+
* Accepts a CRID or qURL
|
|
233
|
+
* display ID (`q_` prefix); the API resolves display IDs to the parent
|
|
234
|
+
* resource automatically.
|
|
235
235
|
*/
|
|
236
236
|
get(id: string): Promise<QURL>;
|
|
237
237
|
/**
|
|
@@ -262,17 +262,17 @@ export declare class QURLClient {
|
|
|
262
262
|
/**
|
|
263
263
|
* Delete (revoke) a qURL resource and all its access tokens.
|
|
264
264
|
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
265
|
+
* Requires a CRID returned by the API. To revoke one qURL, use
|
|
266
|
+
* {@link revokeResourceQurl} instead.
|
|
267
|
+
*
|
|
268
|
+
* @throws {ValidationError} If `id` is not a canonical CRID.
|
|
269
269
|
*/
|
|
270
270
|
delete(id: string): Promise<void>;
|
|
271
271
|
/**
|
|
272
272
|
* Extend a qURL's expiration.
|
|
273
273
|
*
|
|
274
|
-
* Accepts
|
|
275
|
-
* prefix).
|
|
274
|
+
* Accepts a CRID or qURL
|
|
275
|
+
* display ID (`q_` prefix). Delegates to {@link update} with only the
|
|
276
276
|
* expiration fields. `ExtendInput` shares its `extend_by` / `expires_at`
|
|
277
277
|
* fields with `UpdateInput` but is *narrower in two ways*: (1) exactly
|
|
278
278
|
* one of the two must be present (XOR via `?: never`), where `UpdateInput`
|
|
@@ -287,8 +287,9 @@ export declare class QURLClient {
|
|
|
287
287
|
/**
|
|
288
288
|
* Update a qURL — extend expiration, change description, rename tags.
|
|
289
289
|
*
|
|
290
|
-
* Accepts
|
|
291
|
-
* prefix); the API resolves
|
|
290
|
+
* Accepts a CRID or qURL
|
|
291
|
+
* display ID (`q_` prefix); the API resolves display IDs to the parent
|
|
292
|
+
* resource automatically.
|
|
292
293
|
*/
|
|
293
294
|
update(id: string, input: UpdateInput, options?: RequestOptions): Promise<QURL>;
|
|
294
295
|
/**
|
|
@@ -296,8 +297,9 @@ export declare class QURLClient {
|
|
|
296
297
|
*
|
|
297
298
|
* Portal-flow equivalent: {@link createPortal}.
|
|
298
299
|
*
|
|
299
|
-
* Accepts
|
|
300
|
-
* prefix); the API resolves
|
|
300
|
+
* Accepts a CRID or qURL
|
|
301
|
+
* display ID (`q_` prefix); the API resolves display IDs to the parent
|
|
302
|
+
* resource automatically.
|
|
301
303
|
*
|
|
302
304
|
* Passing `{}` or an object with all fields `null`/`undefined` is
|
|
303
305
|
* equivalent to omitting the second argument: no body is sent, and the
|
|
@@ -346,6 +348,13 @@ export declare class QURLClient {
|
|
|
346
348
|
* Portal-flow equivalent: {@link createPortal} (same endpoint).
|
|
347
349
|
*/
|
|
348
350
|
createQurlForResource(id: string, input?: CreateQurlForResourceInput, options?: RequestOptions): Promise<CreateOutput>;
|
|
351
|
+
/**
|
|
352
|
+
* Mint a fresh, short-lived share link for an existing CRID.
|
|
353
|
+
* The returned link is secret and is not retrievable after this response.
|
|
354
|
+
* If you hold the trusted resource DER SPKI, call `ShareLink.verifyCrid()`
|
|
355
|
+
* before delivery to verify that the response CRID derives from that key.
|
|
356
|
+
*/
|
|
357
|
+
shareResource(id: string, options?: ShareResourceOptions, requestOptions?: RequestOptions): Promise<ShareLink>;
|
|
349
358
|
/** Revoke a specific qURL token on a resource. */
|
|
350
359
|
revokeResourceQurl(id: string, qurlId: string): Promise<void>;
|
|
351
360
|
/** Update a specific qURL token on a resource. */
|
|
@@ -509,20 +518,19 @@ export declare class QURLClient {
|
|
|
509
518
|
/**
|
|
510
519
|
* A LayerV-protected resource, bound to the client that produced it.
|
|
511
520
|
*
|
|
512
|
-
* Obtained from {@link QURLClient.protectUrl}
|
|
513
|
-
* {@link QURLClient.connectorResource}, or {@link QURLClient.resourceById} —
|
|
521
|
+
* Obtained from {@link QURLClient.protectUrl} or {@link QURLClient.resourceByCrid} —
|
|
514
522
|
* not constructed directly. Mint short-lived access links for the resource
|
|
515
523
|
* with {@link createPortal} (qurl-go: `qurl.Resource`).
|
|
516
524
|
*/
|
|
517
525
|
export declare class ProtectedResource {
|
|
518
526
|
#private;
|
|
519
|
-
/** The
|
|
520
|
-
readonly
|
|
527
|
+
/** The public resource CRID. */
|
|
528
|
+
readonly crid: string;
|
|
521
529
|
/** The private URL protected by this resource, when known. */
|
|
522
530
|
readonly targetUrl?: string;
|
|
523
531
|
/**
|
|
524
532
|
* Full server-populated {@link Resource} metadata when the handle came
|
|
525
|
-
* from an API response; `undefined` for {@link QURLClient.
|
|
533
|
+
* from an API response; `undefined` for {@link QURLClient.resourceByCrid}
|
|
526
534
|
* handles and for the one-call {@link QURLClient.createPortalForUrl} path.
|
|
527
535
|
*/
|
|
528
536
|
readonly details?: Resource;
|
|
@@ -540,3 +548,31 @@ export declare class ProtectedResource {
|
|
|
540
548
|
*/
|
|
541
549
|
createPortal(opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
|
|
542
550
|
}
|
|
551
|
+
/** Result of idempotently ensuring a qURL Connector resource. */
|
|
552
|
+
export interface EnsureConnectorResourceResult {
|
|
553
|
+
resource: ConnectorResource;
|
|
554
|
+
foundExisting: boolean;
|
|
555
|
+
}
|
|
556
|
+
declare const CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN: unique symbol;
|
|
557
|
+
/**
|
|
558
|
+
* A validated qURL Connector resource bound to the client that loaded it.
|
|
559
|
+
*
|
|
560
|
+
* Resource identity, reverse-routing identity, and NHP admission identity are
|
|
561
|
+
* distinct server-issued values. Callers must consume each field verbatim and
|
|
562
|
+
* must never derive one from another.
|
|
563
|
+
*/
|
|
564
|
+
export declare class ConnectorResource {
|
|
565
|
+
#private;
|
|
566
|
+
readonly resourcePublicKey: string;
|
|
567
|
+
/** Required public identifier, verified against the returned resource key. */
|
|
568
|
+
readonly crid: string;
|
|
569
|
+
readonly connectorRoutingId: string;
|
|
570
|
+
readonly knockResourceId: string;
|
|
571
|
+
readonly slug: string;
|
|
572
|
+
readonly alias?: string;
|
|
573
|
+
/** @internal Instances are returned by QURLClient after wire validation. */
|
|
574
|
+
constructor(client: QURLClient, details: Resource, validationToken: typeof CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN);
|
|
575
|
+
/** Mint a short-lived portal for this Connector resource. */
|
|
576
|
+
createPortal(opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
|
|
577
|
+
}
|
|
578
|
+
export {};
|