@layervai/qurl 0.4.0 → 0.6.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.
Files changed (51) hide show
  1. package/README.md +174 -61
  2. package/dist/cjs/client.d.ts +46 -20
  3. package/dist/cjs/client.js +338 -54
  4. package/dist/cjs/errors.d.ts +24 -3
  5. package/dist/cjs/errors.js +34 -4
  6. package/dist/cjs/index.d.ts +3 -2
  7. package/dist/cjs/index.js +5 -1
  8. package/dist/cjs/node/deployment.d.ts +1 -1
  9. package/dist/cjs/node/deployment.js +2 -2
  10. package/dist/cjs/node/native-udp.d.ts +2 -4
  11. package/dist/cjs/node/native-udp.js +8 -8
  12. package/dist/cjs/node/portal-opener.d.ts +54 -23
  13. package/dist/cjs/node/portal-opener.js +643 -340
  14. package/dist/cjs/node.d.ts +2 -2
  15. package/dist/cjs/node.js +8 -1
  16. package/dist/cjs/types.d.ts +18 -2
  17. package/dist/cjs/version.d.ts +1 -1
  18. package/dist/cjs/version.js +1 -1
  19. package/dist/esm/client.d.ts +46 -20
  20. package/dist/esm/client.d.ts.map +1 -1
  21. package/dist/esm/client.js +337 -54
  22. package/dist/esm/client.js.map +1 -1
  23. package/dist/esm/errors.d.ts +24 -3
  24. package/dist/esm/errors.d.ts.map +1 -1
  25. package/dist/esm/errors.js +32 -3
  26. package/dist/esm/errors.js.map +1 -1
  27. package/dist/esm/index.d.ts +3 -2
  28. package/dist/esm/index.d.ts.map +1 -1
  29. package/dist/esm/index.js +2 -2
  30. package/dist/esm/index.js.map +1 -1
  31. package/dist/esm/node/deployment.d.ts +1 -1
  32. package/dist/esm/node/deployment.d.ts.map +1 -1
  33. package/dist/esm/node/deployment.js +2 -2
  34. package/dist/esm/node/deployment.js.map +1 -1
  35. package/dist/esm/node/native-udp.d.ts +2 -4
  36. package/dist/esm/node/native-udp.d.ts.map +1 -1
  37. package/dist/esm/node/native-udp.js +5 -5
  38. package/dist/esm/node/native-udp.js.map +1 -1
  39. package/dist/esm/node/portal-opener.d.ts +54 -23
  40. package/dist/esm/node/portal-opener.d.ts.map +1 -1
  41. package/dist/esm/node/portal-opener.js +636 -340
  42. package/dist/esm/node/portal-opener.js.map +1 -1
  43. package/dist/esm/node.d.ts +2 -2
  44. package/dist/esm/node.d.ts.map +1 -1
  45. package/dist/esm/node.js +1 -1
  46. package/dist/esm/node.js.map +1 -1
  47. package/dist/esm/types.d.ts +18 -2
  48. package/dist/esm/types.d.ts.map +1 -1
  49. package/dist/esm/version.d.ts +1 -1
  50. package/dist/esm/version.js +1 -1
  51. package/package.json +4 -4
@@ -1,4 +1,5 @@
1
- export { ProtectedResource, QURLClient } from "./client.js";
2
- export { AuthenticationError, AuthorizationError, createError, ERROR_CODE_AMBIGUOUS_RESOURCE, ERROR_CODE_CLIENT_VALIDATION, 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";
1
+ export { ConnectorResource, ProtectedResource, QURLClient } from "./client.js";
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
4
  export { VERSION } from "./version.js";
4
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";
package/dist/cjs/index.js CHANGED
@@ -1,15 +1,19 @@
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_CLIENT_VALIDATION = exports.ERROR_CODE_AMBIGUOUS_RESOURCE = exports.createError = exports.AuthorizationError = exports.AuthenticationError = exports.QURLClient = exports.ProtectedResource = 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_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;
4
4
  var client_js_1 = require("./client.js");
5
+ Object.defineProperty(exports, "ConnectorResource", { enumerable: true, get: function () { return client_js_1.ConnectorResource; } });
5
6
  Object.defineProperty(exports, "ProtectedResource", { enumerable: true, get: function () { return client_js_1.ProtectedResource; } });
6
7
  Object.defineProperty(exports, "QURLClient", { enumerable: true, get: function () { return client_js_1.QURLClient; } });
7
8
  var errors_js_1 = require("./errors.js");
8
9
  Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return errors_js_1.AuthenticationError; } });
