@layervai/qurl 0.3.0 → 0.3.1
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 +153 -16
- package/dist/cjs/client.d.ts +164 -4
- package/dist/cjs/client.js +483 -6
- package/dist/cjs/errors.d.ts +4 -0
- package/dist/cjs/errors.js +5 -1
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/types.d.ts +83 -0
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +164 -4
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +482 -6
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/errors.d.ts +4 -0
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +4 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +3 -3
- 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/types.d.ts +83 -0
- 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 +1 -1
package/README.md
CHANGED
|
@@ -4,13 +4,34 @@
|
|
|
4
4
|
[](https://github.com/layervai/qurl-typescript/actions/workflows/ci.yml)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Use the LayerV [qURL™ Platform](https://docs.layerv.ai) from TypeScript: protect a
|
|
8
|
+
private URL once, then mint short-lived portal links for it.**
|
|
8
9
|
|
|
9
10
|
> **Quantum URL (qURL)** · The internet has a hidden layer. This is how you enter.
|
|
10
11
|
|
|
12
|
+
Portal recipients do not need LayerV credentials, API keys, or SDK state. They
|
|
13
|
+
open the qURL link. Credentials are only for software that protects URLs or
|
|
14
|
+
creates portals.
|
|
15
|
+
|
|
11
16
|
## Why qURL?
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
Agents and services increasingly need to reach private MCP servers, APIs, and
|
|
19
|
+
internal tools. The issue is visibility: every standing public endpoint becomes
|
|
20
|
+
inventory for scanners, fingerprinting, credential attacks, and AI-assisted
|
|
21
|
+
probing before a legitimate user or agent ever arrives.
|
|
22
|
+
|
|
23
|
+
Opening an inbound port, running a VPN, shipping a bastion, publishing a
|
|
24
|
+
Cloudflare Tunnel or ngrok URL, or passing around a long-lived key all leave
|
|
25
|
+
something durable to find, scan, or steal. qURL flips that model: it is an
|
|
26
|
+
invisibility primitive for authenticated access, and the private resource is
|
|
27
|
+
not public inventory. A portal is **cryptographic, just-in-time permission for
|
|
28
|
+
one actor to reach one private resource** — not another externally visible
|
|
29
|
+
endpoint in front of the same service:
|
|
30
|
+
|
|
31
|
+
- **Time-limited** — portals expire after minutes, hours, or days
|
|
32
|
+
- **IP-scoped** — access is granted only to the requesting IP via NHP
|
|
33
|
+
- **Auditable** — every access is logged with who, when, and from where
|
|
34
|
+
- **Revocable** — kill access instantly if something goes wrong
|
|
14
35
|
|
|
15
36
|
## Installation
|
|
16
37
|
|
|
@@ -18,16 +39,99 @@ AI agents need to access protected resources — APIs, databases, internal tools
|
|
|
18
39
|
npm install @layervai/qurl
|
|
19
40
|
```
|
|
20
41
|
|
|
21
|
-
Requires Node.js
|
|
42
|
+
Requires Node.js 20+ and has **no runtime dependencies**. Both `import { QURLClient } from '@layervai/qurl'` (ESM) and `const { QURLClient } = require('@layervai/qurl')` (CJS) work.
|
|
22
43
|
|
|
23
|
-
##
|
|
44
|
+
## Quickstart
|
|
24
45
|
|
|
25
46
|
```typescript
|
|
26
47
|
import { QURLClient } from '@layervai/qurl';
|
|
27
48
|
|
|
28
49
|
const client = new QURLClient({ apiKey: 'YOUR_API_KEY' });
|
|
29
50
|
|
|
30
|
-
|
|
51
|
+
const resource = await client.protectUrl('https://internal.example.com/dashboard');
|
|
52
|
+
const portal = await resource.createPortal({ validFor: '5m' });
|
|
53
|
+
|
|
54
|
+
console.log(portal.link); // Share this link — recipients need no credentials
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
That is the core flow:
|
|
58
|
+
|
|
59
|
+
| Step | Call | What you provide |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| Protect a private URL | `client.protectUrl` | The target URL you already know |
|
|
62
|
+
| Mint a short-lived access link | `resource.createPortal` | The returned resource handle |
|
|
63
|
+
|
|
64
|
+
`protectUrl` is idempotent for the same account and target URL: protecting the
|
|
65
|
+
same URL again returns the existing resource. `validFor` accepts a duration
|
|
66
|
+
string (`'5m'`, `'24h'`) or a number of milliseconds (whole seconds, at least
|
|
67
|
+
one minute); prefer short portal lifetimes.
|
|
68
|
+
|
|
69
|
+
If qURL Connector already protects the service, use the connector id instead
|
|
70
|
+
of calling `protectUrl`:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const resource = await client.connectorResource('prod-dashboard');
|
|
74
|
+
const portal = await resource.createPortal({ validFor: '5m' });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
If you persist the resource id, future calls do not need to recreate the
|
|
78
|
+
handle (no API call is made until you mint):
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const resource = client.resourceById('r_demo1234567');
|
|
82
|
+
const portal = await resource.createPortal({ validFor: '1h' });
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
For one-off scripts, `client.createPortalForUrl` combines the two API calls
|
|
86
|
+
and returns both the portal and a reusable resource handle. The handle carries
|
|
87
|
+
the resource id and target URL; use `protectUrl` when you need the full
|
|
88
|
+
server-populated resource metadata:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const { portal, resource } = await client.createPortalForUrl(
|
|
92
|
+
'https://internal.example.com/dashboard',
|
|
93
|
+
{ validFor: '5m' },
|
|
94
|
+
);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Portal options mirror qurl-go:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const portal = await resource.createPortal({
|
|
101
|
+
validFor: 5 * 60 * 1000, // milliseconds work too
|
|
102
|
+
label: 'Alice from Acme',
|
|
103
|
+
oneTimeUse: true,
|
|
104
|
+
maxSessions: 1,
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Opening Portals
|
|
109
|
+
|
|
110
|
+
Most recipients open qURL links directly and do not use this SDK at all. If
|
|
111
|
+
you are building a service or agent that opens received qURL links
|
|
112
|
+
programmatically, `enterPortal` accepts a full link or a bare access token,
|
|
113
|
+
grants network access for the caller's IP, and returns the reachable resource:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const handle = await client.enterPortal(link);
|
|
117
|
+
console.log(handle.resourceUrl); // The reachable resource location
|
|
118
|
+
console.log(handle.openSeconds); // How long access stays open
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Unlike qurl-go's offline `EnterPortal`, this SDK opens links through the
|
|
122
|
+
LayerV API: the client needs an API key with the `qurl:resolve` scope.
|
|
123
|
+
`enterPortal` fails closed — if access is granted but no resource URL comes
|
|
124
|
+
back, it throws instead of returning an empty handle.
|
|
125
|
+
|
|
126
|
+
## REST-Shaped API (Compatibility)
|
|
127
|
+
|
|
128
|
+
The original REST-shaped methods remain fully supported and share the same
|
|
129
|
+
client. Use them for the qURL/resource/token management surface that has no
|
|
130
|
+
portal-verb equivalent (listing, updating, revoking, quotas, webhooks, ...) or
|
|
131
|
+
if you already build on them:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// Create a protected link (portal equivalent: createPortalForUrl)
|
|
31
135
|
const result = await client.create({
|
|
32
136
|
target_url: 'https://api.example.com/data',
|
|
33
137
|
expires_in: '24h',
|
|
@@ -35,8 +139,8 @@ const result = await client.create({
|
|
|
35
139
|
});
|
|
36
140
|
console.log(result.qurl_link);
|
|
37
141
|
|
|
38
|
-
// Resolve a token (
|
|
39
|
-
const access = await client.resolve('
|
|
142
|
+
// Resolve a token headlessly (portal equivalent: enterPortal)
|
|
143
|
+
const access = await client.resolve('at_k8xqp9h2sj9lx7r4a');
|
|
40
144
|
console.log(`Access granted to ${access.target_url} for ${access.access_grant?.expires_in}s`);
|
|
41
145
|
```
|
|
42
146
|
|
|
@@ -54,7 +158,18 @@ console.log(`Access granted to ${access.target_url} for ${access.access_grant?.e
|
|
|
54
158
|
| `userAgent` | No | `qurl-typescript/<version>` |
|
|
55
159
|
| `debug` | No | `false` |
|
|
56
160
|
|
|
57
|
-
###
|
|
161
|
+
### Portal methods
|
|
162
|
+
|
|
163
|
+
| Method | Description |
|
|
164
|
+
|--------|-------------|
|
|
165
|
+
| `protectUrl(targetUrl, opts?)` | Protect a private URL → portal-minting `ProtectedResource` handle |
|
|
166
|
+
| `resource.createPortal(opts?)` / `createPortal(resourceOrId, opts?)` | Mint a short-lived portal link (`Portal`) |
|
|
167
|
+
| `createPortalForUrl(targetUrl, opts?)` | Protect + mint in one API call → `{ portal, resource }` |
|
|
168
|
+
| `connectorResource(connectorId)` | Handle for a service qURL Connector already protects |
|
|
169
|
+
| `resourceById(id)` | Handle from a stored resource id (no API call) |
|
|
170
|
+
| `enterPortal(linkOrToken)` | Open a qURL link programmatically → `ResourceHandle` |
|
|
171
|
+
|
|
172
|
+
### REST-shaped methods
|
|
58
173
|
|
|
59
174
|
| Method | Description |
|
|
60
175
|
|--------|-------------|
|
|
@@ -122,7 +237,7 @@ Non-400 errors (401, 403, 429, 5xx, and unexpected 400 body shapes) still throw
|
|
|
122
237
|
|
|
123
238
|
## Error Handling
|
|
124
239
|
|
|
125
|
-
|
|
240
|
+
Match errors by type, not message text — every failure throws a typed `QURLError` subclass, and message wording is not part of the API contract:
|
|
126
241
|
|
|
127
242
|
```typescript
|
|
128
243
|
import {
|
|
@@ -134,16 +249,16 @@ import {
|
|
|
134
249
|
} from '@layervai/qurl';
|
|
135
250
|
|
|
136
251
|
try {
|
|
137
|
-
await client.
|
|
252
|
+
await client.enterPortal('https://qurl.link/#at_k8xqp9h2sj9lx7r4a');
|
|
138
253
|
} catch (err) {
|
|
139
|
-
if (err instanceof
|
|
140
|
-
console.error('Invalid input:', err.invalidFields);
|
|
141
|
-
} else if (err instanceof RateLimitError) {
|
|
142
|
-
console.error(`Rate limited — retry after ${err.retryAfter}s`);
|
|
143
|
-
} else if (err instanceof AuthenticationError) {
|
|
254
|
+
if (err instanceof AuthenticationError) {
|
|
144
255
|
console.error('Bad API key');
|
|
145
256
|
} else if (err instanceof NotFoundError) {
|
|
146
|
-
console.error('
|
|
257
|
+
console.error('Portal doesn\'t exist or already expired');
|
|
258
|
+
} else if (err instanceof RateLimitError) {
|
|
259
|
+
console.error(`Rate limited — retry after ${err.retryAfter}s`);
|
|
260
|
+
} else if (err instanceof ValidationError) {
|
|
261
|
+
console.error('Invalid input:', err.detail, err.invalidFields);
|
|
147
262
|
} else if (err instanceof QURLError) {
|
|
148
263
|
console.error(`API error [${err.code}]: ${err.detail}`);
|
|
149
264
|
}
|
|
@@ -161,6 +276,13 @@ try {
|
|
|
161
276
|
| `NetworkError` | — | Connection failure |
|
|
162
277
|
| `TimeoutError` | — | Request exceeded timeout |
|
|
163
278
|
|
|
279
|
+
Client-detected failures use `status: 0` with a discriminating `code`:
|
|
280
|
+
`"client_validation"` for bad input caught before a request, and — on the
|
|
281
|
+
portal surface — `"resource_not_found"` / `"ambiguous_resource"` when
|
|
282
|
+
`connectorResource` cannot resolve a connector id to exactly one resource,
|
|
283
|
+
and `"unexpected_response"` when a response is missing required fields (e.g.
|
|
284
|
+
`enterPortal` failing closed on a grant with no resource URL).
|
|
285
|
+
|
|
164
286
|
## Pagination
|
|
165
287
|
|
|
166
288
|
```typescript
|
|
@@ -210,8 +332,23 @@ await client.create(
|
|
|
210
332
|
);
|
|
211
333
|
```
|
|
212
334
|
|
|
335
|
+
The portal verbs take the same per-call options as their final argument, e.g.
|
|
336
|
+
`resource.createPortal({ validFor: '5m' }, { idempotencyKey: 'mint-alice-1' })`.
|
|
337
|
+
|
|
213
338
|
SDK-generated keys require `globalThis.crypto.getRandomValues`, which is available in supported Node 20+ runtimes and modern edge/browser runtimes. In constrained runtimes without Web Crypto, pass a caller-provided key with `idempotencyKey`; otherwise POST/PATCH calls throw `RuntimeError` before sending a request.
|
|
214
339
|
|
|
340
|
+
## Security Notes
|
|
341
|
+
|
|
342
|
+
- Treat API keys and qURL links like credentials. Do not log them.
|
|
343
|
+
- Prefer short portal lifetimes such as `validFor: '5m'`.
|
|
344
|
+
- Do not ask portal recipients to handle credentials. Recipients only need
|
|
345
|
+
the link.
|
|
346
|
+
- `protectUrl` and `createPortalForUrl` reject malformed target URLs and URLs
|
|
347
|
+
with embedded credentials (`https://user:pass@...`) before any request,
|
|
348
|
+
matching qurl-go.
|
|
349
|
+
- The programmatic opener fails closed: `enterPortal` throws when access is
|
|
350
|
+
granted without a resource URL, and its error messages never echo the link.
|
|
351
|
+
|
|
215
352
|
## Versioning & breaking changes
|
|
216
353
|
|
|
217
354
|
This SDK is pre-1.0; breaking changes between minor versions are possible until the API surface stabilizes. Significant changes are called out in [`CHANGELOG.md`](CHANGELOG.md) and in the corresponding GitHub release notes.
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccessCodeListOutput, AgentBootstrapInput, AgentBootstrapOutput, ApiKey, ApiKeyListOutput, BillingInvoiceListOutput, BatchCreateInput, BatchCreateOutput, ClientOptions, ConnectorInstallation, ConnectorInstallationListOutput, CreateAccessCodeInput, CreateAccessCodeOutput, CreateApiKeyInput, CreateApiKeyOutput, CreateBillingCheckoutInput, CreateInput, CreateOutput, CreateQurlForResourceInput, CreateResourceInput, CreateWebhookInput, Customer, CheckoutSession, Domain, DomainListOutput, DomainVerifyResult, ExtendInput, Invoice, ListInput, ListOutput, ListApiKeysInput, ListBillingInvoicesInput, ListConnectorInstallationsInput, ListDomainsInput, ListWebhookDeliveriesInput, ListWebhooksInput, MintInput, MintOutput, PortalSession, QURL, QurlSummary, Quota, RedeemAccessCodeInput, RedeemAccessCodeOutput, RegisterDomainInput, Resource, ResourceDetail, ResourceListInput, ResourceListOutput, RequestOptions, ResolveInput, ResolveOutput, SessionListOutput, SessionTerminateOutput, UpdateApiKeyInput, UpdateCustomerInput, UpdateInput, UpdateResourceInput, UpdateResourceQurlInput, UpdateWebhookInput, UsageCurrentPeriod, UsageDaily, Webhook, WebhookDelivery, WebhookDeliveryListOutput, WebhookEventTypeInfo, WebhookListOutput, WebhookWithSecret } from "./types.js";
|
|
1
|
+
import type { AccessCodeListOutput, AgentBootstrapInput, AgentBootstrapOutput, ApiKey, ApiKeyListOutput, 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, UpdateApiKeyInput, UpdateCustomerInput, UpdateInput, UpdateResourceInput, UpdateResourceQurlInput, UpdateWebhookInput, UsageCurrentPeriod, UsageDaily, Webhook, WebhookDelivery, WebhookDeliveryListOutput, WebhookEventTypeInfo, WebhookListOutput, WebhookWithSecret } from "./types.js";
|
|
2
2
|
/** qURL API client. */
|
|
3
3
|
export declare class QURLClient {
|
|
4
4
|
private readonly baseUrl;
|
|
@@ -49,7 +49,119 @@ export declare class QURLClient {
|
|
|
49
49
|
*/
|
|
50
50
|
private validateBatchCreateResponse;
|
|
51
51
|
private paginateAll;
|
|
52
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Protect a private URL — create or reuse its LayerV resource.
|
|
54
|
+
*
|
|
55
|
+
* The first step of the portal flow (qurl-go: `Client.ProtectURL`).
|
|
56
|
+
* Protect the URL once, then mint short-lived links for it with
|
|
57
|
+
* {@link ProtectedResource.createPortal}:
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* const resource = await client.protectUrl("https://internal.example.com/dashboard");
|
|
61
|
+
* const portal = await resource.createPortal({ validFor: "5m" });
|
|
62
|
+
* share(portal.link);
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* Protecting the same target URL again returns the existing resource.
|
|
66
|
+
* Malformed URLs and URLs with embedded credentials (userinfo) are
|
|
67
|
+
* rejected before any request, mirroring qurl-go. REST-shaped equivalent:
|
|
68
|
+
* {@link createResource}.
|
|
69
|
+
*/
|
|
70
|
+
protectUrl(targetUrl: string, opts?: ProtectUrlOptions, requestOptions?: RequestOptions): Promise<ProtectedResource>;
|
|
71
|
+
/**
|
|
72
|
+
* Return a portal-minting handle for a stored resource id, without an API
|
|
73
|
+
* call.
|
|
74
|
+
*
|
|
75
|
+
* Use when you persisted a LayerV resource id and want to mint more
|
|
76
|
+
* portals for it (qurl-go: `Client.ResourceByID`):
|
|
77
|
+
*
|
|
78
|
+
* ```ts
|
|
79
|
+
* const resource = client.resourceById("r_demo1234567");
|
|
80
|
+
* const portal = await resource.createPortal({ validFor: "1h" });
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* The handle carries no server metadata; use {@link getResource} when you
|
|
84
|
+
* need the full resource details.
|
|
85
|
+
*/
|
|
86
|
+
resourceById(id: string): ProtectedResource;
|
|
87
|
+
/**
|
|
88
|
+
* Return the resource qURL Connector created for `connectorId`.
|
|
89
|
+
*
|
|
90
|
+
* Use when qURL Connector already protects the service — do not call
|
|
91
|
+
* {@link protectUrl} again for the same service. The connector id is the
|
|
92
|
+
* resource slug LayerV stores for that connector; the LayerV API performs
|
|
93
|
+
* the slug lookup, and the SDK confirms the returned alias matches
|
|
94
|
+
* `connectorId` before binding the handle (qurl-go:
|
|
95
|
+
* `Client.ConnectorResource`).
|
|
96
|
+
*
|
|
97
|
+
* Throws {@link NotFoundError} (`status: 0`, `code: "resource_not_found"`)
|
|
98
|
+
* when no resource exists for the connector id, {@link QURLError}
|
|
99
|
+
* (`code: "ambiguous_resource"`) when the lookup returns more than one
|
|
100
|
+
* resource, and {@link ValidationError} (`code: "unexpected_response"`)
|
|
101
|
+
* when the returned resource's alias is missing or does not match.
|
|
102
|
+
*/
|
|
103
|
+
connectorResource(connectorId: string): Promise<ProtectedResource>;
|
|
104
|
+
/**
|
|
105
|
+
* Mint a short-lived qURL link (a portal) for a protected resource.
|
|
106
|
+
*
|
|
107
|
+
* A portal is cryptographic, just-in-time permission for one actor to
|
|
108
|
+
* reach one private resource. Recipients open `portal.link` directly and
|
|
109
|
+
* need no LayerV credentials. Prefer short lifetimes such as
|
|
110
|
+
* `{ validFor: "5m" }`. Accepts a {@link ProtectedResource} handle or a
|
|
111
|
+
* resource id (`r_` prefix). REST-shaped equivalents: {@link mintLink} /
|
|
112
|
+
* {@link createQurlForResource}.
|
|
113
|
+
*
|
|
114
|
+
* Duration options take a string (`"5m"`, `"24h"`; server-validated) or a
|
|
115
|
+
* number of milliseconds with qurl-go's client-side guardrails: whole
|
|
116
|
+
* seconds only, at least one minute for `validFor` and one second for
|
|
117
|
+
* `sessionDuration`. `maxSessions: 0` is sent explicitly and means
|
|
118
|
+
* unlimited; blank labels are rejected.
|
|
119
|
+
*/
|
|
120
|
+
createPortal(resource: ProtectedResource | string, opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
|
|
121
|
+
/**
|
|
122
|
+
* Protect `targetUrl` and mint a portal in one API call.
|
|
123
|
+
*
|
|
124
|
+
* Convenience for one-off scripts (qurl-go: `Client.CreatePortalForURL`).
|
|
125
|
+
* The returned resource handle is reusable for minting more portals but
|
|
126
|
+
* carries only the resource id and the caller-supplied target URL; use
|
|
127
|
+
* {@link protectUrl} when you need the full server-populated resource
|
|
128
|
+
* metadata. Portal options match {@link createPortal}; `targetUrl` gets
|
|
129
|
+
* the same malformed-URL and embedded-credentials rejection as
|
|
130
|
+
* {@link protectUrl}. REST-shaped equivalent: {@link create}.
|
|
131
|
+
*/
|
|
132
|
+
createPortalForUrl(targetUrl: string, opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<{
|
|
133
|
+
portal: Portal;
|
|
134
|
+
resource: ProtectedResource;
|
|
135
|
+
}>;
|
|
136
|
+
/**
|
|
137
|
+
* Open a qURL link programmatically and return the reachable resource.
|
|
138
|
+
*
|
|
139
|
+
* For services and agents that open received qURL links in code — human
|
|
140
|
+
* recipients just open the link. Accepts a full platform link
|
|
141
|
+
* (`https://qurl.link/#at_...`) or a bare access token. Triggers an NHP
|
|
142
|
+
* knock that grants network access for the caller's IP and returns a
|
|
143
|
+
* {@link ResourceHandle} with the reachable resource URL and access
|
|
144
|
+
* lifetime.
|
|
145
|
+
*
|
|
146
|
+
* Unlike qurl-go's `EnterPortal` (which verifies links locally and needs
|
|
147
|
+
* no LayerV credentials), this SDK opens links through the LayerV API:
|
|
148
|
+
* the client needs an API key with the `qurl:resolve` scope. REST-shaped
|
|
149
|
+
* equivalent: {@link resolve}.
|
|
150
|
+
*
|
|
151
|
+
* Throws {@link ValidationError} (`code: "client_validation"`) when no
|
|
152
|
+
* access token can be extracted from `qurlLink`, and
|
|
153
|
+
* (`code: "unexpected_response"`) when access is granted but the API
|
|
154
|
+
* reports no resource URL — it fails closed rather than returning an
|
|
155
|
+
* empty handle. Error messages never echo the link; qURL links are
|
|
156
|
+
* credentials.
|
|
157
|
+
*/
|
|
158
|
+
enterPortal(qurlLink: string, requestOptions?: RequestOptions): Promise<ResourceHandle>;
|
|
159
|
+
/**
|
|
160
|
+
* Create a new qURL.
|
|
161
|
+
*
|
|
162
|
+
* Portal-flow equivalent: {@link createPortalForUrl} (same endpoint,
|
|
163
|
+
* returns a {@link Portal} plus a reusable resource handle).
|
|
164
|
+
*/
|
|
53
165
|
create(input: CreateInput, options?: RequestOptions): Promise<CreateOutput>;
|
|
54
166
|
/**
|
|
55
167
|
* Batch create multiple qURLs (1-`MAX_BATCH_ITEMS` items).
|
|
@@ -171,6 +283,8 @@ export declare class QURLClient {
|
|
|
171
283
|
/**
|
|
172
284
|
* Mint a new access link for a qURL.
|
|
173
285
|
*
|
|
286
|
+
* Portal-flow equivalent: {@link createPortal}.
|
|
287
|
+
*
|
|
174
288
|
* Accepts either a resource ID (`r_` prefix) or a qURL display ID (`q_`
|
|
175
289
|
* prefix); the API resolves `q_` IDs to the parent resource automatically.
|
|
176
290
|
*
|
|
@@ -182,6 +296,9 @@ export declare class QURLClient {
|
|
|
182
296
|
/**
|
|
183
297
|
* Resolve a qURL access token (headless).
|
|
184
298
|
*
|
|
299
|
+
* Portal-flow equivalent: {@link enterPortal} (also accepts full qURL
|
|
300
|
+
* links and fails closed when no resource URL is returned).
|
|
301
|
+
*
|
|
185
302
|
* Triggers an NHP knock to grant network access for the caller's IP.
|
|
186
303
|
* Requires `qurl:resolve` scope on the API key.
|
|
187
304
|
*
|
|
@@ -201,7 +318,12 @@ export declare class QURLClient {
|
|
|
201
318
|
* iteration begins.
|
|
202
319
|
*/
|
|
203
320
|
listAllResources(input?: Omit<ResourceListInput, "cursor">): AsyncGenerator<Resource, void, undefined>;
|
|
204
|
-
/**
|
|
321
|
+
/**
|
|
322
|
+
* Create a resource directly.
|
|
323
|
+
*
|
|
324
|
+
* Portal-flow equivalent for URL resources: {@link protectUrl} (returns
|
|
325
|
+
* a portal-minting handle).
|
|
326
|
+
*/
|
|
205
327
|
createResource(input: CreateResourceInput, options?: RequestOptions): Promise<Resource>;
|
|
206
328
|
/** Get one resource plus its bounded qURL preview. */
|
|
207
329
|
getResource(id: string): Promise<ResourceDetail>;
|
|
@@ -209,7 +331,11 @@ export declare class QURLClient {
|
|
|
209
331
|
updateResource(id: string, input: UpdateResourceInput, options?: RequestOptions): Promise<Resource>;
|
|
210
332
|
/** Revoke a resource and all of its qURLs. */
|
|
211
333
|
deleteResource(id: string): Promise<void>;
|
|
212
|
-
/**
|
|
334
|
+
/**
|
|
335
|
+
* Mint a qURL against an existing resource.
|
|
336
|
+
*
|
|
337
|
+
* Portal-flow equivalent: {@link createPortal} (same endpoint).
|
|
338
|
+
*/
|
|
213
339
|
createQurlForResource(id: string, input?: CreateQurlForResourceInput, options?: RequestOptions): Promise<CreateOutput>;
|
|
214
340
|
/** Revoke a specific qURL token on a resource. */
|
|
215
341
|
revokeResourceQurl(id: string, qurlId: string): Promise<void>;
|
|
@@ -370,3 +496,37 @@ export declare class QURLClient {
|
|
|
370
496
|
private retryDelay;
|
|
371
497
|
private classifyFetchError;
|
|
372
498
|
}
|
|
499
|
+
/**
|
|
500
|
+
* A LayerV-protected resource, bound to the client that produced it.
|
|
501
|
+
*
|
|
502
|
+
* Obtained from {@link QURLClient.protectUrl},
|
|
503
|
+
* {@link QURLClient.connectorResource}, or {@link QURLClient.resourceById} —
|
|
504
|
+
* not constructed directly. Mint short-lived access links for the resource
|
|
505
|
+
* with {@link createPortal} (qurl-go: `qurl.Resource`).
|
|
506
|
+
*/
|
|
507
|
+
export declare class ProtectedResource {
|
|
508
|
+
#private;
|
|
509
|
+
/** The LayerV resource id (`r_` prefix). */
|
|
510
|
+
readonly id: string;
|
|
511
|
+
/** The private URL protected by this resource, when known. */
|
|
512
|
+
readonly targetUrl?: string;
|
|
513
|
+
/**
|
|
514
|
+
* Full server-populated {@link Resource} metadata when the handle came
|
|
515
|
+
* from an API response; `undefined` for {@link QURLClient.resourceById}
|
|
516
|
+
* handles and for the one-call {@link QURLClient.createPortalForUrl} path.
|
|
517
|
+
*/
|
|
518
|
+
readonly details?: Resource;
|
|
519
|
+
constructor(client: QURLClient, id: string, targetUrl?: string, details?: Resource);
|
|
520
|
+
/**
|
|
521
|
+
* Whether `resource` was produced by `client`. A static because the
|
|
522
|
+
* binding lives in a private field that only this class can read; used
|
|
523
|
+
* by {@link QURLClient.createPortal} to refuse handles bound to a
|
|
524
|
+
* different client.
|
|
525
|
+
*/
|
|
526
|
+
static isBoundTo(resource: ProtectedResource, client: QURLClient): boolean;
|
|
527
|
+
/**
|
|
528
|
+
* Mint a short-lived qURL link (a portal) for this resource. See
|
|
529
|
+
* {@link QURLClient.createPortal} for the option reference.
|
|
530
|
+
*/
|
|
531
|
+
createPortal(opts?: CreatePortalOptions, requestOptions?: RequestOptions): Promise<Portal>;
|
|
532
|
+
}
|