@layervai/qurl 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -15
- package/dist/cjs/client.d.ts +44 -31
- package/dist/cjs/client.js +412 -154
- package/dist/cjs/crid.d.ts +2 -0
- package/dist/cjs/crid.js +53 -0
- package/dist/cjs/errors.d.ts +10 -2
- package/dist/cjs/errors.js +11 -3
- package/dist/cjs/index.d.ts +5 -3
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/share.d.ts +56 -0
- package/dist/cjs/share.js +113 -0
- package/dist/cjs/types.d.ts +76 -13
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +44 -31
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +411 -154
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/crid.d.ts +3 -0
- package/dist/esm/crid.d.ts.map +1 -0
- package/dist/esm/crid.js +50 -0
- package/dist/esm/crid.js.map +1 -0
- package/dist/esm/errors.d.ts +10 -2
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +10 -2
- 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/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 +76 -13
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +2 -2
package/dist/cjs/crid.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseCrid = parseCrid;
|
|
4
|
+
exports.cridKeyMatches = cridKeyMatches;
|
|
5
|
+
// CRID v1 local gate and delivered-key binding, matching qurl-go/crid.
|
|
6
|
+
// The byte selects a version/environment/digest width, not a new hash scheme.
|
|
7
|
+
// All key matches use the v1 domain separator, as in qurl-go/crid.KeyMatches.
|
|
8
|
+
// Unknown nonzero versions remain forwardable; the service owns activation.
|
|
9
|
+
const ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
|
|
10
|
+
function parseCrid(value) {
|
|
11
|
+
if (typeof value !== "string" || ![47, 60].includes(value.length))
|
|
12
|
+
return undefined;
|
|
13
|
+
const bytes = new Uint8Array(Math.floor((value.length * 5) / 8));
|
|
14
|
+
let buffer = 0, bits = 0, offset = 0;
|
|
15
|
+
for (const character of value) {
|
|
16
|
+
const digit = ALPHABET.indexOf(character);
|
|
17
|
+
if (digit < 0)
|
|
18
|
+
return undefined;
|
|
19
|
+
buffer = (buffer << 5) | digit;
|
|
20
|
+
bits += 5;
|
|
21
|
+
if (bits >= 8) {
|
|
22
|
+
bits -= 8;
|
|
23
|
+
bytes[offset++] = buffer >>> bits;
|
|
24
|
+
buffer &= (1 << bits) - 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (buffer !== 0 || bytes[0] === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
const payload = bytes.subarray(0, -4);
|
|
30
|
+
let crc = 0xffffffff;
|
|
31
|
+
for (const byte of payload) {
|
|
32
|
+
crc ^= byte;
|
|
33
|
+
for (let bit = 0; bit < 8; bit++)
|
|
34
|
+
crc = (crc >>> 1) ^ (crc & 1 ? 0x82f63b78 : 0);
|
|
35
|
+
}
|
|
36
|
+
if (~crc >>> 0 !== new DataView(bytes.buffer).getUint32(bytes.length - 4))
|
|
37
|
+
return undefined;
|
|
38
|
+
return payload.subarray(1);
|
|
39
|
+
}
|
|
40
|
+
async function cridKeyMatches(value, der) {
|
|
41
|
+
const expected = parseCrid(value);
|
|
42
|
+
if (!expected)
|
|
43
|
+
return false;
|
|
44
|
+
const prefix = new TextEncoder().encode("NHP-QURL-CRID-V1\0");
|
|
45
|
+
const message = new Uint8Array(prefix.length + der.length);
|
|
46
|
+
message.set(prefix);
|
|
47
|
+
message.set(der, prefix.length);
|
|
48
|
+
const digest = new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", message));
|
|
49
|
+
let difference = 0;
|
|
50
|
+
for (let i = 0; i < expected.length; i++)
|
|
51
|
+
difference |= expected[i] ^ digest[i];
|
|
52
|
+
return difference === 0;
|
|
53
|
+
}
|
package/dist/cjs/errors.d.ts
CHANGED
|
@@ -14,6 +14,14 @@ export declare const ERROR_CODE_UNEXPECTED_RESPONSE = "unexpected_response";
|
|
|
14
14
|
export declare const ERROR_CODE_NETWORK = "network_error";
|
|
15
15
|
export declare const ERROR_CODE_TIMEOUT = "timeout";
|
|
16
16
|
export declare const ERROR_CODE_RUNTIME = "runtime_error";
|
|
17
|
+
/** A share response omitted the CRID required for delivered-key verification. */
|
|
18
|
+
export declare const ERROR_CODE_MISSING_CRID = "missing_crid";
|
|
19
|
+
/** A held CRID failed the local CRID v1 validation gate. */
|
|
20
|
+
export declare const ERROR_CODE_INVALID_CRID = "invalid_crid";
|
|
21
|
+
/** A caller supplied a non-binary runtime value where DER SPKI bytes are required. */
|
|
22
|
+
export declare const ERROR_CODE_INVALID_CRID_KEY = "invalid_crid_key";
|
|
23
|
+
/** A delivered key does not derive the held CRID. */
|
|
24
|
+
export declare const ERROR_CODE_CRID_MISMATCH = "crid_mismatch";
|
|
17
25
|
/** A dispatched Connector resource mutation could have committed and must be reconciled. */
|
|
18
26
|
export declare const ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN = "connector_resource_outcome_unknown";
|
|
19
27
|
/** A by-ID Connector resource lookup found a revoked lifecycle row. */
|
|
@@ -28,8 +36,8 @@ export declare const ERROR_CODE_UNKNOWN = "unknown";
|
|
|
28
36
|
* Base error thrown by the qURL API client. Catch this to handle all SDK errors.
|
|
29
37
|
*
|
|
30
38
|
* **`status: 0` convention:** Client-only validation, runtime, network, and
|
|
31
|
-
* timeout failures use `status: 0`
|
|
32
|
-
* response-shape guards can also use zero. An `unexpected_response` tied to a
|
|
39
|
+
* timeout failures and local CRID verification failures use `status: 0`
|
|
40
|
+
* because no HTTP status applies. Logical response-shape guards can also use zero. An `unexpected_response` tied to a
|
|
33
41
|
* concrete HTTP contract violation (redirect, oversized body, wrong
|
|
34
42
|
* no-content status/body) instead preserves the observed status. Branch on
|
|
35
43
|
* `.code` first and then `.status`; see {@link ValidationError}.
|
package/dist/cjs/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RuntimeError = exports.TimeoutError = exports.NetworkError = exports.ConnectorResourceOutcomeUnknownError = exports.ServerError = exports.RateLimitError = exports.ValidationError = exports.NotFoundError = exports.AuthorizationError = exports.AuthenticationError = exports.QURLError = exports.ERROR_CODE_UNKNOWN = exports.ERROR_CODE_AMBIGUOUS_RESOURCE = exports.ERROR_CODE_RESOURCE_NOT_FOUND = exports.ERROR_CODE_CONNECTOR_RESOURCE_REVOKED = exports.ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN = exports.ERROR_CODE_RUNTIME = exports.ERROR_CODE_TIMEOUT = exports.ERROR_CODE_NETWORK = exports.ERROR_CODE_UNEXPECTED_RESPONSE = exports.ERROR_CODE_CLIENT_VALIDATION = void 0;
|
|
3
|
+
exports.RuntimeError = exports.TimeoutError = exports.NetworkError = exports.ConnectorResourceOutcomeUnknownError = exports.ServerError = exports.RateLimitError = exports.ValidationError = exports.NotFoundError = exports.AuthorizationError = exports.AuthenticationError = exports.QURLError = exports.ERROR_CODE_UNKNOWN = exports.ERROR_CODE_AMBIGUOUS_RESOURCE = exports.ERROR_CODE_RESOURCE_NOT_FOUND = exports.ERROR_CODE_CONNECTOR_RESOURCE_REVOKED = exports.ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN = exports.ERROR_CODE_CRID_MISMATCH = exports.ERROR_CODE_INVALID_CRID_KEY = exports.ERROR_CODE_INVALID_CRID = exports.ERROR_CODE_MISSING_CRID = exports.ERROR_CODE_RUNTIME = exports.ERROR_CODE_TIMEOUT = exports.ERROR_CODE_NETWORK = exports.ERROR_CODE_UNEXPECTED_RESPONSE = exports.ERROR_CODE_CLIENT_VALIDATION = void 0;
|
|
4
4
|
exports.createError = createError;
|
|
5
5
|
/**
|
|
6
6
|
* Sentinel `.code` values for SDK-internal failure modes (no real HTTP
|
|
@@ -17,6 +17,14 @@ exports.ERROR_CODE_UNEXPECTED_RESPONSE = "unexpected_response";
|
|
|
17
17
|
exports.ERROR_CODE_NETWORK = "network_error";
|
|
18
18
|
exports.ERROR_CODE_TIMEOUT = "timeout";
|
|
19
19
|
exports.ERROR_CODE_RUNTIME = "runtime_error";
|
|
20
|
+
/** A share response omitted the CRID required for delivered-key verification. */
|
|
21
|
+
exports.ERROR_CODE_MISSING_CRID = "missing_crid";
|
|
22
|
+
/** A held CRID failed the local CRID v1 validation gate. */
|
|
23
|
+
exports.ERROR_CODE_INVALID_CRID = "invalid_crid";
|
|
24
|
+
/** A caller supplied a non-binary runtime value where DER SPKI bytes are required. */
|
|
25
|
+
exports.ERROR_CODE_INVALID_CRID_KEY = "invalid_crid_key";
|
|
26
|
+
/** A delivered key does not derive the held CRID. */
|
|
27
|
+
exports.ERROR_CODE_CRID_MISMATCH = "crid_mismatch";
|
|
20
28
|
/** A dispatched Connector resource mutation could have committed and must be reconciled. */
|
|
21
29
|
exports.ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN = "connector_resource_outcome_unknown";
|
|
22
30
|
/** A by-ID Connector resource lookup found a revoked lifecycle row. */
|
|
@@ -31,8 +39,8 @@ exports.ERROR_CODE_UNKNOWN = "unknown";
|
|
|
31
39
|
* Base error thrown by the qURL API client. Catch this to handle all SDK errors.
|
|
32
40
|
*
|
|
33
41
|
* **`status: 0` convention:** Client-only validation, runtime, network, and
|
|
34
|
-
* timeout failures use `status: 0`
|
|
35
|
-
* response-shape guards can also use zero. An `unexpected_response` tied to a
|
|
42
|
+
* timeout failures and local CRID verification failures use `status: 0`
|
|
43
|
+
* because no HTTP status applies. Logical response-shape guards can also use zero. An `unexpected_response` tied to a
|
|
36
44
|
* concrete HTTP contract violation (redirect, oversized body, wrong
|
|
37
45
|
* no-content status/body) instead preserves the observed status. Branch on
|
|
38
46
|
* `.code` first and then `.status`; see {@link ValidationError}.
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { ConnectorResource, ProtectedResource, QURLClient } from "./client.js";
|
|
1
|
+
export { ConnectorResource, isApiKeyRequestScope, ProtectedResource, QURLClient, } from "./client.js";
|
|
2
2
|
export type { EnsureConnectorResourceResult } from "./client.js";
|
|
3
|
-
export {
|
|
3
|
+
export { CRIDVerificationError, ShareLink } from "./share.js";
|
|
4
|
+
export type { CRIDVerificationErrorCode, ShareLinkInit, ShareLinkJSON } from "./share.js";
|
|
5
|
+
export { AuthenticationError, AuthorizationError, ConnectorResourceOutcomeUnknownError, createError, ERROR_CODE_AMBIGUOUS_RESOURCE, ERROR_CODE_CLIENT_VALIDATION, ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN, ERROR_CODE_CONNECTOR_RESOURCE_REVOKED, ERROR_CODE_CRID_MISMATCH, ERROR_CODE_INVALID_CRID, ERROR_CODE_INVALID_CRID_KEY, ERROR_CODE_MISSING_CRID, ERROR_CODE_NETWORK, ERROR_CODE_RESOURCE_NOT_FOUND, ERROR_CODE_RUNTIME, ERROR_CODE_TIMEOUT, ERROR_CODE_UNEXPECTED_RESPONSE, ERROR_CODE_UNKNOWN, NetworkError, NotFoundError, QURLError, RateLimitError, RuntimeError, ServerError, TimeoutError, ValidationError, } from "./errors.js";
|
|
4
6
|
export { VERSION } from "./version.js";
|
|
5
|
-
export type { AccessCode, AccessCodeListOutput, AccessGrant, AccessPolicy, AccessToken, AIAgentCategory, AIAgentPolicy, ApiKey, ApiKeyListOutput, ApiKeyScope, BatchCreateInput, BatchCreateOutput, BatchItemFailure, BatchItemResult, BatchItemSuccess, BillingInvoiceListOutput, ClientOptions, ConnectorInstallation, ConnectorInstallationCapabilities, ConnectorInstallationListOutput, ConnectorInstallationStats, CreateAccessCodeInput, CreateAccessCodeOutput, CreateApiKeyInput, CreateApiKeyOutput, CreateBillingCheckoutInput, CreateInput, CreateOutput, CreatePortalOptions, CreateQurlForResourceInput, CreateResourceInput, CreateWebhookInput, Customer, CheckoutSession, DnsRecord, Domain, DomainCheckDetail, DomainListOutput, DomainVerifyResult, ExtendInput, Invoice, ListApiKeysInput, ListBillingInvoicesInput, ListConnectorInstallationsInput, ListDomainsInput, ListInput, ListOutput, ListWebhooksInput, ListWebhookDeliveriesInput, Meta, MintInput, MintOutput, PaginatedOutput, Portal, PortalSession, ProtectUrlOptions, QURL, QURLErrorData, QurlSummary, Quota, RedeemAccessCodeInput, RedeemAccessCodeOutput, RegisterDomainInput, Resource, ResourceDetail, ResourceHandle, ResourceListInput, ResourceListOutput, ResourceType, RequestOptions, ResolveInput, ResolveOutput, Session, SessionListOutput, SessionTerminateOutput, TombstoneInfo, UnpaginatedOutput, UpdateApiKeyInput, UpdateCustomerInput, UpdateInput, UpdateResourceInput, UpdateResourceQurlInput, UpdateWebhookInput, UsageCostEstimate, UsageCurrentPeriod, UsageDaily, UsageDailyEntry, Webhook, WebhookDelivery, WebhookDeliveryListOutput, WebhookEventType, WebhookEventTypeInfo, WebhookListOutput, WebhookPayload, WebhookWithSecret, } from "./types.js";
|
|
7
|
+
export type { AccessCode, AccessCodeListOutput, AccessGrant, AccessPolicy, AccessToken, AIAgentCategory, AIAgentPolicy, ApiKey, ApiKeyListOutput, ApiKeyRequestScope, ApiKeyScope, BatchCreateInput, BatchCreateOutput, BatchItemFailure, BatchItemResult, BatchItemSuccess, BillingInvoiceListOutput, ClientOptions, ConnectorInstallation, ConnectorInstallationCapabilities, ConnectorInstallationListOutput, ConnectorInstallationStats, CreateAccessCodeInput, CreateAccessCodeOutput, CreateApiKeyInput, CreateApiKeyOutput, CreateDurableApiKeyInput, CreateEnrollmentTokenInput, CreateBillingCheckoutInput, CreateInput, CreateOutput, CreatePortalOptions, CreateQurlForResourceInput, CreateResourceInput, CreateWebhookInput, CredentialClaim, CredentialClaimInput, CredentialKind, CredentialTarget, CredentialTargetInput, Customer, CheckoutSession, DnsRecord, Domain, DomainCheckDetail, DomainListOutput, DomainVerifyResult, ExtendInput, Invoice, ListApiKeysInput, ListBillingInvoicesInput, ListConnectorInstallationsInput, ListDomainsInput, ListInput, ListOutput, ListWebhooksInput, ListWebhookDeliveriesInput, Meta, MintInput, MintOutput, PaginatedOutput, Portal, PortalSession, ProtectUrlOptions, QURL, QURLErrorData, QurlSummary, Quota, RedeemAccessCodeInput, RedeemAccessCodeOutput, RegisterDomainInput, Resource, ResourceDetail, ResourceHandle, ResourceListInput, ResourceListOutput, ResourceType, RequestOptions, ResolveInput, ResolveOutput, Session, SessionListOutput, SessionTerminateOutput, ShareResourceOptions, TombstoneInfo, UnpaginatedOutput, UpdateApiKeyInput, UpdateCustomerInput, UpdateInput, UpdateResourceInput, UpdateResourceQurlInput, UpdateWebhookInput, UsageCostEstimate, UsageCurrentPeriod, UsageDaily, UsageDailyEntry, Webhook, WebhookDelivery, WebhookDeliveryListOutput, WebhookEventType, WebhookEventTypeInfo, WebhookListOutput, WebhookPayload, WebhookWithSecret, } from "./types.js";
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.VERSION = exports.ValidationError = exports.TimeoutError = exports.ServerError = exports.RuntimeError = exports.RateLimitError = exports.QURLError = exports.NotFoundError = exports.NetworkError = exports.ERROR_CODE_UNKNOWN = exports.ERROR_CODE_UNEXPECTED_RESPONSE = exports.ERROR_CODE_TIMEOUT = exports.ERROR_CODE_RUNTIME = exports.ERROR_CODE_RESOURCE_NOT_FOUND = exports.ERROR_CODE_NETWORK = exports.ERROR_CODE_CONNECTOR_RESOURCE_REVOKED = exports.ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN = exports.ERROR_CODE_CLIENT_VALIDATION = exports.ERROR_CODE_AMBIGUOUS_RESOURCE = exports.createError = exports.ConnectorResourceOutcomeUnknownError = exports.AuthorizationError = exports.AuthenticationError = exports.QURLClient = exports.ProtectedResource = exports.ConnectorResource = void 0;
|
|
3
|
+
exports.VERSION = exports.ValidationError = exports.TimeoutError = exports.ServerError = exports.RuntimeError = exports.RateLimitError = exports.QURLError = exports.NotFoundError = exports.NetworkError = exports.ERROR_CODE_UNKNOWN = exports.ERROR_CODE_UNEXPECTED_RESPONSE = exports.ERROR_CODE_TIMEOUT = exports.ERROR_CODE_RUNTIME = exports.ERROR_CODE_RESOURCE_NOT_FOUND = exports.ERROR_CODE_NETWORK = exports.ERROR_CODE_MISSING_CRID = exports.ERROR_CODE_INVALID_CRID_KEY = exports.ERROR_CODE_INVALID_CRID = exports.ERROR_CODE_CRID_MISMATCH = exports.ERROR_CODE_CONNECTOR_RESOURCE_REVOKED = exports.ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN = exports.ERROR_CODE_CLIENT_VALIDATION = exports.ERROR_CODE_AMBIGUOUS_RESOURCE = exports.createError = exports.ConnectorResourceOutcomeUnknownError = exports.AuthorizationError = exports.AuthenticationError = exports.ShareLink = exports.CRIDVerificationError = exports.QURLClient = exports.ProtectedResource = exports.isApiKeyRequestScope = exports.ConnectorResource = void 0;
|
|
4
4
|
var client_js_1 = require("./client.js");
|
|
5
5
|
Object.defineProperty(exports, "ConnectorResource", { enumerable: true, get: function () { return client_js_1.ConnectorResource; } });
|
|
6
|
+
Object.defineProperty(exports, "isApiKeyRequestScope", { enumerable: true, get: function () { return client_js_1.isApiKeyRequestScope; } });
|
|
6
7
|
Object.defineProperty(exports, "ProtectedResource", { enumerable: true, get: function () { return client_js_1.ProtectedResource; } });
|
|
7
8
|
Object.defineProperty(exports, "QURLClient", { enumerable: true, get: function () { return client_js_1.QURLClient; } });
|
|
9
|
+
var share_js_1 = require("./share.js");
|
|
10
|
+
Object.defineProperty(exports, "CRIDVerificationError", { enumerable: true, get: function () { return share_js_1.CRIDVerificationError; } });
|
|
11
|
+
Object.defineProperty(exports, "ShareLink", { enumerable: true, get: function () { return share_js_1.ShareLink; } });
|
|
8
12
|
var errors_js_1 = require("./errors.js");
|
|
9
13
|
Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return errors_js_1.AuthenticationError; } });
|
|
10
14
|
Object.defineProperty(exports, "AuthorizationError", { enumerable: true, get: function () { return errors_js_1.AuthorizationError; } });
|
|
@@ -14,6 +18,10 @@ Object.defineProperty(exports, "ERROR_CODE_AMBIGUOUS_RESOURCE", { enumerable: tr
|
|
|
14
18
|
Object.defineProperty(exports, "ERROR_CODE_CLIENT_VALIDATION", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_CLIENT_VALIDATION; } });
|
|
15
19
|
Object.defineProperty(exports, "ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN; } });
|
|
16
20
|
Object.defineProperty(exports, "ERROR_CODE_CONNECTOR_RESOURCE_REVOKED", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_CONNECTOR_RESOURCE_REVOKED; } });
|
|
21
|
+
Object.defineProperty(exports, "ERROR_CODE_CRID_MISMATCH", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_CRID_MISMATCH; } });
|
|
22
|
+
Object.defineProperty(exports, "ERROR_CODE_INVALID_CRID", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_INVALID_CRID; } });
|
|
23
|
+
Object.defineProperty(exports, "ERROR_CODE_INVALID_CRID_KEY", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_INVALID_CRID_KEY; } });
|
|
24
|
+
Object.defineProperty(exports, "ERROR_CODE_MISSING_CRID", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_MISSING_CRID; } });
|
|
17
25
|
Object.defineProperty(exports, "ERROR_CODE_NETWORK", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_NETWORK; } });
|
|
18
26
|
Object.defineProperty(exports, "ERROR_CODE_RESOURCE_NOT_FOUND", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_RESOURCE_NOT_FOUND; } });
|
|
19
27
|
Object.defineProperty(exports, "ERROR_CODE_RUNTIME", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_RUNTIME; } });
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ERROR_CODE_CRID_MISMATCH, ERROR_CODE_INVALID_CRID, ERROR_CODE_INVALID_CRID_KEY, ERROR_CODE_MISSING_CRID, QURLError } from "./errors.js";
|
|
2
|
+
export type CRIDVerificationErrorCode = typeof ERROR_CODE_MISSING_CRID | typeof ERROR_CODE_INVALID_CRID | typeof ERROR_CODE_INVALID_CRID_KEY | typeof ERROR_CODE_CRID_MISMATCH;
|
|
3
|
+
/** A local, fail-closed CRID verification failure. */
|
|
4
|
+
export declare class CRIDVerificationError extends QURLError {
|
|
5
|
+
readonly code: CRIDVerificationErrorCode;
|
|
6
|
+
constructor(code: CRIDVerificationErrorCode, message: string);
|
|
7
|
+
}
|
|
8
|
+
export interface ShareLinkInit {
|
|
9
|
+
link: string;
|
|
10
|
+
qurlId?: string;
|
|
11
|
+
crid?: string;
|
|
12
|
+
type?: string;
|
|
13
|
+
expiresAt?: Date;
|
|
14
|
+
expiresInSeconds?: number;
|
|
15
|
+
singleUse?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Credential-redacted, JSON-safe representation returned by {@link ShareLink.toJSON}. */
|
|
18
|
+
export interface ShareLinkJSON {
|
|
19
|
+
link: "[redacted]";
|
|
20
|
+
qurlId?: string;
|
|
21
|
+
crid?: string;
|
|
22
|
+
type?: string;
|
|
23
|
+
expiresAt?: string;
|
|
24
|
+
expiresInSeconds?: number;
|
|
25
|
+
singleUse?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A freshly minted, one-time-returned access link for an existing resource.
|
|
29
|
+
* Instances are frozen during construction and are not designed for subclassing.
|
|
30
|
+
*/
|
|
31
|
+
export declare class ShareLink {
|
|
32
|
+
#private;
|
|
33
|
+
/**
|
|
34
|
+
* The one-time secret share link. This property is deliberately
|
|
35
|
+
* non-enumerable: read it directly instead of spreading or cloning the object.
|
|
36
|
+
*/
|
|
37
|
+
readonly link: string;
|
|
38
|
+
/** Token ID when supplied by the service. */
|
|
39
|
+
readonly qurlId?: string;
|
|
40
|
+
readonly crid?: string;
|
|
41
|
+
readonly type?: string;
|
|
42
|
+
readonly expiresInSeconds?: number;
|
|
43
|
+
/** Whether the link is single-use; undefined when the service omits the field. */
|
|
44
|
+
readonly singleUse?: boolean;
|
|
45
|
+
constructor(init: ShareLinkInit);
|
|
46
|
+
/** Effective expiry as a defensive copy; mutating it does not change this share. */
|
|
47
|
+
get expiresAt(): Date | undefined;
|
|
48
|
+
/** Serialize safe metadata while redacting the one-time-returned credential. */
|
|
49
|
+
toJSON(): ShareLinkJSON;
|
|
50
|
+
/**
|
|
51
|
+
* Verify that this response's CRID was derived from a DER SubjectPublicKeyInfo
|
|
52
|
+
* already trusted by the caller. This binds the response CRID to that key; it
|
|
53
|
+
* does not independently prove that the secret link fragment belongs to it.
|
|
54
|
+
*/
|
|
55
|
+
verifyCrid(derSpki: ArrayBuffer | ArrayBufferView): Promise<void>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ShareLink = exports.CRIDVerificationError = void 0;
|
|
4
|
+
const errors_js_1 = require("./errors.js");
|
|
5
|
+
const crid_js_1 = require("./crid.js");
|
|
6
|
+
/** A local, fail-closed CRID verification failure. */
|
|
7
|
+
class CRIDVerificationError extends errors_js_1.QURLError {
|
|
8
|
+
constructor(code, message) {
|
|
9
|
+
super({ status: 0, code, title: "CRID Verification Error", detail: message });
|
|
10
|
+
this.name = "CRIDVerificationError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.CRIDVerificationError = CRIDVerificationError;
|
|
14
|
+
/**
|
|
15
|
+
* A freshly minted, one-time-returned access link for an existing resource.
|
|
16
|
+
* Instances are frozen during construction and are not designed for subclassing.
|
|
17
|
+
*/
|
|
18
|
+
class ShareLink {
|
|
19
|
+
/**
|
|
20
|
+
* The one-time secret share link. This property is deliberately
|
|
21
|
+
* non-enumerable: read it directly instead of spreading or cloning the object.
|
|
22
|
+
*/
|
|
23
|
+
link;
|
|
24
|
+
/** Token ID when supplied by the service. */
|
|
25
|
+
qurlId;
|
|
26
|
+
crid;
|
|
27
|
+
type;
|
|
28
|
+
#expiresAtEpochMs;
|
|
29
|
+
expiresInSeconds;
|
|
30
|
+
/** Whether the link is single-use; undefined when the service omits the field. */
|
|
31
|
+
singleUse;
|
|
32
|
+
constructor(init) {
|
|
33
|
+
if (typeof init?.link !== "string" || init.link.trim() === "") {
|
|
34
|
+
throw new TypeError("ShareLink: link must be a non-empty string");
|
|
35
|
+
}
|
|
36
|
+
this.link = init.link;
|
|
37
|
+
// Keep accidental object spread/structured logging from copying the
|
|
38
|
+
// one-time-returned credential. Callers can still read `.link` explicitly.
|
|
39
|
+
Object.defineProperty(this, "link", {
|
|
40
|
+
enumerable: false,
|
|
41
|
+
writable: false,
|
|
42
|
+
configurable: false,
|
|
43
|
+
});
|
|
44
|
+
this.qurlId = init.qurlId;
|
|
45
|
+
this.crid = init.crid;
|
|
46
|
+
this.type = init.type;
|
|
47
|
+
const expiresAtEpochMs = init.expiresAt?.getTime();
|
|
48
|
+
this.#expiresAtEpochMs = Number.isFinite(expiresAtEpochMs) ? expiresAtEpochMs : undefined;
|
|
49
|
+
this.expiresInSeconds = init.expiresInSeconds;
|
|
50
|
+
this.singleUse = init.singleUse;
|
|
51
|
+
Object.freeze(this);
|
|
52
|
+
}
|
|
53
|
+
/** Effective expiry as a defensive copy; mutating it does not change this share. */
|
|
54
|
+
get expiresAt() {
|
|
55
|
+
return this.#expiresAtEpochMs === undefined ? undefined : new Date(this.#expiresAtEpochMs);
|
|
56
|
+
}
|
|
57
|
+
/** Serialize safe metadata while redacting the one-time-returned credential. */
|
|
58
|
+
toJSON() {
|
|
59
|
+
const expiresAt = this.expiresAt;
|
|
60
|
+
return {
|
|
61
|
+
link: "[redacted]",
|
|
62
|
+
qurlId: this.qurlId,
|
|
63
|
+
crid: this.crid,
|
|
64
|
+
type: this.type,
|
|
65
|
+
expiresAt: expiresAt && Number.isFinite(expiresAt.getTime()) ? expiresAt.toISOString() : undefined,
|
|
66
|
+
expiresInSeconds: this.expiresInSeconds,
|
|
67
|
+
singleUse: this.singleUse,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Prevent Node's default object inspector from printing the credential. */
|
|
71
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
72
|
+
return this.toJSON();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Verify that this response's CRID was derived from a DER SubjectPublicKeyInfo
|
|
76
|
+
* already trusted by the caller. This binds the response CRID to that key; it
|
|
77
|
+
* does not independently prove that the secret link fragment belongs to it.
|
|
78
|
+
*/
|
|
79
|
+
async verifyCrid(derSpki) {
|
|
80
|
+
if (this.crid === undefined) {
|
|
81
|
+
throw new CRIDVerificationError(errors_js_1.ERROR_CODE_MISSING_CRID, "Share response carried no CRID; the resource key cannot be verified");
|
|
82
|
+
}
|
|
83
|
+
if (!(0, crid_js_1.parseCrid)(this.crid))
|
|
84
|
+
throw invalidCrid();
|
|
85
|
+
const key = copyBytes(derSpki);
|
|
86
|
+
const crypto = globalThis.crypto;
|
|
87
|
+
if (!crypto?.subtle) {
|
|
88
|
+
throw new errors_js_1.RuntimeError("globalThis.crypto.subtle is required to verify a CRID");
|
|
89
|
+
}
|
|
90
|
+
if (!(await (0, crid_js_1.cridKeyMatches)(this.crid, key))) {
|
|
91
|
+
throw new CRIDVerificationError(errors_js_1.ERROR_CODE_CRID_MISMATCH, "Resource key does not derive the held CRID");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.ShareLink = ShareLink;
|
|
96
|
+
function copyBytes(value) {
|
|
97
|
+
try {
|
|
98
|
+
if (ArrayBuffer.isView(value)) {
|
|
99
|
+
return Uint8Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
|
100
|
+
}
|
|
101
|
+
// The intrinsic checks the real internal slot, including across realms.
|
|
102
|
+
return new Uint8Array(ArrayBuffer.prototype.slice.call(value, 0));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (!(error instanceof TypeError))
|
|
106
|
+
throw error;
|
|
107
|
+
// Non-binary values and detached buffers are invalid caller key material.
|
|
108
|
+
}
|
|
109
|
+
throw new CRIDVerificationError(errors_js_1.ERROR_CODE_INVALID_CRID_KEY, "Resource key must be binary DER SPKI data");
|
|
110
|
+
}
|
|
111
|
+
function invalidCrid() {
|
|
112
|
+
return new CRIDVerificationError(errors_js_1.ERROR_CODE_INVALID_CRID, "Share response carried an invalid CRID");
|
|
113
|
+
}
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -81,6 +81,8 @@ export interface QurlSummary {
|
|
|
81
81
|
*/
|
|
82
82
|
export interface QURL {
|
|
83
83
|
resource_id: string;
|
|
84
|
+
/** Raw REST metadata; check presence before use, or use a validated resource handle. */
|
|
85
|
+
crid?: string;
|
|
84
86
|
type?: ResourceType;
|
|
85
87
|
target_url: string;
|
|
86
88
|
status: "active" | "revoked";
|
|
@@ -125,6 +127,8 @@ export interface CreateInput {
|
|
|
125
127
|
export interface CreateOutput {
|
|
126
128
|
qurl_id: string;
|
|
127
129
|
resource_id: string;
|
|
130
|
+
/** Raw REST metadata; check presence before use, or use a validated resource handle. */
|
|
131
|
+
crid?: string;
|
|
128
132
|
qurl_link: string;
|
|
129
133
|
branded_domain?: string;
|
|
130
134
|
qurl_site: string;
|
|
@@ -291,7 +295,9 @@ export interface CreatePortalOptions {
|
|
|
291
295
|
*/
|
|
292
296
|
export interface Portal {
|
|
293
297
|
/** Identifies the protected resource this link opens. */
|
|
294
|
-
|
|
298
|
+
crid: string;
|
|
299
|
+
/** Public key used for verification. */
|
|
300
|
+
resourcePublicKey: string;
|
|
295
301
|
/** The shareable qURL link. */
|
|
296
302
|
link: string;
|
|
297
303
|
/** The qURL-hosted site for this resource, when returned by the API. */
|
|
@@ -307,7 +313,7 @@ export interface Portal {
|
|
|
307
313
|
* Result of a successful `enterPortal`: the reachable resource.
|
|
308
314
|
*
|
|
309
315
|
* Carries the reachable resource URL and the access lifetime reported by
|
|
310
|
-
* qURL (`openSeconds` is `0` when the API does not report one). `
|
|
316
|
+
* qURL (`openSeconds` is `0` when the API does not report one). `resourcePublicKey`
|
|
311
317
|
* is populated by this SDK's API-backed opener; the field does not exist on
|
|
312
318
|
* qurl-go's offline `ResourceHandle`.
|
|
313
319
|
*/
|
|
@@ -316,8 +322,8 @@ export interface ResourceHandle {
|
|
|
316
322
|
resourceUrl: string;
|
|
317
323
|
/** How long access stays open in seconds, as reported by qURL. */
|
|
318
324
|
openSeconds: number;
|
|
319
|
-
/** The
|
|
320
|
-
|
|
325
|
+
/** The verification key behind the portal, when reported. */
|
|
326
|
+
resourcePublicKey?: string;
|
|
321
327
|
}
|
|
322
328
|
/** Input for headless qURL resolution. */
|
|
323
329
|
export interface ResolveInput {
|
|
@@ -374,13 +380,15 @@ export interface BatchCreateInput {
|
|
|
374
380
|
* A successfully created item in a batch create response.
|
|
375
381
|
*
|
|
376
382
|
* This shape is intentionally slimmer than {@link CreateOutput}: batch items do
|
|
377
|
-
* not include `qurl_id` or `label`. Use the returned `
|
|
383
|
+
* not include `qurl_id` or `label`. Use the returned `crid` to fetch more
|
|
378
384
|
* detail when per-token identifiers are needed.
|
|
379
385
|
*/
|
|
380
386
|
export interface BatchItemSuccess {
|
|
381
387
|
index: number;
|
|
382
388
|
success: true;
|
|
383
389
|
resource_id: string;
|
|
390
|
+
/** Raw REST metadata; check presence before use, or use a validated resource handle. */
|
|
391
|
+
crid?: string;
|
|
384
392
|
qurl_link: string;
|
|
385
393
|
branded_domain?: string;
|
|
386
394
|
qurl_site: string;
|
|
@@ -468,6 +476,15 @@ export interface ResourceListInput {
|
|
|
468
476
|
export interface ResourceListOutput extends PaginatedOutput {
|
|
469
477
|
resources: Resource[];
|
|
470
478
|
}
|
|
479
|
+
/** Options for minting a fresh share link from an existing CRID. */
|
|
480
|
+
export interface ShareResourceOptions {
|
|
481
|
+
/**
|
|
482
|
+
* Requested lifetime in positive whole seconds. Omit to use the platform
|
|
483
|
+
* default; an explicit zero is rejected to avoid silently lengthening a
|
|
484
|
+
* computed lifetime.
|
|
485
|
+
*/
|
|
486
|
+
ttlSeconds?: number;
|
|
487
|
+
}
|
|
471
488
|
/** Input for minting a qURL against an existing resource. */
|
|
472
489
|
export type CreateQurlForResourceInput = Omit<CreateInput, "target_url" | "custom_domain" | "type"> & {
|
|
473
490
|
/**
|
|
@@ -730,30 +747,75 @@ export interface WebhookPayload {
|
|
|
730
747
|
api_version?: string;
|
|
731
748
|
data?: Record<string, unknown>;
|
|
732
749
|
}
|
|
733
|
-
|
|
734
|
-
export
|
|
750
|
+
/** Closed scope vocabulary accepted by current create/update request schemas. */
|
|
751
|
+
export type ApiKeyRequestScope = "qurl:read" | "qurl:write" | "qurl:resolve" | "qurl:agent";
|
|
752
|
+
export type ApiKeyScope = OpenString<ApiKeyRequestScope>;
|
|
753
|
+
/** Which credential `createApiKey` mints. `device` is system-minted only. */
|
|
754
|
+
export type CredentialKind = OpenString<"api_key" | "enrollment_token" | "device">;
|
|
755
|
+
/** Closed target vocabulary accepted by the current enrollment-token request schema. */
|
|
756
|
+
export type CredentialTargetInput = "agent" | "connector";
|
|
757
|
+
/** What a `kind: "enrollment_token"` credential enrolls. */
|
|
758
|
+
export type CredentialTarget = OpenString<CredentialTargetInput>;
|
|
759
|
+
/** Closed resource claim binding an enrollment token to one resource. */
|
|
760
|
+
export interface CredentialClaimInput {
|
|
761
|
+
type: "connector";
|
|
762
|
+
/** Immutable 3-64 character connector slug. */
|
|
763
|
+
id: string;
|
|
764
|
+
}
|
|
765
|
+
/** A potentially partial resource claim returned for a credential. */
|
|
766
|
+
export interface CredentialClaim {
|
|
767
|
+
/** `connector` is the only claim type today; responses remain forward-compatible. */
|
|
768
|
+
type?: OpenString<CredentialClaimInput["type"]>;
|
|
769
|
+
id?: string;
|
|
770
|
+
}
|
|
771
|
+
interface CreateCredentialBase {
|
|
735
772
|
name: string;
|
|
736
|
-
|
|
773
|
+
}
|
|
774
|
+
/** Input for a durable, caller-scoped account API key. */
|
|
775
|
+
export interface CreateDurableApiKeyInput extends CreateCredentialBase {
|
|
776
|
+
/** Omit for backward compatibility; the SDK sends `api_key`. */
|
|
777
|
+
kind?: "api_key";
|
|
778
|
+
scopes: ApiKeyRequestScope[];
|
|
779
|
+
target?: never;
|
|
780
|
+
claims?: never;
|
|
781
|
+
expires_in?: never;
|
|
782
|
+
}
|
|
783
|
+
/** Input for a one-shot agent or Connector enrollment token. */
|
|
784
|
+
export interface CreateEnrollmentTokenInput extends CreateCredentialBase {
|
|
785
|
+
kind: "enrollment_token";
|
|
786
|
+
/**
|
|
787
|
+
* Omit for enrollment tokens: the server derives scopes from the target.
|
|
788
|
+
*/
|
|
789
|
+
scopes?: never;
|
|
790
|
+
/** Derived from `claims` when omitted. */
|
|
791
|
+
target?: CredentialTargetInput;
|
|
792
|
+
/** At most one entry is supported today. */
|
|
793
|
+
claims?: CredentialClaimInput[];
|
|
794
|
+
/** Defaults to 24h and cannot exceed 24h. */
|
|
737
795
|
expires_in?: string;
|
|
738
|
-
purpose?: "tunnel_bootstrap";
|
|
739
|
-
tunnel_slug?: string;
|
|
740
796
|
}
|
|
797
|
+
export type CreateApiKeyInput = CreateDurableApiKeyInput | CreateEnrollmentTokenInput;
|
|
741
798
|
export interface UpdateApiKeyInput {
|
|
742
799
|
name?: string;
|
|
743
|
-
scopes?:
|
|
800
|
+
scopes?: ApiKeyRequestScope[];
|
|
744
801
|
}
|
|
745
802
|
export interface ApiKey {
|
|
746
803
|
key_id?: string;
|
|
747
804
|
key_prefix?: string;
|
|
805
|
+
/** Unknown future kinds may appear; clients must tolerate them. */
|
|
806
|
+
kind?: CredentialKind;
|
|
748
807
|
name?: string;
|
|
808
|
+
/** Server-assigned for enrollment tokens and device credentials. */
|
|
749
809
|
scopes?: ApiKeyScope[];
|
|
750
810
|
status?: "active" | "revoked" | (string & {});
|
|
751
811
|
created_at?: string;
|
|
752
812
|
updated_at?: string;
|
|
753
813
|
last_used_at?: string;
|
|
754
814
|
expires_at?: string;
|
|
755
|
-
|
|
756
|
-
|
|
815
|
+
/** Present only on enrollment tokens. */
|
|
816
|
+
target?: CredentialTarget;
|
|
817
|
+
/** Present only on bound enrollment tokens. */
|
|
818
|
+
claims?: CredentialClaim[];
|
|
757
819
|
}
|
|
758
820
|
export interface CreateApiKeyOutput extends ApiKey {
|
|
759
821
|
/** Full API key secret, returned only on create. Store it immediately. */
|
|
@@ -776,6 +838,7 @@ export interface RedeemAccessCodeOutput {
|
|
|
776
838
|
redirect_url?: string;
|
|
777
839
|
}
|
|
778
840
|
export interface CreateAccessCodeInput {
|
|
841
|
+
/** The resource CRID; the wire field retains its API name. */
|
|
779
842
|
resource_id: string;
|
|
780
843
|
name?: string;
|
|
781
844
|
max_uses?: number;
|
package/dist/cjs/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.7.0";
|
package/dist/cjs/version.js
CHANGED