9
10
  Object.defineProperty(exports, "AuthorizationError", { enumerable: true, get: function () { return errors_js_1.AuthorizationError; } });
11
+ Object.defineProperty(exports, "ConnectorResourceOutcomeUnknownError", { enumerable: true, get: function () { return errors_js_1.ConnectorResourceOutcomeUnknownError; } });
10
12
  Object.defineProperty(exports, "createError", { enumerable: true, get: function () { return errors_js_1.createError; } });
11
13
  Object.defineProperty(exports, "ERROR_CODE_AMBIGUOUS_RESOURCE", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_AMBIGUOUS_RESOURCE; } });
12
14
  Object.defineProperty(exports, "ERROR_CODE_CLIENT_VALIDATION", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_CLIENT_VALIDATION; } });
15
+ Object.defineProperty(exports, "ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_CONNECTOR_RESOURCE_OUTCOME_UNKNOWN; } });
16
+ Object.defineProperty(exports, "ERROR_CODE_CONNECTOR_RESOURCE_REVOKED", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_CONNECTOR_RESOURCE_REVOKED; } });
13
17
  Object.defineProperty(exports, "ERROR_CODE_NETWORK", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_NETWORK; } });
14
18
  Object.defineProperty(exports, "ERROR_CODE_RESOURCE_NOT_FOUND", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_RESOURCE_NOT_FOUND; } });
15
19
  Object.defineProperty(exports, "ERROR_CODE_RUNTIME", { enumerable: true, get: function () { return errors_js_1.ERROR_CODE_RUNTIME; } });
@@ -32,7 +32,7 @@ interface DeploymentFileRuntime {
32
32
  readonly read: (descriptor: number, buffer: Buffer, offset: number, length: number, position: number) => number;
33
33
  readonly close: (descriptor: number) => void;
34
34
  }
35
- /** Load and validate deployment trust once, before an opener can do network I/O. */
35
+ /** Load and validate deployment trust before an opener can do network I/O. */
36
36
  export declare function loadPortalDeployment(explicit?: PortalDeployment): ValidatedDeployment;
37
37
  declare function readBoundedDeploymentFile(path: string, runtime?: DeploymentFileRuntime): Buffer;
38
38
  export declare function fingerprintKey(key: Uint8Array): string;
@@ -17,7 +17,7 @@ const defaultDeploymentFileRuntime = {
17
17
  read: node_fs_1.readSync,
18
18
  close: node_fs_1.closeSync,
19
19
  };
