@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.
@@ -0,0 +1,2 @@
1
+ export declare function parseCrid(value: unknown): Uint8Array | undefined;
2
+ export declare function cridKeyMatches(value: unknown, der: Uint8Array): Promise<boolean>;
@@ -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
+ }
@@ -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` because no HTTP status applies. Logical
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}.
@@ -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` because no HTTP status applies. Logical
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}.
@@ -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 { 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_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";
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
+ }
@@ -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
- resourceId: string;
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). `resourceId`
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 LayerV resource id behind the portal, when reported. */
320
- resourceId?: string;
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 `resource_id` to fetch more
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
- export type ApiKeyScope = OpenString<"qurl:read" | "qurl:write" | "qurl:resolve" | "qurl:agent">;
734
- export interface CreateApiKeyInput {
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
- scopes: ApiKeyScope[];
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?: ApiKeyScope[];
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
- purpose?: "tunnel_bootstrap" | (string & {});
756
- tunnel_slug?: string;
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;
@@ -1 +1 @@
1
- export declare const VERSION = "0.6.0";
1
+ export declare const VERSION = "0.7.0";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = "0.6.0"; // x-release-please-version
4
+ exports.VERSION = "0.7.0"; // x-release-please-version