@layervai/qurl 0.6.0 → 2.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 +332 -15
- package/dist/cjs/client.d.ts +62 -31
- package/dist/cjs/client.js +932 -163
- package/dist/cjs/crid.d.ts +3 -0
- package/dist/cjs/crid.js +62 -0
- package/dist/cjs/errors.d.ts +30 -5
- package/dist/cjs/errors.js +41 -6
- package/dist/cjs/index.d.ts +5 -3
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/node/agent-runtime.d.ts +108 -0
- package/dist/cjs/node/agent-runtime.js +952 -0
- package/dist/cjs/node/agent-state.d.ts +100 -0
- package/dist/cjs/node/agent-state.js +311 -0
- package/dist/cjs/node/agent-transport.d.ts +17 -0
- package/dist/cjs/node/agent-transport.js +118 -0
- package/dist/cjs/node/deployment.d.ts +6 -0
- package/dist/cjs/node/deployment.js +24 -3
- package/dist/cjs/node/file-agent-state.d.ts +15 -0
- package/dist/cjs/node/file-agent-state.js +137 -0
- package/dist/cjs/node/native-loader.cjs +7 -0
- package/dist/cjs/node/native-loader.d.cts +11 -0
- package/dist/cjs/node/native-udp.d.ts +8 -3
- package/dist/cjs/node/native-udp.js +8 -3
- package/dist/cjs/node/nhp-wire.d.ts +9 -0
- package/dist/cjs/node/nhp-wire.js +24 -7
- package/dist/cjs/node/portal-opener.d.ts +7 -0
- package/dist/cjs/node/portal-opener.js +28 -6
- package/dist/cjs/node/provider.d.ts +19 -0
- package/dist/cjs/node/provider.js +153 -0
- package/dist/cjs/node/qv2.d.ts +3 -1
- package/dist/cjs/node/qv2.js +16 -2
- package/dist/cjs/node/relay.d.ts +12 -0
- package/dist/cjs/node/relay.js +111 -0
- package/dist/cjs/node/sealed-agent-state.d.ts +19 -0
- package/dist/cjs/node/sealed-agent-state.js +193 -0
- package/dist/cjs/node/strict-json.d.ts +1 -1
- package/dist/cjs/node/strict-json.js +8 -3
- package/dist/cjs/node.d.ts +14 -0
- package/dist/cjs/node.js +24 -1
- package/dist/cjs/share.d.ts +56 -0
- package/dist/cjs/share.js +113 -0
- package/dist/cjs/types.d.ts +173 -13
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +62 -31
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +932 -164
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/crid.d.ts +4 -0
- package/dist/esm/crid.d.ts.map +1 -0
- package/dist/esm/crid.js +58 -0
- package/dist/esm/crid.js.map +1 -0
- package/dist/esm/errors.d.ts +30 -5
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +39 -5
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +5 -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/node/agent-runtime.d.ts +109 -0
- package/dist/esm/node/agent-runtime.d.ts.map +1 -0
- package/dist/esm/node/agent-runtime.js +946 -0
- package/dist/esm/node/agent-runtime.js.map +1 -0
- package/dist/esm/node/agent-state.d.ts +101 -0
- package/dist/esm/node/agent-state.d.ts.map +1 -0
- package/dist/esm/node/agent-state.js +298 -0
- package/dist/esm/node/agent-state.js.map +1 -0
- package/dist/esm/node/agent-transport.d.ts +18 -0
- package/dist/esm/node/agent-transport.d.ts.map +1 -0
- package/dist/esm/node/agent-transport.js +114 -0
- package/dist/esm/node/agent-transport.js.map +1 -0
- package/dist/esm/node/deployment.d.ts +6 -0
- package/dist/esm/node/deployment.d.ts.map +1 -1
- package/dist/esm/node/deployment.js +23 -3
- package/dist/esm/node/deployment.js.map +1 -1
- package/dist/esm/node/file-agent-state.d.ts +16 -0
- package/dist/esm/node/file-agent-state.d.ts.map +1 -0
- package/dist/esm/node/file-agent-state.js +134 -0
- package/dist/esm/node/file-agent-state.js.map +1 -0
- package/dist/esm/node/native-loader.cjs +7 -0
- package/dist/esm/node/native-loader.d.cts +11 -0
- package/dist/esm/node/native-udp.d.ts +8 -3
- package/dist/esm/node/native-udp.d.ts.map +1 -1
- package/dist/esm/node/native-udp.js +7 -5
- package/dist/esm/node/native-udp.js.map +1 -1
- package/dist/esm/node/nhp-wire.d.ts +9 -0
- package/dist/esm/node/nhp-wire.d.ts.map +1 -1
- package/dist/esm/node/nhp-wire.js +23 -6
- package/dist/esm/node/nhp-wire.js.map +1 -1
- package/dist/esm/node/portal-opener.d.ts +7 -0
- package/dist/esm/node/portal-opener.d.ts.map +1 -1
- package/dist/esm/node/portal-opener.js +28 -6
- package/dist/esm/node/portal-opener.js.map +1 -1
- package/dist/esm/node/provider.d.ts +20 -0
- package/dist/esm/node/provider.d.ts.map +1 -0
- package/dist/esm/node/provider.js +149 -0
- package/dist/esm/node/provider.js.map +1 -0
- package/dist/esm/node/qv2.d.ts +3 -1
- package/dist/esm/node/qv2.d.ts.map +1 -1
- package/dist/esm/node/qv2.js +17 -3
- package/dist/esm/node/qv2.js.map +1 -1
- package/dist/esm/node/relay.d.ts +13 -0
- package/dist/esm/node/relay.d.ts.map +1 -0
- package/dist/esm/node/relay.js +105 -0
- package/dist/esm/node/relay.js.map +1 -0
- package/dist/esm/node/sealed-agent-state.d.ts +20 -0
- package/dist/esm/node/sealed-agent-state.d.ts.map +1 -0
- package/dist/esm/node/sealed-agent-state.js +190 -0
- package/dist/esm/node/sealed-agent-state.js.map +1 -0
- package/dist/esm/node/strict-json.d.ts +1 -1
- package/dist/esm/node/strict-json.d.ts.map +1 -1
- package/dist/esm/node/strict-json.js +8 -3
- package/dist/esm/node/strict-json.js.map +1 -1
- package/dist/esm/node.d.ts +14 -0
- package/dist/esm/node.d.ts.map +1 -1
- package/dist/esm/node.js +7 -0
- package/dist/esm/node.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 +173 -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 +17 -8
package/README.md
CHANGED
|
@@ -88,12 +88,15 @@ checks the non-empty 2048-byte boundary; the API remains authoritative for the
|
|
|
88
88
|
path grammar and the tunnel-only resource gate. `createPortalForUrl` rejects
|
|
89
89
|
this option because it creates a URL resource.
|
|
90
90
|
|
|
91
|
-
`resource.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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)`
|
|
95
98
|
or `getConnectorResourceBySlug(slug)` for read-only lookup and
|
|
96
|
-
`deleteConnectorResource(
|
|
99
|
+
`deleteConnectorResource(crid)` to revoke it. This replaces the old
|
|
97
100
|
alias-based `connectorResource(connectorId)` method.
|
|
98
101
|
`ConnectorResource` instances cannot be constructed directly; the client
|
|
99
102
|
returns them only after validating the complete response contract.
|
|
@@ -101,7 +104,7 @@ returns them only after validating the complete response contract.
|
|
|
101
104
|
`ensureConnectorResource` and `deleteConnectorResource` throw
|
|
102
105
|
`ConnectorResourceOutcomeUnknownError` when a dispatched mutation may have
|
|
103
106
|
committed but its response cannot prove the result. Reconcile by immutable slug
|
|
104
|
-
or
|
|
107
|
+
or CRID before deciding whether to retry. The wrapper deliberately uses
|
|
105
108
|
`status: 0`; the original typed error is available as `cause`, including its
|
|
106
109
|
observed HTTP status.
|
|
107
110
|
|
|
@@ -117,17 +120,20 @@ is terminal: do not retry it. Obtain a new bootstrap key and use normal
|
|
|
117
120
|
owner-authenticated lookup by immutable slug to reconcile the resource first.
|
|
118
121
|
|
|
119
122
|
The API does not apply idempotency replay to DELETE, so after an outcome-unknown
|
|
120
|
-
`deleteConnectorResource` call, reconcile by
|
|
123
|
+
`deleteConnectorResource` call, reconcile by CRID before issuing a
|
|
121
124
|
deliberate retry. A valid exact-201 resource missing only `meta.found_existing`
|
|
122
125
|
is known to have selected that row but still fails as an unwrapped
|
|
123
126
|
`unexpected_response` because required ensure metadata is absent.
|
|
124
127
|
|
|
125
|
-
If you persist the
|
|
128
|
+
If you persist the CRID, future calls do not need to recreate the
|
|
126
129
|
handle (no API call is made until you mint):
|
|
127
130
|
|
|
128
131
|
```typescript
|
|
129
|
-
|
|
130
|
-
|
|
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
|
+
}
|
|
131
137
|
```
|
|
132
138
|
|
|
133
139
|
For one-off scripts, `client.createPortalForUrl` combines the two API calls
|
|
@@ -162,6 +168,174 @@ NHP UDP. It has no relay or HTTP-resolve fallback. Connector assignment and
|
|
|
162
168
|
registration are separate producer operations and are not part of the portal
|
|
163
169
|
opener.
|
|
164
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 and `expectedCRID: advertisedCRID`.
|
|
186
|
+
Obtain `advertisedCRID` independently of the response carrying the link.
|
|
187
|
+
The opener verifies the issuer signature and CRID/key binding before each
|
|
188
|
+
native access request, including renewals. Empty, malformed, unsupported,
|
|
189
|
+
and mismatched CRIDs fail closed. Omitting the option retains link-only access.
|
|
190
|
+
This verifies resource identity, not content or current revocation state.
|
|
191
|
+
The access-binding check accepts only locally registered versions and their
|
|
192
|
+
registered digest widths, matching the Go SDK and browser agent. Upgrade the
|
|
193
|
+
SDK when a new version is activated. This is stricter than the forwarding
|
|
194
|
+
and supplied-key helper below. See the native opener example below.
|
|
195
|
+
`verifyCrid` does not open the link or make a network request.
|
|
196
|
+
|
|
197
|
+
`verifyCrid` verifies that the response CRID derives from the trusted resource
|
|
198
|
+
key; it does not independently bind the secret link fragment to that key.
|
|
199
|
+
It intentionally follows qurl-go's environment-agnostic key-match rule: CRID
|
|
200
|
+
version zero is rejected as reserved, while every other structurally valid
|
|
201
|
+
version is accepted and environment classification is not part of key-to-digest
|
|
202
|
+
verification. It compares all 32 digest bytes for a full CRID and all 24 for
|
|
203
|
+
a truncated CRID; the response determines the digest width. CRIDs use their canonical lowercase base32 spelling; uppercase or
|
|
204
|
+
mixed-case spellings fail closed. Binary inputs of any length are hashed;
|
|
205
|
+
`invalid_crid_key` is reserved for non-binary or unreadable (detached) runtime
|
|
206
|
+
values, and a readable binary key that does not match reports `crid_mismatch`.
|
|
207
|
+
Use `error instanceof CRIDVerificationError` (or `error.status === 0`) to
|
|
208
|
+
distinguish these local verification failures from a server error that happens
|
|
209
|
+
to use the same problem code.
|
|
210
|
+
`qurlId` and `singleUse` are undefined when omitted by the service.
|
|
211
|
+
`ShareLink` keeps `.link` directly readable but redacts it from JSON, Node
|
|
212
|
+
inspection, and object spread to reduce accidental credential logging. Read
|
|
213
|
+
`.link` directly before delivery; do not spread or clone a `ShareLink`, because
|
|
214
|
+
those operations deliberately omit the credential. Browser developer consoles
|
|
215
|
+
can still display non-enumerable properties, so do not log the object there.
|
|
216
|
+
The constructor snapshots a caller-provided expiry, and the `ShareLink`
|
|
217
|
+
properties are frozen. Each `expiresAt` read returns a defensive `Date` copy;
|
|
218
|
+
changing that copy does not change the share metadata.
|
|
219
|
+
|
|
220
|
+
Omitting `ttlSeconds` matches qurl-go's zero-value behavior and requests the
|
|
221
|
+
platform default. TypeScript additionally rejects an explicit zero so a
|
|
222
|
+
computed countdown cannot silently become a longer-lived default credential.
|
|
223
|
+
A response can report `expiresInSeconds: 0`; do not reuse that value as a
|
|
224
|
+
request TTL. Expiry parsing uses the SDK's shared API date parser.
|
|
225
|
+
|
|
226
|
+
The service chooses single-use policy; this matches the Go SDK options.
|
|
227
|
+
|
|
228
|
+
qURL Connector assignment and registration use native UDP through
|
|
229
|
+
`qurl-connector` and `qurl-go`. This package does not expose an HTTP enrollment API.
|
|
230
|
+
Like the Go SDK, credential minting uses HTTPS and token consumption uses
|
|
231
|
+
native UDP. The Go SDK currently exposes minting through its restricted
|
|
232
|
+
`RegisteredAgentResourceHTTPDoer` bridge; this SDK uses `createApiKey`.
|
|
233
|
+
The service controls which credential kinds each caller can mint.
|
|
234
|
+
It can mint the one-shot credential consumed by that native enrollment flow:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const enrollment = await client.createApiKey({
|
|
238
|
+
kind: 'enrollment_token',
|
|
239
|
+
name: 'prod-dashboard enrollment',
|
|
240
|
+
target: 'connector',
|
|
241
|
+
claims: [{ type: 'connector', id: 'prod-dashboard' }],
|
|
242
|
+
expires_in: '15m',
|
|
243
|
+
});
|
|
244
|
+
if (!enrollment.api_key) throw new Error('Enrollment response omitted its one-time token');
|
|
245
|
+
await deliverEnrollmentTokenSecurely(enrollment.api_key);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Durable `api_key` credentials require explicit scopes and do not accept
|
|
249
|
+
`expires_in`; enrollment tokens derive their scopes from `target`/`claims` and
|
|
250
|
+
expire within 24 hours. A connector claim's `id` is its immutable connector
|
|
251
|
+
slug. Request enums are validated against the current service contract;
|
|
252
|
+
response types remain additive for forward-compatible reads, and new request
|
|
253
|
+
enum values require a matching SDK release. Use
|
|
254
|
+
`isApiKeyRequestScope(scope)` to validate response scopes before writing them
|
|
255
|
+
back. Reject unknown scopes; do not filter them out, which can remove permissions.
|
|
256
|
+
When changing only a name, omit `scopes` from `updateApiKey`.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { isApiKeyRequestScope } from '@layervai/qurl';
|
|
260
|
+
|
|
261
|
+
const scopes = key.scopes;
|
|
262
|
+
if (!scopes?.length || !scopes.every(isApiKeyRequestScope)) {
|
|
263
|
+
throw new Error('Cannot reuse these scopes; check the current SDK contract');
|
|
264
|
+
}
|
|
265
|
+
await client.updateApiKey(keyId, { scopes });
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
This credential surface requires the kind-first qurl-service contract at or
|
|
269
|
+
after commit `047cf31e1cdf545e3060e0f9294d738a19fb997b`.
|
|
270
|
+
|
|
271
|
+
## Delegated qURL batches
|
|
272
|
+
|
|
273
|
+
Services with a delegated mint capability can create up to 100 ordered grants
|
|
274
|
+
without an API call per recipient:
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { DelegatedBatchOutcomeUnknownError, QURLClient } from '@layervai/qurl';
|
|
278
|
+
|
|
279
|
+
const client = new QURLClient({ apiKey: process.env.QURL_API_KEY! });
|
|
280
|
+
const key = crypto.randomUUID();
|
|
281
|
+
const mintCapability = process.env.QURL_MINT_CAPABILITY!;
|
|
282
|
+
const recipients = [{ name: 'Alice' }];
|
|
283
|
+
|
|
284
|
+
const accepted = await client
|
|
285
|
+
.createDelegatedQurlBatch(
|
|
286
|
+
{
|
|
287
|
+
mint_capability: mintCapability,
|
|
288
|
+
grants: recipients.map((recipient) => ({ label: recipient.name, expires_in: '1h' })),
|
|
289
|
+
},
|
|
290
|
+
{ idempotencyKey: key },
|
|
291
|
+
)
|
|
292
|
+
.catch((error: unknown) => {
|
|
293
|
+
if (error instanceof DelegatedBatchOutcomeUnknownError && error.batchId) {
|
|
294
|
+
console.error('Poll the accepted batch before retrying', { batchId: error.batchId });
|
|
295
|
+
}
|
|
296
|
+
// A deliberate retry must reuse this key and exact body.
|
|
297
|
+
throw error;
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
let etag = accepted.etag;
|
|
301
|
+
let retryAfter = accepted.retry_after;
|
|
302
|
+
let state;
|
|
303
|
+
const deadline = Date.now() + 60_000;
|
|
304
|
+
for (let polls = 0; polls < 30 && Date.now() < deadline; polls++) {
|
|
305
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
|
|
306
|
+
state = await client.getDelegatedQurlBatch(accepted.batch_id, { etag });
|
|
307
|
+
if (state.http_status === 304) {
|
|
308
|
+
retryAfter = state.retry_after ?? retryAfter;
|
|
309
|
+
} else if (state.http_status === 202) {
|
|
310
|
+
({ etag, retry_after: retryAfter } = state);
|
|
311
|
+
} else {
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (state?.http_status !== 200) throw new Error('Delegated qURL batch did not finish');
|
|
317
|
+
|
|
318
|
+
if (state.results[0]?.status === 'succeeded') {
|
|
319
|
+
const qurlId = state.results[0].qurl.qurl_id;
|
|
320
|
+
const metadata = await client.getDelegatedQurl(qurlId);
|
|
321
|
+
console.log(metadata.status);
|
|
322
|
+
await client.deleteDelegatedQurl(qurlId);
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The idempotency key must contain 32–256 visible ASCII characters. A UUID meets
|
|
327
|
+
this requirement.
|
|
328
|
+
|
|
329
|
+
One read makes one HTTP attempt. The caller owns the poll count, total deadline,
|
|
330
|
+
and wait from `retry_after`. A `304` means the prior ETag is still current. A
|
|
331
|
+
`200` contains the terminal, input-ordered results. Keep each returned qURL as
|
|
332
|
+
an opaque bearer link and apply any deployment-specific origin check before
|
|
333
|
+
publishing it.
|
|
334
|
+
|
|
335
|
+
Delegated DELETE calls are not retried automatically. After a transport failure
|
|
336
|
+
or `503 mutation_outcome_unknown`, retry the same qURL ID until the service
|
|
337
|
+
returns `204`.
|
|
338
|
+
|
|
165
339
|
## Opening Portals
|
|
166
340
|
|
|
167
341
|
Most recipients open qURL links directly and do not use this SDK at all. If
|
|
@@ -201,6 +375,7 @@ const { createPortalOpener } = require('@layervai/qurl/node');
|
|
|
201
375
|
async function uploadPrivateObject(uploadBody) {
|
|
202
376
|
const opener = createPortalOpener({
|
|
203
377
|
qurl: process.env.PRIVATE_UPLOAD_QURL,
|
|
378
|
+
expectedCRID: process.env.PRIVATE_UPLOAD_CRID ?? '', // Independently held; missing fails closed.
|
|
204
379
|
});
|
|
205
380
|
try {
|
|
206
381
|
await opener.start();
|
|
@@ -378,9 +553,9 @@ console.log(`Access granted to ${access.target_url} for ${access.access_grant?.e
|
|
|
378
553
|
| `resource.createPortal(opts?)` / `createPortal(resourceOrId, opts?)` | Mint a short-lived portal link; existing resources can set `targetPath` |
|
|
379
554
|
| `createPortalForUrl(targetUrl, opts?)` | Protect + mint a URL resource; rejects `targetPath` |
|
|
380
555
|
| `ensureConnectorResource(slug, requestOptions?)` | Find or create an active Connector resource by immutable slug |
|
|
381
|
-
| `getConnectorResource(
|
|
382
|
-
| `deleteConnectorResource(
|
|
383
|
-
| `
|
|
556
|
+
| `getConnectorResource(crid)` / `getConnectorResourceBySlug(slug)` | Load a validated Connector resource by immutable identity |
|
|
557
|
+
| `deleteConnectorResource(crid)` | Revoke a Connector resource by CRID |
|
|
558
|
+
| `resourceByCrid(crid)` | Handle from a stored CRID (no API call) |
|
|
384
559
|
| `enterPortal(linkOrToken)` | Open a qURL link programmatically → `ResourceHandle` |
|
|
385
560
|
|
|
386
561
|
### REST-shaped methods
|
|
@@ -442,7 +617,7 @@ if (result.failed > 0) {
|
|
|
442
617
|
|
|
443
618
|
Non-400 errors (401, 403, 429, 5xx, and unexpected 400 body shapes) still throw the appropriate `QURLError` subclass.
|
|
444
619
|
|
|
445
|
-
**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(
|
|
620
|
+
**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).
|
|
446
621
|
|
|
447
622
|
**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.
|
|
448
623
|
|
|
@@ -488,6 +663,7 @@ try {
|
|
|
488
663
|
| `ServerError` | 5xx | Server-side failure |
|
|
489
664
|
| `NetworkError` | — | Connection failure |
|
|
490
665
|
| `TimeoutError` | — | Request exceeded timeout |
|
|
666
|
+
| `DelegatedBatchOutcomeUnknownError` | — | Dispatched batch create needs reconciliation |
|
|
491
667
|
|
|
492
668
|
Client-detected failures use `status: 0` with a discriminating `code`:
|
|
493
669
|
`"client_validation"` for bad input caught before a request, and — on the
|
|
@@ -504,7 +680,8 @@ const page = await client.list({ limit: 10, status: 'active' });
|
|
|
504
680
|
|
|
505
681
|
// Auto-paginate through all results
|
|
506
682
|
for await (const qurl of client.listAll({ status: 'active' })) {
|
|
507
|
-
|
|
683
|
+
if (!qurl.crid) throw new Error("The API response has no resource CRID");
|
|
684
|
+
console.log(qurl.crid);
|
|
508
685
|
}
|
|
509
686
|
```
|
|
510
687
|
|
|
@@ -588,6 +765,10 @@ SDK-generated keys require `globalThis.crypto.getRandomValues`, which is availab
|
|
|
588
765
|
## Security Notes
|
|
589
766
|
|
|
590
767
|
- Treat API keys and qURL links like credentials. Do not log them.
|
|
768
|
+
- Resource/qURL path arguments reject pasted access tokens and full URLs before
|
|
769
|
+
dispatch without echoing the caller input. Pass the opaque identifier returned
|
|
770
|
+
by the API (public resource key or CRID); never derive it from a secret qURL
|
|
771
|
+
link. Private `r_` storage IDs are not public API identifiers.
|
|
591
772
|
- SDK API requests use manual redirect handling. Redirect-capable HTTP statuses
|
|
592
773
|
(300, 301, 302, 303, 305, 307, 308), filtered `opaqueredirect` responses in
|
|
593
774
|
browsers and Node native fetch, and responses a custom fetch reports as
|
|
@@ -651,3 +832,139 @@ When upgrading, check the release notes for migration guidance — recent breaki
|
|
|
651
832
|
## License
|
|
652
833
|
|
|
653
834
|
MIT
|
|
835
|
+
|
|
836
|
+
Resource verification requires Web Crypto (`crypto.subtle`). In browsers, use
|
|
837
|
+
an HTTPS page or localhost. Raw REST response types keep `crid` optional;
|
|
838
|
+
resource handles and portals require a validated CRID.
|
|
839
|
+
|
|
840
|
+
## Node producer runtime (SDK 2.x, NHP 1.1)
|
|
841
|
+
|
|
842
|
+
The SDK implements the producer lifecycle in TypeScript. A small optional native
|
|
843
|
+
package, `@layervai/qurl-state-fs`, supplies descriptor-relative filesystem
|
|
844
|
+
operations on Linux and macOS. There is no bundled Go runtime. The portable
|
|
845
|
+
`@layervai/qurl` entry point does not load the native module.
|
|
846
|
+
|
|
847
|
+
```ts
|
|
848
|
+
import { FileAgentState, connectAgentRuntime } from '@layervai/qurl/node';
|
|
849
|
+
|
|
850
|
+
const store = new FileAgentState('/private/agent/state.json');
|
|
851
|
+
const runtime = await connectAgentRuntime(store, {
|
|
852
|
+
hub: { host: 'hub.nhp.layerv.ai', port: 443, server_public_key_b64: hubPublicKey },
|
|
853
|
+
headless: true,
|
|
854
|
+
enrollmentCredential: bootstrapCredential,
|
|
855
|
+
});
|
|
856
|
+
try {
|
|
857
|
+
// runtime.client uses the durable device credential, with bounded reloads.
|
|
858
|
+
const grant = await runtime.knock(knockResourceID, {
|
|
859
|
+
protectedResourceID: crid, runID: '0123456789abcdef', runAttempt: 1n,
|
|
860
|
+
});
|
|
861
|
+
// Keep the original receipt. It retains the issuing cell across relocation.
|
|
862
|
+
await runtime.retire(grant.receipt);
|
|
863
|
+
} finally {
|
|
864
|
+
await runtime.close();
|
|
865
|
+
store.close();
|
|
866
|
+
}
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
A registered state opens without enrollment or network work while its assignment
|
|
870
|
+
lease is current. An explicit Hub option or the `hub` field in `QURL_DEPLOYMENT`
|
|
871
|
+
provides refresh trust. `runtime.refresh()` handles assignment renewal and
|
|
872
|
+
relocation. `knock()` attempts renewal near lease expiry and refuses expired
|
|
873
|
+
assignments. Account enrollment uses an explicit `otpProvider` instead of
|
|
874
|
+
`headless`. Resume an interrupted enrollment with the same credential and
|
|
875
|
+
metadata. The SDK persists activation and completion replay authority before
|
|
876
|
+
sending the corresponding mutation.
|
|
877
|
+
|
|
878
|
+
`recoverAgentRuntime(store, recoveryCredential, { hub })` starts explicit device
|
|
879
|
+
credential recovery. It persists the issue nonce, replacement candidate, and
|
|
880
|
+
recovery horizon. It saves the recovered credential before mandatory assignment
|
|
881
|
+
refresh. A failed refresh resumes without another recovery grant. No startup
|
|
882
|
+
path silently resets an existing identity.
|
|
883
|
+
|
|
884
|
+
Legacy pre-v6 pending-completion state remains readable, but enrollment returns
|
|
885
|
+
`RECOVERY_MIGRATION_REQUIRED` without UDP or state changes. Those records lack
|
|
886
|
+
an authenticated recovery deadline; explicit recovery or reprovisioning is required.
|
|
887
|
+
Filesystem state creation honors the process umask and creates missing ancestors.
|
|
888
|
+
Keep owner read/write/search permissions enabled (for example, umask `077`);
|
|
889
|
+
unsafe existing permissions or an incompatible umask fail closed.
|
|
890
|
+
|
|
891
|
+
`FileAgentState` uses a private directory, mode 0600 files, a process-safe setup
|
|
892
|
+
lock, exclusive temporary files, file and directory fsync, and atomic replacement.
|
|
893
|
+
It rejects symlinks, hard links, unsafe permissions, and directory or lock
|
|
894
|
+
replacement. Await `runtime.close()` before closing the store. Inside `withLock`,
|
|
895
|
+
use the supplied locked handle; using the outer store waits until the lock timeout.
|
|
896
|
+
Crash-abandoned `.qurl-*` temporary files remain private but need offline cleanup;
|
|
897
|
+
automatic sweeps could race other state files in the same directory. Filesystem state
|
|
898
|
+
uses synchronous native I/O, including fsync, on the event loop. Use local storage;
|
|
899
|
+
slow or network filesystems can block the process. Continuity checks also run before
|
|
900
|
+
lifecycle datagrams and cannot be cached safely across external directory changes.
|
|
901
|
+
An existing state directory must be owned by the process user with mode 0700.
|
|
902
|
+
On macOS, `/var`, `/tmp`, and `/etc` resolve through their standard `/private` paths.
|
|
903
|
+
A save error after rename can mean the new state is present but its durability is
|
|
904
|
+
unconfirmed; do not assume the previous state remains. Filesystem state
|
|
905
|
+
currently requires Linux or macOS; unsupported platforms fail closed. Install
|
|
906
|
+
scripts may be disabled when a matching prebuilt native binary is available.
|
|
907
|
+
Linux x64/arm64 prebuilds distinguish glibc and musl; CI tests Ubuntu 24.04 and
|
|
908
|
+
Alpine 3.22. macOS prebuilds cover x64 and arm64.
|
|
909
|
+
|
|
910
|
+
For sealed state, use `openSealedFileAgentState(path, providerID, keyWrapper,
|
|
911
|
+
expectedAgentID)`. Each save uses a fresh AES-256-GCM key and verifies wrapping and
|
|
912
|
+
unwrapping before commit. The envelope is compatible with the pinned Go SDK.
|
|
913
|
+
Sealing authenticates the agent, provider, purpose, version, and wrapped-key
|
|
914
|
+
metadata. It does not detect rollback to an older valid envelope. JavaScript
|
|
915
|
+
strings cannot be reliably erased; avoid logging state or credentials.
|
|
916
|
+
|
|
917
|
+
AWS adapters are separate exports from `@layervai/qurl-aws` (ESM and CommonJS).
|
|
918
|
+
Install the AWS client for the subpath you use: `/ssm`, `/secrets-manager`, or
|
|
919
|
+
`/kms`. These clients are optional peer dependencies. Importing the root barrel
|
|
920
|
+
requires all three clients.
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
- `createSSMAgentStateStore(ssmClient, parameterName, { kmsKeyID, tier })` stores
|
|
924
|
+
SecureString parameters and enforces tier size limits.
|
|
925
|
+
- `createSecretsManagerAgentStateStore(secretsClient, secretID, kmsKeyID)` handles
|
|
926
|
+
missing-secret creation races with one write token.
|
|
927
|
+
- `createKMSAgentStateKeyWrapper(kmsClient, keyID)` wraps only the data key and
|
|
928
|
+
authenticates all four state-binding fields in the KMS encryption context.
|
|
929
|
+
|
|
930
|
+
SSM and Secrets Manager stores serialize lifecycle calls within one store handle.
|
|
931
|
+
Loads outside a lifecycle lock can read the previous committed state.
|
|
932
|
+
They require one process to own each state object: these services do not provide
|
|
933
|
+
an agent setup transaction lock. KMS is a key wrapper, not a state store.
|
|
934
|
+
|
|
935
|
+
## Explicit relay and discovery
|
|
936
|
+
|
|
937
|
+
Native UDP remains the default transport. An unknown native cell fails closed, including
|
|
938
|
+
when a relay allowlist exists. Select `transport: 'relay'` explicitly to use HTTPS
|
|
939
|
+
relay. Explicit relay trust uses signed qv2 claims and the relay allowlist, not
|
|
940
|
+
the native cell catalog. Relay URLs must match that deployment trust.
|
|
941
|
+
Redirects and oversized responses are refused. A hostname-only allowlist entry
|
|
942
|
+
authorizes all ports on that host; use `host:port` to limit the port.
|
|
943
|
+
|
|
944
|
+
`createStaticProvider(deployment)` snapshots fixed trust. For discovery, use
|
|
945
|
+
`createDiscoveryProvider({ fetcher, pinSHA256, manifestKeys, requireSignature,
|
|
946
|
+
minVersion, expectedProfile })` and `createHTTPManifestFetcher(httpsURL)`. Supply
|
|
947
|
+
a manifest pin or signing keys. Discovery is relay-only and requires
|
|
948
|
+
`transport: 'relay'`; native UDP requires a static cell catalog. Discovery verifies the domain-separated low-S
|
|
949
|
+
signature when configured, validity times, profile, and monotonic version floor.
|
|
950
|
+
It never returns stale trust after a failed refresh. Persist `minVersion` in
|
|
951
|
+
configuration when downgrade protection must survive process restarts.
|
|
952
|
+
Like Go, discovery fetches on each resolution, including renewal retries; it has
|
|
953
|
+
no built-in cache. Use static trust for high-volume opens, or supply a provider
|
|
954
|
+
with a deployment-owned refresh policy that refuses expired or failed-refresh trust.
|
|
955
|
+
|
|
956
|
+
```ts
|
|
957
|
+
const opener = createPortalOpener({ qurl: signedLink, provider, transport: 'relay' });
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
The SHA-pinned reviewed baseline and behavior gates are in `parity-manifest.json`.
|
|
961
|
+
CI reports the candidate SHA and tests that candidate against pinned Go behavior.
|
|
962
|
+
It does not require release or dependency PRs to match the baseline tree.
|
|
963
|
+
Run `npm run build`, `npm test`, `npm run smoke:dist`, and `npm run parity:go`.
|
|
964
|
+
Set `QURL_GO_REFERENCE` to a clean checkout at the manifest's exact Go revision.
|
|
965
|
+
The direct gate compares producer wire bytes and sealed-state reads and writes
|
|
966
|
+
across both languages. It also runs the TypeScript lifecycle against a local Go UDP
|
|
967
|
+
peer: registration, restart, relocation, exact retirement, and credential recovery.
|
|
968
|
+
This checks protocol interoperability; the peer is not a sandbox authority.
|
|
969
|
+
Shared conformance vectors cover assignment, registration,
|
|
970
|
+
OTP, and completion packet construction and reply decryption.
|
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, CreateDelegatedQurlBatchInput, CreateDelegatedQurlBatchOptions, CreateInput, CreateOutput, CreatePortalOptions, CreateQurlForResourceInput, CreateResourceInput, CreateWebhookInput, Customer, CheckoutSession, Domain, DomainListOutput, DomainVerifyResult, DelegatedQurl, DelegatedQurlBatchAccepted, GetDelegatedQurlBatchOptions, GetDelegatedQurlBatchOutput, 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,34 +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;
|
|
95
98
|
/** Find or create the active qURL Connector resource for an immutable slug. */
|
|
96
99
|
ensureConnectorResource(slug: string, requestOptions?: RequestOptions): Promise<EnsureConnectorResourceResult>;
|
|
97
|
-
/** Fetch a qURL Connector resource by its
|
|
98
|
-
getConnectorResource(
|
|
100
|
+
/** Fetch a qURL Connector resource by its CRID. */
|
|
101
|
+
getConnectorResource(crid: string): Promise<ConnectorResource>;
|
|
99
102
|
/** Fetch the single active qURL Connector resource for an immutable slug. */
|
|
100
103
|
getConnectorResourceBySlug(slug: string): Promise<ConnectorResource>;
|
|
101
104
|
/**
|
|
102
|
-
* Revoke a qURL Connector resource by
|
|
105
|
+
* Revoke a qURL Connector resource by CRID.
|
|
103
106
|
*
|
|
104
107
|
* The API does not apply Idempotency-Key replay to DELETE. After an
|
|
105
108
|
* outcome-unknown failure, reconcile by ID before issuing a deliberate retry.
|
|
106
109
|
*/
|
|
107
|
-
deleteConnectorResource(
|
|
110
|
+
deleteConnectorResource(crid: string): Promise<void>;
|
|
108
111
|
/**
|
|
109
112
|
* Mint a short-lived qURL link (a portal) for a protected resource.
|
|
110
113
|
*
|
|
@@ -112,7 +115,7 @@ export declare class QURLClient {
|
|
|
112
115
|
* reach one private resource. Recipients open `portal.link` directly and
|
|
113
116
|
* need no LayerV credentials. Prefer short lifetimes such as
|
|
114
117
|
* `{ validFor: "5m" }`. Accepts a {@link ProtectedResource} handle or a
|
|
115
|
-
*
|
|
118
|
+
* CRID string. REST-shaped equivalents: {@link mintLink} /
|
|
116
119
|
* {@link createQurlForResource}.
|
|
117
120
|
*
|
|
118
121
|
* Duration options take a string (`"5m"`, `"24h"`; server-validated) or a
|
|
@@ -128,7 +131,7 @@ export declare class QURLClient {
|
|
|
128
131
|
*
|
|
129
132
|
* Convenience for one-off scripts (qurl-go: `Client.CreatePortalForURL`).
|
|
130
133
|
* The returned resource handle is reusable for minting more portals but
|
|
131
|
-
* carries
|
|
134
|
+
* carries the CRID, verification key, and the caller-supplied target URL; use
|
|
132
135
|
* {@link protectUrl} when you need the full server-populated resource
|
|
133
136
|
* metadata. Portal options match {@link createPortal}; `targetUrl` gets
|
|
134
137
|
* the same malformed-URL and embedded-credentials rejection as
|
|
@@ -223,11 +226,30 @@ export declare class QURLClient {
|
|
|
223
226
|
* ```
|
|
224
227
|
*/
|
|
225
228
|
batchCreate(input: BatchCreateInput, options?: RequestOptions): Promise<BatchCreateOutput>;
|
|
229
|
+
/**
|
|
230
|
+
* Start one durable delegated qURL batch. The SDK sends this mutation once.
|
|
231
|
+
* If the result may have committed, retry explicitly with the same key and
|
|
232
|
+
* semantic body.
|
|
233
|
+
*/
|
|
234
|
+
createDelegatedQurlBatch(input: CreateDelegatedQurlBatchInput, options: CreateDelegatedQurlBatchOptions): Promise<DelegatedQurlBatchAccepted>;
|
|
235
|
+
/**
|
|
236
|
+
* Read one delegated qURL batch state. One call performs one logical read;
|
|
237
|
+
* callers retain control of the poll count and total deadline.
|
|
238
|
+
*/
|
|
239
|
+
getDelegatedQurlBatch(batchId: string, options?: GetDelegatedQurlBatchOptions): Promise<GetDelegatedQurlBatchOutput>;
|
|
240
|
+
/** Read owner-visible metadata for one delegated qURL. */
|
|
241
|
+
getDelegatedQurl(qurlId: string): Promise<DelegatedQurl>;
|
|
242
|
+
/**
|
|
243
|
+
* Revoke one delegated qURL. DELETE is never retried automatically. A 503
|
|
244
|
+
* `mutation_outcome_unknown` is safe to retry with the same qURL ID.
|
|
245
|
+
*/
|
|
246
|
+
deleteDelegatedQurl(qurlId: string): Promise<void>;
|
|
226
247
|
/**
|
|
227
248
|
* Get a qURL resource and its access tokens.
|
|
228
249
|
*
|
|
229
|
-
* Accepts
|
|
230
|
-
* prefix); the API resolves
|
|
250
|
+
* Accepts a CRID or qURL
|
|
251
|
+
* display ID (`q_` prefix); the API resolves display IDs to the parent
|
|
252
|
+
* resource automatically.
|
|
231
253
|
*/
|
|
232
254
|
get(id: string): Promise<QURL>;
|
|
233
255
|
/**
|
|
@@ -258,17 +280,17 @@ export declare class QURLClient {
|
|
|
258
280
|
/**
|
|
259
281
|
* Delete (revoke) a qURL resource and all its access tokens.
|
|
260
282
|
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
283
|
+
* Requires a CRID returned by the API. To revoke one qURL, use
|
|
284
|
+
* {@link revokeResourceQurl} instead.
|
|
285
|
+
*
|
|
286
|
+
* @throws {ValidationError} If `id` is not a canonical CRID.
|
|
265
287
|
*/
|
|
266
288
|
delete(id: string): Promise<void>;
|
|
267
289
|
/**
|
|
268
290
|
* Extend a qURL's expiration.
|
|
269
291
|
*
|
|
270
|
-
* Accepts
|
|
271
|
-
* prefix).
|
|
292
|
+
* Accepts a CRID or qURL
|
|
293
|
+
* display ID (`q_` prefix). Delegates to {@link update} with only the
|
|
272
294
|
* expiration fields. `ExtendInput` shares its `extend_by` / `expires_at`
|
|
273
295
|
* fields with `UpdateInput` but is *narrower in two ways*: (1) exactly
|
|
274
296
|
* one of the two must be present (XOR via `?: never`), where `UpdateInput`
|
|
@@ -283,8 +305,9 @@ export declare class QURLClient {
|
|
|
283
305
|
/**
|
|
284
306
|
* Update a qURL — extend expiration, change description, rename tags.
|
|
285
307
|
*
|
|
286
|
-
* Accepts
|
|
287
|
-
* prefix); the API resolves
|
|
308
|
+
* Accepts a CRID or qURL
|
|
309
|
+
* display ID (`q_` prefix); the API resolves display IDs to the parent
|
|
310
|
+
* resource automatically.
|
|
288
311
|
*/
|
|
289
312
|
update(id: string, input: UpdateInput, options?: RequestOptions): Promise<QURL>;
|
|
290
313
|
/**
|
|
@@ -292,8 +315,9 @@ export declare class QURLClient {
|
|
|
292
315
|
*
|
|
293
316
|
* Portal-flow equivalent: {@link createPortal}.
|
|
294
317
|
*
|
|
295
|
-
* Accepts
|
|
296
|
-
* prefix); the API resolves
|
|
318
|
+
* Accepts a CRID or qURL
|
|
319
|
+
* display ID (`q_` prefix); the API resolves display IDs to the parent
|
|
320
|
+
* resource automatically.
|
|
297
321
|
*
|
|
298
322
|
* Passing `{}` or an object with all fields `null`/`undefined` is
|
|
299
323
|
* equivalent to omitting the second argument: no body is sent, and the
|
|
@@ -342,6 +366,13 @@ export declare class QURLClient {
|
|
|
342
366
|
* Portal-flow equivalent: {@link createPortal} (same endpoint).
|
|
343
367
|
*/
|
|
344
368
|
createQurlForResource(id: string, input?: CreateQurlForResourceInput, options?: RequestOptions): Promise<CreateOutput>;
|
|
369
|
+
/**
|
|
370
|
+
* Mint a fresh, short-lived share link for an existing CRID.
|
|
371
|
+
* The returned link is secret and is not retrievable after this response.
|
|
372
|
+
* If you hold the trusted resource DER SPKI, call `ShareLink.verifyCrid()`
|
|
373
|
+
* before delivery to verify that the response CRID derives from that key.
|
|
374
|
+
*/
|
|
375
|
+
shareResource(id: string, options?: ShareResourceOptions, requestOptions?: RequestOptions): Promise<ShareLink>;
|
|
345
376
|
/** Revoke a specific qURL token on a resource. */
|
|
346
377
|
revokeResourceQurl(id: string, qurlId: string): Promise<void>;
|
|
347
378
|
/** Update a specific qURL token on a resource. */
|
|
@@ -505,19 +536,19 @@ export declare class QURLClient {
|
|
|
505
536
|
/**
|
|
506
537
|
* A LayerV-protected resource, bound to the client that produced it.
|
|
507
538
|
*
|
|
508
|
-
* Obtained from {@link QURLClient.protectUrl} or {@link QURLClient.
|
|
539
|
+
* Obtained from {@link QURLClient.protectUrl} or {@link QURLClient.resourceByCrid} —
|
|
509
540
|
* not constructed directly. Mint short-lived access links for the resource
|
|
510
541
|
* with {@link createPortal} (qurl-go: `qurl.Resource`).
|
|
511
542
|
*/
|
|
512
543
|
export declare class ProtectedResource {
|
|
513
544
|
#private;
|
|
514
|
-
/** The
|
|
515
|
-
readonly
|
|
545
|
+
/** The public resource CRID. */
|
|
546
|
+
readonly crid: string;
|
|
516
547
|
/** The private URL protected by this resource, when known. */
|
|
517
548
|
readonly targetUrl?: string;
|
|
518
549
|
/**
|
|
519
550
|
* Full server-populated {@link Resource} metadata when the handle came
|
|
520
|
-
* from an API response; `undefined` for {@link QURLClient.
|
|
551
|
+
* from an API response; `undefined` for {@link QURLClient.resourceByCrid}
|
|
521
552
|
* handles and for the one-call {@link QURLClient.createPortalForUrl} path.
|
|
522
553
|
*/
|
|
523
554
|
readonly details?: Resource;
|
|
@@ -550,9 +581,9 @@ declare const CONNECTOR_RESOURCE_CONSTRUCTOR_TOKEN: unique symbol;
|
|
|
550
581
|
*/
|
|
551
582
|
export declare class ConnectorResource {
|
|
552
583
|
#private;
|
|
553
|
-
readonly
|
|
554
|
-
/**
|
|
555
|
-
readonly crid
|
|
584
|
+
readonly resourcePublicKey: string;
|
|
585
|
+
/** Required public identifier, verified against the returned resource key. */
|
|
586
|
+
readonly crid: string;
|
|
556
587
|
readonly connectorRoutingId: string;
|
|
557
588
|
readonly knockResourceId: string;
|
|
558
589
|
readonly slug: string;
|