20
- /** Load and validate deployment trust once, before an opener can do network I/O. */
20
+ /** Load and validate deployment trust before an opener can do network I/O. */
21
21
  function loadPortalDeployment(explicit) {
22
22
  if (explicit !== undefined)
23
23
  return validateDeployment(explicit);
@@ -32,7 +32,7 @@ function loadPortalDeployment(explicit) {
32
32
  return validateDeploymentValue((0, strict_json_js_1.parseStrictJson)(raw, MAX_DEPLOYMENT_BYTES));
33
33
  }
34
34
  function readBoundedDeploymentFile(path, runtime = defaultDeploymentFileRuntime) {
35
- // O_NONBLOCK prevents a FIFO or device path from hanging construction before
35
+ // O_NONBLOCK prevents a FIFO or device path from hanging startup before
36
36
  // fstat can enforce the regular-file contract. It has no effect on files.
37
37
  const descriptor = runtime.open(path, node_fs_1.constants.O_RDONLY | node_fs_1.constants.O_NONBLOCK);
38
38
  try {
@@ -2,13 +2,11 @@ import { lookup } from "node:dns/promises";
2
2
  import { type Socket } from "node:dgram";
3
3
  import { buildNHPMessage, decryptNHPReply, type NHPMessage } from "./nhp-wire.js";
4
4
  import type { ValidatedCell } from "./deployment.js";
5
- export declare const NATIVE_DEFAULT_TIMEOUT_MS = 3000;
6
- export declare const NATIVE_DEFAULT_MAX_ADDRESSES = 3;
7
5
  export interface NativeExchangeOptions {
8
6
  readonly signal?: AbortSignal;
9
- /** Per-address timeout; serial fallback can use maxAddresses times this value. */
7
+ /** Internal per-address timeout. PortalOpener does not expose this setting. */
10
8
  readonly timeoutMs?: number;
11
- /** Maximum number of resolved addresses tried serially. */
9
+ /** Internal serial-address cap. PortalOpener does not expose this setting. */
12
10
  readonly maxAddresses?: number;
13
11
  }
14
12
  interface NativeKnockRuntime {
@@ -1,15 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.nativeUdpTesting = exports.NATIVE_DEFAULT_MAX_ADDRESSES = exports.NATIVE_DEFAULT_TIMEOUT_MS = void 0;
3
+ exports.nativeUdpTesting = void 0;
4
4
  exports.nativeKnock = nativeKnock;
5
5
  const promises_1 = require("node:dns/promises");
6
6
  const node_dgram_1 = require("node:dgram");
7
7
  const node_net_1 = require("node:net");
8
8
  const nhp_wire_js_1 = require("./nhp-wire.js");
9
- // Keep these defaults exported inside the Node implementation so the portal
10
- // lifecycle can derive one bounded whole-open deadline from the same values.
11
- exports.NATIVE_DEFAULT_TIMEOUT_MS = 3_000;
12
- exports.NATIVE_DEFAULT_MAX_ADDRESSES = 3;
9
+ // These are internal per-address exchange bounds. PortalOpener applies its own
10
+ // whole-open deadline and does not expose per-address tuning.
11
+ const NATIVE_DEFAULT_TIMEOUT_MS = 3_000;
12
+ const NATIVE_DEFAULT_MAX_ADDRESSES = 3;
13
13
  const ipv4Denied = new node_net_1.BlockList();
14
14
  // Keep this set in lockstep with qurl-go's nonRoutablePrefixes plus the
15
15
  // IsPrivate/IsLoopback/IsLinkLocal/IsMulticast predicates it applies first.
@@ -107,13 +107,13 @@ async function nativeKnockWithRuntime(cell, devicePrivateKey, body, options, run
107
107
  body,
108
108
  });
109
109
  try {
110
- const addresses = await runtime.resolveAddresses(cell.host, options.maxAddresses ?? exports.NATIVE_DEFAULT_MAX_ADDRESSES, options.signal);
110
+ const addresses = await runtime.resolveAddresses(cell.host, options.maxAddresses ?? NATIVE_DEFAULT_MAX_ADDRESSES, options.signal);
111
111
  let lastError;
112
112
  for (const address of addresses) {
113
113
  try {
114
- const packet = await runtime.exchange(address.address, address.family, cell.port, built.packet, options.timeoutMs ?? exports.NATIVE_DEFAULT_TIMEOUT_MS, options.signal);
114
+ const packet = await runtime.exchange(address.address, address.family, cell.port, built.packet, options.timeoutMs ?? NATIVE_DEFAULT_TIMEOUT_MS, options.signal);
115
115
  const reply = runtime.decryptReply(devicePrivateKey, cell.serverPublicKey, packet);
116
- // NHP 1.1 COOKIE is a stateless busy response. Go intentionally does
116
+ // NHP COOKIE is a stateless busy response. Go intentionally does
117
117
  // not bind its counter to the request; only an ACK must match it.
118
118
  if (reply.type === nhp_wire_js_1.NHP_TYPE_COOKIE)
119
119
  return reply;
@@ -3,43 +3,53 @@ import { type PortalDeployment, type ValidatedCell, type ValidatedDeployment } f
3
3
  import { type NativeExchangeOptions } from "./native-udp.js";
4
4
  import { type VerifiedQv2Link } from "./qv2.js";
5
5
  export interface CreatePortalOpenerOptions {
6
- /** Full qv2t1 qURL. The opener verifies and then drops this immutable string. */
6
+ /** Full qv2t1 qURL. The opener re-verifies it before each native open. */
7
7
  readonly qurl: string;
8
- /** Native-only is deliberate. There is no relay or HTTP-resolve fallback. */
9
- readonly transport: "native-only";
10
- /** Public deployment trust. Omit to load QURL_DEPLOYMENT once at construction. */
8
+ /** Public deployment trust. Omit to load QURL_DEPLOYMENT on the first start. */
11
9
  readonly deployment?: PortalDeployment;
12
- /** Per-address UDP attempt timeout. Use start({ signal }) for one overall deadline. */
13
- readonly timeoutMs?: number;
14
- /** Maximum serial address attempts for one native exchange. */
15
- readonly maxAddresses?: number;
10
+ /** Whole-operation deadline for each NHP open. The default is 15 seconds. */
11
+ readonly openTimeoutMs?: number;
12
+ /** Protected-content fetch implementation. Native NHP opening never uses it. */
13
+ readonly fetch?: typeof globalThis.fetch;
16
14
  }
17
15
  export interface PortalStartOptions {
18
16
  readonly signal?: AbortSignal;
19
17
  }
18
+ /**
19
+ * Build one request for the exact target selected by `fetch` or
20
+ * `fetchDescendant`. The URL is a disposable copy; the opener pins the final
21
+ * request URL and Host to its SDK-owned target after this function returns.
22
+ */
20
23
  export type PortalRequestBuilder = (authenticatedTarget: URL) => RequestInit;
21
24
  export interface PortalFetchOptions {
22
25
  /** Use `error` for signed requests whose method, target, timestamp, and nonce must not be replayed. */
23
26
  readonly redirects?: "follow" | "error";
24
27
  }
25
- export type PortalRenewalFailure = {
26
- readonly kind: "transport" | "busy" | "denied" | "invalid_reply" | "local_state";
27
- };
28
- export type PortalSessionHealth = {
29
- readonly state: "idle" | "starting" | "closed";
30
- } | {
31
- readonly state: "healthy" | "renewing" | "degraded" | "expired";
32
- readonly expiresInMs: number;
33
- readonly backgroundAttempts: number;
34
- readonly renewalFailure?: PortalRenewalFailure;
35
- };
28
+ export type PortalOpenerState = "new" | "starting" | "ready" | "degraded" | "closed";
29
+ export type PortalOpenerFailureClass = "" | "open_failed" | "target_changed";
30
+ /** Secret-free, nonblocking lifecycle snapshot. */
31
+ export interface PortalOpenerHealth {
32
+ readonly state: PortalOpenerState;
33
+ readonly ready: boolean;
34
+ readonly expiresAt?: Date;
35
+ readonly renewAt?: Date;
36
+ readonly lastOpenSucceededAt?: Date;
37
+ readonly lastFailureClass: PortalOpenerFailureClass;
38
+ readonly consecutiveFailures: number;
39
+ }
36
40
  export interface PortalOpener {
37
41
  /** Open now and schedule renewal before the admission expires. */
38
42
  start(options?: PortalStartOptions): Promise<void>;
39
43
  /** Start a fetch at the exact ACK URL. Same-origin redirects follow only when allowed. */
40
44
  fetch(init?: RequestInit | PortalRequestBuilder, options?: PortalFetchOptions): Promise<Response>;
45
+ /**
46
+ * Start a fetch below the ACK URL. Each caller value is one raw path segment;
47
+ * the opener validates and escapes each segment before use. The ACK target's
48
+ * query is preserved.
49
+ */
50
+ fetchDescendant(pathSegments: readonly string[], init?: RequestInit | PortalRequestBuilder, options?: PortalFetchOptions): Promise<Response>;
41
51
  /** Read local session state. This method does no I/O and exposes no capability. */
42
- health(): PortalSessionHealth;
52
+ health(): PortalOpenerHealth;
43
53
  /** Stop renewal and wipe mutable private-key, visitor-secret, and token buffers. */
44
54
  close(): Promise<void>;
45
55
  }
@@ -56,6 +66,27 @@ export declare class PortalDenyError extends Error {
56
66
  export declare class PortalStateError extends Error {
57
67
  constructor(message: string, options?: ErrorOptions);
58
68
  }
69
+ export declare class PortalOpenerNotStartedError extends PortalStateError {
70
+ constructor();
71
+ }
72
+ export declare class PortalOpenerNotReadyError extends PortalStateError {
73
+ constructor(options?: ErrorOptions);
74
+ }
75
+ export declare class PortalOpenerClosedError extends PortalStateError {
76
+ constructor(options?: ErrorOptions);
77
+ }
78
+ export declare class PortalOpenTimeoutError extends PortalStateError {
79
+ constructor();
80
+ }
81
+ export declare class PortalTargetChangedError extends PortalStateError {
82
+ constructor();
83
+ }
84
+ export declare class PortalRedirectError extends PortalStateError {
85
+ constructor(message?: string, options?: ErrorOptions);
86
+ }
87
+ export declare class PortalTooManyRedirectsError extends PortalStateError {
88
+ constructor();
89
+ }
59
90
  export declare class PortalBusyError extends PortalStateError {
60
91
  constructor();
61
92
  }
@@ -66,7 +97,7 @@ interface PortalRuntime {
66
97
  readonly knock: (cell: ValidatedCell, key: Uint8Array, body: Uint8Array, options: NativeExchangeOptions) => Promise<NHPMessage>;
67
98
  readonly fetch: typeof globalThis.fetch;
68
99
  readonly nowNanos: () => bigint;
69
- readonly randomFraction: () => number;
100
+ readonly nowEpochMs: () => number;
70
101
  readonly setTimer: (callback: () => void, delayMs: number) => NodeJS.Timeout;
71
102
  readonly clearTimer: (timer: NodeJS.Timeout) => void;
72
103
  readonly setDeadlineTimer: (callback: () => void, delayMs: number) => NodeJS.Timeout;
@@ -74,10 +105,10 @@ interface PortalRuntime {
74
105
  }
75
106
  export declare function createPortalOpener(options: CreatePortalOpenerOptions): PortalOpener;
76
107
  export declare function createPortalOpenerWithRuntime(options: CreatePortalOpenerOptions, runtime: PortalRuntime): PortalOpener;
77
- declare function constructVerifiedPortalOpener(options: CreatePortalOpenerOptions, link: VerifiedQv2Link, deployment: ValidatedDeployment, runtime: PortalRuntime): PortalOpener;
108
+ declare function validatedCellForLink(link: VerifiedQv2Link, deployment: ValidatedDeployment): ValidatedCell;
78
109
  declare function validateSessionToken(value: string, decode?: (part: string) => Buffer): void;
79
110
  export declare const portalOpenerTesting: {
80
- constructVerifiedPortalOpener: typeof constructVerifiedPortalOpener;
111
+ validatedCellForLink: typeof validatedCellForLink;
81
112
  validateSessionToken: typeof validateSessionToken;
82
113
  };
83
114
  export {};