@layervai/qurl 0.7.0 → 3.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 +242 -2
- package/dist/cjs/client.d.ts +41 -9
- package/dist/cjs/client.js +734 -41
- package/dist/cjs/crid.d.ts +2 -1
- package/dist/cjs/crid.js +11 -2
- package/dist/cjs/errors.d.ts +20 -3
- package/dist/cjs/errors.js +31 -4
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +3 -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/types.d.ts +161 -8
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +41 -9
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +735 -42
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/crid.d.ts +2 -1
- package/dist/esm/crid.d.ts.map +1 -1
- package/dist/esm/crid.js +10 -2
- package/dist/esm/crid.js.map +1 -1
- package/dist/esm/errors.d.ts +20 -3
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +29 -3
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- 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/types.d.ts +161 -8
- 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 +16 -6
package/README.md
CHANGED
|
@@ -182,7 +182,16 @@ console.log(share.qurlId); // Safe handle for revoking only this link later
|
|
|
182
182
|
`shareResource` returns the current share-safe `#qv2t1...` link. Recipients
|
|
183
183
|
open that URL in the qURL browser flow. For native TypeScript opening, pass
|
|
184
184
|
`share.link` to `createPortalOpener` from `@layervai/qurl/node` with trusted
|
|
185
|
-
`QURL_DEPLOYMENT` configuration
|
|
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.
|
|
186
195
|
`verifyCrid` does not open the link or make a network request.
|
|
187
196
|
|
|
188
197
|
`verifyCrid` verifies that the response CRID derives from the trusted resource
|
|
@@ -259,6 +268,74 @@ await client.updateApiKey(keyId, { scopes });
|
|
|
259
268
|
This credential surface requires the kind-first qurl-service contract at or
|
|
260
269
|
after commit `047cf31e1cdf545e3060e0f9294d738a19fb997b`.
|
|
261
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
|
+
|
|
262
339
|
## Opening Portals
|
|
263
340
|
|
|
264
341
|
Most recipients open qURL links directly and do not use this SDK at all. If
|
|
@@ -298,6 +375,7 @@ const { createPortalOpener } = require('@layervai/qurl/node');
|
|
|
298
375
|
async function uploadPrivateObject(uploadBody) {
|
|
299
376
|
const opener = createPortalOpener({
|
|
300
377
|
qurl: process.env.PRIVATE_UPLOAD_QURL,
|
|
378
|
+
expectedCRID: process.env.PRIVATE_UPLOAD_CRID ?? '', // Independently held; missing fails closed.
|
|
301
379
|
});
|
|
302
380
|
try {
|
|
303
381
|
await opener.start();
|
|
@@ -512,7 +590,9 @@ console.log(`Access granted to ${access.target_url} for ${access.access_grant?.e
|
|
|
512
590
|
| `createApiKey(input)` / `listApiKeys(input?)` / `listAllApiKeys(input?)` / `updateApiKey(id, input)` / `revokeApiKey(id)` | API key management |
|
|
513
591
|
| `createAccessCode(input)` / `listAccessCodes()` / `redeemAccessCode(input)` / `revokeAccessCode(id)` | Access code management |
|
|
514
592
|
|
|
515
|
-
`listResourceSessions(id)`
|
|
593
|
+
`listResourceSessions(id)` opts in to bounded pages with `paged=true` and collects every service page, including empty intermediate pages, and returns `has_more: false`. Its `request_id` and `page_size` describe the last page. `terminateAllResourceSessions(id)` follows every page and sums closed sessions. Termination is not atomic: an error can occur after some sessions close. List sessions to reconcile before retrying; a retry can also close newly admitted sessions and counts only its own changes. A successful call returns the last page’s `request_id`.
|
|
594
|
+
|
|
595
|
+
`listAccessCodes()` remains unpaginated. It returns `has_more: false` and logs unexpected cursor metadata.
|
|
516
596
|
|
|
517
597
|
`listAll*()` methods validate ids and query params when called, before the async iterator is consumed. Wrap the `listAll*()` call itself in `try/catch` when passing dynamic input.
|
|
518
598
|
|
|
@@ -585,6 +665,7 @@ try {
|
|
|
585
665
|
| `ServerError` | 5xx | Server-side failure |
|
|
586
666
|
| `NetworkError` | — | Connection failure |
|
|
587
667
|
| `TimeoutError` | — | Request exceeded timeout |
|
|
668
|
+
| `DelegatedBatchOutcomeUnknownError` | — | Dispatched batch create needs reconciliation |
|
|
588
669
|
|
|
589
670
|
Client-detected failures use `status: 0` with a discriminating `code`:
|
|
590
671
|
`"client_validation"` for bad input caught before a request, and — on the
|
|
@@ -757,3 +838,162 @@ MIT
|
|
|
757
838
|
Resource verification requires Web Crypto (`crypto.subtle`). In browsers, use
|
|
758
839
|
an HTTPS page or localhost. Raw REST response types keep `crid` optional;
|
|
759
840
|
resource handles and portals require a validated CRID.
|
|
841
|
+
|
|
842
|
+
## Node producer runtime (SDK 2.x, NHP 1.1)
|
|
843
|
+
|
|
844
|
+
The SDK implements the producer lifecycle in TypeScript. A small optional native
|
|
845
|
+
package, `@layervai/qurl-state-fs`, supplies descriptor-relative filesystem
|
|
846
|
+
operations on Linux and macOS. There is no bundled Go runtime. The portable
|
|
847
|
+
`@layervai/qurl` entry point does not load the native module.
|
|
848
|
+
|
|
849
|
+
```ts
|
|
850
|
+
import { FileAgentState, connectAgentRuntime } from '@layervai/qurl/node';
|
|
851
|
+
|
|
852
|
+
const store = new FileAgentState('/private/agent/state.json');
|
|
853
|
+
const runtime = await connectAgentRuntime(store, {
|
|
854
|
+
hub: { host: 'hub.nhp.layerv.ai', port: 443, server_public_key_b64: hubPublicKey },
|
|
855
|
+
headless: true,
|
|
856
|
+
enrollmentCredential: bootstrapCredential,
|
|
857
|
+
});
|
|
858
|
+
try {
|
|
859
|
+
// runtime.client uses the durable device credential, with bounded reloads.
|
|
860
|
+
const grant = await runtime.knock(knockResourceID, {
|
|
861
|
+
protectedResourceID: crid, runID: '0123456789abcdef', runAttempt: 1n,
|
|
862
|
+
});
|
|
863
|
+
// Keep the original receipt. It retains the issuing cell across relocation.
|
|
864
|
+
await runtime.retire(grant.receipt);
|
|
865
|
+
} finally {
|
|
866
|
+
await runtime.close();
|
|
867
|
+
store.close();
|
|
868
|
+
}
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
A registered state opens without enrollment or network work while its assignment
|
|
872
|
+
lease is current. An explicit Hub option or the `hub` field in `QURL_DEPLOYMENT`
|
|
873
|
+
provides refresh trust. `runtime.refresh()` handles assignment renewal and
|
|
874
|
+
relocation. `knock()` attempts renewal near lease expiry and refuses expired
|
|
875
|
+
assignments. Account enrollment uses an explicit `otpProvider` instead of
|
|
876
|
+
`headless`. Resume an interrupted enrollment with the same credential and
|
|
877
|
+
metadata. The SDK persists activation and completion replay authority before
|
|
878
|
+
sending the corresponding mutation.
|
|
879
|
+
|
|
880
|
+
`recoverAgentRuntime(store, recoveryCredential, { hub })` starts explicit device
|
|
881
|
+
credential recovery. It persists the issue nonce, replacement candidate, and
|
|
882
|
+
recovery horizon. It saves the recovered credential before mandatory assignment
|
|
883
|
+
refresh. A failed refresh resumes without another recovery grant. No startup
|
|
884
|
+
path silently resets an existing identity.
|
|
885
|
+
|
|
886
|
+
Legacy pre-v6 pending-completion state remains readable, but enrollment returns
|
|
887
|
+
`RECOVERY_MIGRATION_REQUIRED` without UDP or state changes. Those records lack
|
|
888
|
+
an authenticated recovery deadline; explicit recovery or reprovisioning is required.
|
|
889
|
+
Filesystem state creation honors the process umask and creates missing ancestors.
|
|
890
|
+
Keep owner read/write/search permissions enabled (for example, umask `077`);
|
|
891
|
+
unsafe existing permissions or an incompatible umask fail closed.
|
|
892
|
+
|
|
893
|
+
`FileAgentState` uses a private directory, mode 0600 files, a process-safe setup
|
|
894
|
+
lock, exclusive temporary files, file and directory fsync, and atomic replacement.
|
|
895
|
+
It rejects symlinks, hard links, unsafe permissions, and directory or lock
|
|
896
|
+
replacement. Await `runtime.close()` before closing the store. Inside `withLock`,
|
|
897
|
+
use the supplied locked handle; using the outer store waits until the lock timeout.
|
|
898
|
+
Crash-abandoned `.qurl-*` temporary files remain private but need offline cleanup;
|
|
899
|
+
automatic sweeps could race other state files in the same directory. Filesystem state
|
|
900
|
+
uses synchronous native I/O, including fsync, on the event loop. Use local storage;
|
|
901
|
+
slow or network filesystems can block the process. Continuity checks also run before
|
|
902
|
+
lifecycle datagrams and cannot be cached safely across external directory changes.
|
|
903
|
+
An existing state directory must be owned by the process user with mode 0700.
|
|
904
|
+
On macOS, `/var`, `/tmp`, and `/etc` resolve through their standard `/private` paths.
|
|
905
|
+
A save error after rename can mean the new state is present but its durability is
|
|
906
|
+
unconfirmed; do not assume the previous state remains. Filesystem state
|
|
907
|
+
currently requires Linux or macOS; unsupported platforms fail closed. Install
|
|
908
|
+
scripts may be disabled when a matching prebuilt native binary is available.
|
|
909
|
+
Linux x64/arm64 prebuilds distinguish glibc and musl; CI tests Ubuntu 24.04 and
|
|
910
|
+
Alpine 3.22. macOS prebuilds cover x64 and arm64.
|
|
911
|
+
|
|
912
|
+
For sealed state, use `openSealedFileAgentState(path, providerID, keyWrapper,
|
|
913
|
+
expectedAgentID)`. Each save uses a fresh AES-256-GCM key and verifies wrapping and
|
|
914
|
+
unwrapping before commit. The envelope is compatible with the pinned Go SDK.
|
|
915
|
+
Sealing authenticates the agent, provider, purpose, version, and wrapped-key
|
|
916
|
+
metadata. It does not detect rollback to an older valid envelope. JavaScript
|
|
917
|
+
strings cannot be reliably erased; avoid logging state or credentials.
|
|
918
|
+
|
|
919
|
+
AWS adapters are separate exports from `@layervai/qurl-aws` (ESM and CommonJS).
|
|
920
|
+
Install the AWS client for the subpath you use: `/ssm`, `/secrets-manager`, or
|
|
921
|
+
`/kms`. These clients are optional peer dependencies. Importing the root barrel
|
|
922
|
+
requires all three clients.
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
- `createSSMAgentStateStore(ssmClient, parameterName, { kmsKeyID, tier })` stores
|
|
926
|
+
SecureString parameters and enforces tier size limits.
|
|
927
|
+
- `createSecretsManagerAgentStateStore(secretsClient, secretID, kmsKeyID)` handles
|
|
928
|
+
missing-secret creation races with one write token.
|
|
929
|
+
- `createKMSAgentStateKeyWrapper(kmsClient, keyID)` wraps only the data key and
|
|
930
|
+
authenticates all four state-binding fields in the KMS encryption context.
|
|
931
|
+
|
|
932
|
+
SSM and Secrets Manager stores serialize lifecycle calls within one store handle.
|
|
933
|
+
Loads outside a lifecycle lock can read the previous committed state.
|
|
934
|
+
They require one process to own each state object: these services do not provide
|
|
935
|
+
an agent setup transaction lock. KMS is a key wrapper, not a state store.
|
|
936
|
+
|
|
937
|
+
## Explicit relay and discovery
|
|
938
|
+
|
|
939
|
+
Native UDP remains the default transport. An unknown native cell fails closed, including
|
|
940
|
+
when a relay allowlist exists. Select `transport: 'relay'` explicitly to use HTTPS
|
|
941
|
+
relay. Explicit relay trust uses signed qv2 claims and the relay allowlist, not
|
|
942
|
+
the native cell catalog. Relay URLs must match that deployment trust.
|
|
943
|
+
Redirects and oversized responses are refused. A hostname-only allowlist entry
|
|
944
|
+
authorizes all ports on that host; use `host:port` to limit the port.
|
|
945
|
+
|
|
946
|
+
`createStaticProvider(deployment)` snapshots fixed trust. For discovery, use
|
|
947
|
+
`createDiscoveryProvider({ fetcher, pinSHA256, manifestKeys, requireSignature,
|
|
948
|
+
minVersion, expectedProfile })` and `createHTTPManifestFetcher(httpsURL)`. Supply
|
|
949
|
+
a manifest pin or signing keys. Discovery is relay-only and requires
|
|
950
|
+
`transport: 'relay'`; native UDP requires a static cell catalog. Discovery verifies the domain-separated low-S
|
|
951
|
+
signature when configured, validity times, profile, and monotonic version floor.
|
|
952
|
+
It never returns stale trust after a failed refresh. Persist `minVersion` in
|
|
953
|
+
configuration when downgrade protection must survive process restarts.
|
|
954
|
+
Like Go, discovery fetches on each resolution, including renewal retries; it has
|
|
955
|
+
no built-in cache. Use static trust for high-volume opens, or supply a provider
|
|
956
|
+
with a deployment-owned refresh policy that refuses expired or failed-refresh trust.
|
|
957
|
+
|
|
958
|
+
```ts
|
|
959
|
+
const opener = createPortalOpener({ qurl: signedLink, provider, transport: 'relay' });
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
The SHA-pinned reviewed baseline and behavior gates are in `parity-manifest.json`.
|
|
963
|
+
CI reports the candidate SHA and tests that candidate against pinned Go behavior.
|
|
964
|
+
It does not require release or dependency PRs to match the baseline tree.
|
|
965
|
+
Run `npm run build`, `npm test`, `npm run smoke:dist`, and `npm run parity:go`.
|
|
966
|
+
Set `QURL_GO_REFERENCE` to a clean checkout at the manifest's exact Go revision.
|
|
967
|
+
The direct gate compares producer wire bytes and sealed-state reads and writes
|
|
968
|
+
across both languages. It also runs the TypeScript lifecycle against a local Go UDP
|
|
969
|
+
peer: registration, restart, relocation, exact retirement, and credential recovery.
|
|
970
|
+
This checks protocol interoperability; the peer is not a sandbox authority.
|
|
971
|
+
Shared conformance vectors cover assignment, registration,
|
|
972
|
+
OTP, and completion packet construction and reply decryption.
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
### Private consumer policies
|
|
976
|
+
|
|
977
|
+
`createResource` accepts `private: true` and `consumer_access` with mode
|
|
978
|
+
`keys`, `invite_only`, `email_capture`, `review` or `contract`, and version `1`.
|
|
979
|
+
`updateResource` changes the policy with `expected_consumer_access_version` and
|
|
980
|
+
the next version. Send policy changes separately from metadata updates.
|
|
981
|
+
|
|
982
|
+
For a URL, `protectUrl(url, { consumerAccess: { mode: "invite_only", version: 1 } })`
|
|
983
|
+
creates the private resource and checks the returned policy. For a local
|
|
984
|
+
Connector, pass the policy as the third argument to `ensureConnectorResource`.
|
|
985
|
+
A private policy cannot be combined with public access. A service that ignores
|
|
986
|
+
the requested policy is refused.
|
|
987
|
+
|
|
988
|
+
Publisher methods are `inviteConsumer`, `listConsumerInvitations`,
|
|
989
|
+
`resendConsumerInvitation`, `revokeConsumerInvitation`, `listConsumerGrants`,
|
|
990
|
+
`approveConsumerGrant`, `denyConsumerGrant` and `revokeConsumerGrant`. Decisions
|
|
991
|
+
use the version read from the invitation or grant. Lists return the usual
|
|
992
|
+
`nextCursor`. Contract entitlement approval requires a trusted publisher backend
|
|
993
|
+
receipt and expiry; the SDK does not verify payment. Contract replies omit email
|
|
994
|
+
and globally reusable credential data.
|
|
995
|
+
|
|
996
|
+
A removal receipt reports `session_cleanup: pending` until connection cleanup
|
|
997
|
+
is confirmed. Retry the same removal and version to confirm `complete`. These
|
|
998
|
+
APIs require the matching service feature and remain off by default during
|
|
999
|
+
integration. Consumer Noise enrollment and browser access are in the NHP agent.
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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";
|
|
2
|
+
import type { ConsumerAccessPolicy, ConsumerInvitation, ConsumerGrant, ConsumerGrantApprovalInput, ConsumerListInput, ConsumerInvitationListOutput, ConsumerGrantListOutput, 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
3
|
/** Narrow a response scope before reusing it in a create/update request. */
|
|
4
4
|
export declare function isApiKeyRequestScope(scope: string): scope is ApiKeyRequestScope;
|
|
5
5
|
/**
|
|
@@ -96,7 +96,7 @@ export declare class QURLClient {
|
|
|
96
96
|
*/
|
|
97
97
|
resourceByCrid(id: string): ProtectedResource;
|
|
98
98
|
/** Find or create the active qURL Connector resource for an immutable slug. */
|
|
99
|
-
ensureConnectorResource(slug: string, requestOptions?: RequestOptions): Promise<EnsureConnectorResourceResult>;
|
|
99
|
+
ensureConnectorResource(slug: string, requestOptions?: RequestOptions, consumerAccess?: ConsumerAccessPolicy): Promise<EnsureConnectorResourceResult>;
|
|
100
100
|
/** Fetch a qURL Connector resource by its CRID. */
|
|
101
101
|
getConnectorResource(crid: string): Promise<ConnectorResource>;
|
|
102
102
|
/** Fetch the single active qURL Connector resource for an immutable slug. */
|
|
@@ -226,6 +226,24 @@ export declare class QURLClient {
|
|
|
226
226
|
* ```
|
|
227
227
|
*/
|
|
228
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>;
|
|
229
247
|
/**
|
|
230
248
|
* Get a qURL resource and its access tokens.
|
|
231
249
|
*
|
|
@@ -340,6 +358,22 @@ export declare class QURLClient {
|
|
|
340
358
|
getResource(id: string): Promise<ResourceDetail>;
|
|
341
359
|
/** Update resource metadata. */
|
|
342
360
|
updateResource(id: string, input: UpdateResourceInput, options?: RequestOptions): Promise<Resource>;
|
|
361
|
+
/** Send a publisher-only invitation. Mail delivery does not prove enrollment. */
|
|
362
|
+
inviteConsumer(id: string, email: string): Promise<ConsumerInvitation>;
|
|
363
|
+
listConsumerInvitations(id: string, input?: ConsumerListInput): Promise<ConsumerInvitationListOutput>;
|
|
364
|
+
listConsumerGrants(id: string, input?: ConsumerListInput): Promise<ConsumerGrantListOutput>;
|
|
365
|
+
resendConsumerInvitation(id: string, invitationId: string, expectedVersion: number): Promise<ConsumerInvitation>;
|
|
366
|
+
/** Remove the invitation and its grant. Inspect session_cleanup before reporting stream removal. */
|
|
367
|
+
revokeConsumerInvitation(id: string, invitationId: string, expectedVersion: number): Promise<ConsumerInvitation>;
|
|
368
|
+
/** Review approval, key opt-in with grantId new, or trusted publisher entitlement. */
|
|
369
|
+
approveConsumerGrant(id: string, grantId: string, input: ConsumerGrantApprovalInput): Promise<ConsumerGrant>;
|
|
370
|
+
denyConsumerGrant(id: string, grantId: string, expectedVersion: number): Promise<ConsumerGrant>;
|
|
371
|
+
revokeConsumerGrant(id: string, grantId: string, expectedVersion: number): Promise<ConsumerGrant>;
|
|
372
|
+
/**
|
|
373
|
+
* Revoke an API resource identifier retained by a client migrating to CRIDs.
|
|
374
|
+
* New callers should use deleteResource with the resource CRID.
|
|
375
|
+
*/
|
|
376
|
+
deleteResourceByIdentifier(id: string): Promise<void>;
|
|
343
377
|
/** Revoke a resource and all of its qURLs. */
|
|
344
378
|
deleteResource(id: string): Promise<void>;
|
|
345
379
|
/**
|
|
@@ -359,15 +393,13 @@ export declare class QURLClient {
|
|
|
359
393
|
revokeResourceQurl(id: string, qurlId: string): Promise<void>;
|
|
360
394
|
/** Update a specific qURL token on a resource. */
|
|
361
395
|
updateResourceQurl(id: string, qurlId: string, input: UpdateResourceQurlInput, options?: RequestOptions): Promise<QurlSummary>;
|
|
396
|
+
/** List every active session, following bounded service pages. */
|
|
397
|
+
listResourceSessions(id: string): Promise<SessionListOutput>;
|
|
362
398
|
/**
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
* If the service later advertises pagination metadata, the SDK returns
|
|
367
|
-
* this page and emits a debug log rather than pretending it fetched all pages.
|
|
399
|
+
* End every session page. This is not atomic: a failed page throws after earlier
|
|
400
|
+
* pages may have closed sessions. List sessions to reconcile, then retry the call.
|
|
401
|
+
* A retry can close newly admitted sessions and counts only its own transitions.
|
|
368
402
|
*/
|
|
369
|
-
listResourceSessions(id: string): Promise<SessionListOutput>;
|
|
370
|
-
/** Terminate all active sessions for a resource and return the server count. */
|
|
371
403
|
terminateAllResourceSessions(id: string): Promise<SessionTerminateOutput>;
|
|
372
404
|
/** Terminate a specific resource session. */
|
|
373
405
|
terminateResourceSession(id: string, sessionId: string): Promise<void>;
|