@cross-deck/node 1.12.1 → 1.14.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/CHANGELOG.md CHANGED
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.14.0] — 2026-07-27
10
+
11
+ - **`campaignLink()` — stamp an outbound email link so its click binds to the recipient's identity on landing.** You know the recipient's email at send time; pass it with the link's destination and Crossdeck returns the same URL carrying a one-time arrival ticket (`cd_ref`). Put the returned URL in the email (Resend, HubSpot, your own SMTP — any tool). When the recipient clicks and lands on a page running `@cross-deck/web`, the SDK reads the ticket automatically (v1.10.0+) and binds that session to the person, so the post-click journey joins their identity. Server-side only — it mints an identity bind, so it requires a secret key. Degrades honestly: without the web SDK on the landing page the deep session stitch softens, but the server-side email funnel still lands by identity. Wraps `POST /v1/campaign/email-link`.
12
+
13
+ ## [1.13.0] — 2026-07-26
14
+
15
+ - **Crossdeck Trust — the gate now carries the browser's human-proof token.** `GateInput` gains an optional `token`: pass the attestation minted by the Crossdeck Trust panel in the browser (`Crossdeck.trust.panel(...)` / `<CrossdeckTrust>` in `@cross-deck/web`) and `gate()` forwards it as `challengeToken` to `/v1/trust/gate`, completing the SDK-native round trip. A real browser carries one; a server-to-server bot has none — that absence is scored, and in strict mode (`requireHumanProof`) a tokenless + suspicious attempt is the confident block. Optional and fail-open: omitting it never blocks on its own, and `gate()` remains fail-open by contract (a glitch returns `{ action: "allow", degraded: true }`, never a throw or a false block).
16
+
9
17
  ## [1.12.1] — 2026-07-23
10
18
 
11
19
  - **Reliability telemetry re-pointed to the live project.** The internal `crossdeck.contract_failed` self-check channel used a retired reliability-workspace key that had begun returning `401 invalid_api_key`, silently dropping failures; it now targets the live "Crossdeck Health Check" project. Internal SDK-health only — never touches your dashboard or your app. (No API changes; the anonymous-`reset()` hardening in the other SDKs does not apply — the Node SDK has no persistent device identity to churn.)
@@ -1,4 +1,4 @@
1
- import { x as CrossdeckServer } from '../crossdeck-server-CuTGwrwx.mjs';
1
+ import { z as CrossdeckServer } from '../crossdeck-server-NL1p6hlR.mjs';
2
2
  import 'node:events';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { x as CrossdeckServer } from '../crossdeck-server-CuTGwrwx.js';
1
+ import { z as CrossdeckServer } from '../crossdeck-server-NL1p6hlR.js';
2
2
  import 'node:events';
3
3
 
4
4
  /**
@@ -549,6 +549,15 @@ interface GateInput {
549
549
  domain?: string;
550
550
  /** Optional device fingerprint — sharpens the composite signup score. */
551
551
  fingerprint?: string;
552
+ /**
553
+ * The human-proof attestation minted by the Crossdeck Trust panel in the browser
554
+ * (`Crossdeck.trust.panel(...)` / `<CrossdeckTrust>` in @cross-deck/web). Forward
555
+ * whatever the client delivered. A real browser carries one; a server-to-server
556
+ * bot has none — that absence is scored, and in strict mode (`requireHumanProof`)
557
+ * a tokenless + suspicious attempt is the confident block. Optional and fail-open:
558
+ * omitting it never blocks on its own.
559
+ */
560
+ token?: string;
552
561
  }
553
562
  /**
554
563
  * The signup-gate verdict from {@link CrossdeckServer.gate}. Fail-open: on any error these
@@ -645,6 +654,25 @@ interface AliasResult {
645
654
  mergePending: boolean;
646
655
  env: Environment;
647
656
  }
657
+ /** Input to `campaignLink` — stamp an outbound email link with an identity-bind ticket. */
658
+ interface CampaignLinkInput {
659
+ /** The recipient's email address. The person is resolved from it once, at stamp time.
660
+ * Rides the request body only — never a URL/query (it is PII). */
661
+ email: string;
662
+ /** The absolute http(s) destination the recipient should land on. */
663
+ url: string;
664
+ /** Optional provenance for later enrichment (e.g. "resend", "hubspot"). Never required. */
665
+ rail?: string;
666
+ }
667
+ /** Result of `campaignLink` — the same destination with the arrival ticket appended. */
668
+ interface CampaignLinkResult {
669
+ object: "campaign_link";
670
+ /** The stamped URL to put in the email. Carries only an opaque `cd_ref` ticket. */
671
+ url: string;
672
+ /** How long the ticket stays live (a campaign's useful life). */
673
+ expiresInDays: number;
674
+ env: Environment;
675
+ }
648
676
  interface IngestResponse {
649
677
  object: "list";
650
678
  received: number;
@@ -1569,6 +1597,21 @@ declare class CrossdeckServer extends EventEmitter {
1569
1597
  identify(userId: string, anonymousId: string, options?: IdentifyOptions & RequestOptions): Promise<AliasResult>;
1570
1598
  aliasIdentity(input: AliasIdentityInput, options?: RequestOptions): Promise<AliasResult>;
1571
1599
  forget(hints: IdentityHints, options?: RequestOptions): Promise<ForgetResult>;
1600
+ /**
1601
+ * Stamp an outbound email link so its click binds to the recipient on landing.
1602
+ *
1603
+ * You know the recipient's email at send time; pass it with the link's destination
1604
+ * and Crossdeck returns the same URL carrying a one-time arrival ticket (`cd_ref`).
1605
+ * Put the returned URL in the email. When the recipient clicks and lands on a page
1606
+ * running `@cross-deck/web`, the SDK reads the ticket automatically and binds that
1607
+ * session to the person — the post-click journey then joins their identity.
1608
+ *
1609
+ * Rail-agnostic: works for Resend, HubSpot, your own SMTP, anything. Server-side
1610
+ * only (it mints an identity bind, so a secret key is required). If the landing page
1611
+ * has no Crossdeck web SDK, the deep session stitch softens but the server-side
1612
+ * email funnel (via the rail adapter) still lands by identity.
1613
+ */
1614
+ campaignLink(input: CampaignLinkInput, options?: RequestOptions): Promise<CampaignLinkResult>;
1572
1615
  /**
1573
1616
  * Fetch a customer's entitlements from Crossdeck and warm the cache.
1574
1617
  *
@@ -2119,4 +2162,4 @@ declare class CrossdeckServer extends EventEmitter {
2119
2162
  private normalizeIngestEvent;
2120
2163
  }
2121
2164
 
2122
- export { type IngestOptions as $, type AliasIdentityInput as A, type BlockVerdict as B, CROSSDECK_API_VERSION as C, DEFAULT_BASE_URL as D, DEFAULT_TIMEOUT_MS as E, type Diagnostics as F, type EntitlementCacheOptions as G, type EntitlementMutationResult as H, type EntitlementStore as I, type EntitlementsListResponse as J, type EntitlementsListener as K, type Environment as L, type ErrorCaptureConfig as M, type ErrorLevel as N, type EventProperties as O, type ForgetResult as P, type GateInput as Q, type GateVerdict as R, type GrantDuration as S, type GrantEntitlementInput as T, type GroupMembership as U, type HeartbeatResponse as V, type HttpRequestInfo as W, type HttpResponseInfo as X, type HttpRetriesConfig as Y, type IdentifyOptions as Z, type IdentityHints as _, type AliasResult as a, type IngestResponse as a0, type OwnerStatusInput as a1, type PublicEntitlement as a2, type PurchaseResult as a3, type RequestOptions as a4, type ResolveInput as a5, type ResolveResult as a6, type RevokeEntitlementInput as a7, type RuntimeHost as a8, type RuntimeInfo as a9, type ServerEvent as aa, type StackFrame as ab, type StoredEntitlements as ac, type SyncPurchaseInput as ad, makeCrossdeckError as ae, type AuditDecision as b, type AuditEntry as c, type Breadcrumb as d, type BreadcrumbCategory as e, type BreadcrumbLevel as f, type CapturedError as g, type Contract as h, type ContractAppliesTo as i, type ContractFailureInput as j, type ContractPillar as k, type ContractStatus as l, type ContractTestRef as m, CrossdeckAuthenticationError as n, CrossdeckConfigurationError as o, CrossdeckContracts as p, CrossdeckError as q, type CrossdeckErrorPayload as r, type CrossdeckErrorType as s, CrossdeckInternalError as t, CrossdeckNetworkError as u, CrossdeckPermissionError as v, CrossdeckRateLimitError as w, CrossdeckServer as x, type CrossdeckServerOptions as y, CrossdeckValidationError as z };
2165
+ export { type IdentifyOptions as $, type AliasIdentityInput as A, type BlockVerdict as B, CROSSDECK_API_VERSION as C, type CrossdeckServerOptions as D, CrossdeckValidationError as E, DEFAULT_BASE_URL as F, DEFAULT_TIMEOUT_MS as G, type Diagnostics as H, type EntitlementCacheOptions as I, type EntitlementMutationResult as J, type EntitlementStore as K, type EntitlementsListResponse as L, type EntitlementsListener as M, type Environment as N, type ErrorCaptureConfig as O, type ErrorLevel as P, type EventProperties as Q, type ForgetResult as R, type GateInput as S, type GateVerdict as T, type GrantDuration as U, type GrantEntitlementInput as V, type GroupMembership as W, type HeartbeatResponse as X, type HttpRequestInfo as Y, type HttpResponseInfo as Z, type HttpRetriesConfig as _, type AliasResult as a, type IdentityHints as a0, type IngestOptions as a1, type IngestResponse as a2, type OwnerStatusInput as a3, type PublicEntitlement as a4, type PurchaseResult as a5, type RequestOptions as a6, type ResolveInput as a7, type ResolveResult as a8, type RevokeEntitlementInput as a9, type RuntimeHost as aa, type RuntimeInfo as ab, type ServerEvent as ac, type StackFrame as ad, type StoredEntitlements as ae, type SyncPurchaseInput as af, makeCrossdeckError as ag, type AuditDecision as b, type AuditEntry as c, type Breadcrumb as d, type BreadcrumbCategory as e, type BreadcrumbLevel as f, type CampaignLinkInput as g, type CampaignLinkResult as h, type CapturedError as i, type Contract as j, type ContractAppliesTo as k, type ContractFailureInput as l, type ContractPillar as m, type ContractStatus as n, type ContractTestRef as o, CrossdeckAuthenticationError as p, CrossdeckConfigurationError as q, CrossdeckContracts as r, CrossdeckError as s, type CrossdeckErrorPayload as t, type CrossdeckErrorType as u, CrossdeckInternalError as v, CrossdeckNetworkError as w, CrossdeckPermissionError as x, CrossdeckRateLimitError as y, CrossdeckServer as z };
@@ -549,6 +549,15 @@ interface GateInput {
549
549
  domain?: string;
550
550
  /** Optional device fingerprint — sharpens the composite signup score. */
551
551
  fingerprint?: string;
552
+ /**
553
+ * The human-proof attestation minted by the Crossdeck Trust panel in the browser
554
+ * (`Crossdeck.trust.panel(...)` / `<CrossdeckTrust>` in @cross-deck/web). Forward
555
+ * whatever the client delivered. A real browser carries one; a server-to-server
556
+ * bot has none — that absence is scored, and in strict mode (`requireHumanProof`)
557
+ * a tokenless + suspicious attempt is the confident block. Optional and fail-open:
558
+ * omitting it never blocks on its own.
559
+ */
560
+ token?: string;
552
561
  }
553
562
  /**
554
563
  * The signup-gate verdict from {@link CrossdeckServer.gate}. Fail-open: on any error these
@@ -645,6 +654,25 @@ interface AliasResult {
645
654
  mergePending: boolean;
646
655
  env: Environment;
647
656
  }
657
+ /** Input to `campaignLink` — stamp an outbound email link with an identity-bind ticket. */
658
+ interface CampaignLinkInput {
659
+ /** The recipient's email address. The person is resolved from it once, at stamp time.
660
+ * Rides the request body only — never a URL/query (it is PII). */
661
+ email: string;
662
+ /** The absolute http(s) destination the recipient should land on. */
663
+ url: string;
664
+ /** Optional provenance for later enrichment (e.g. "resend", "hubspot"). Never required. */
665
+ rail?: string;
666
+ }
667
+ /** Result of `campaignLink` — the same destination with the arrival ticket appended. */
668
+ interface CampaignLinkResult {
669
+ object: "campaign_link";
670
+ /** The stamped URL to put in the email. Carries only an opaque `cd_ref` ticket. */
671
+ url: string;
672
+ /** How long the ticket stays live (a campaign's useful life). */
673
+ expiresInDays: number;
674
+ env: Environment;
675
+ }
648
676
  interface IngestResponse {
649
677
  object: "list";
650
678
  received: number;
@@ -1569,6 +1597,21 @@ declare class CrossdeckServer extends EventEmitter {
1569
1597
  identify(userId: string, anonymousId: string, options?: IdentifyOptions & RequestOptions): Promise<AliasResult>;
1570
1598
  aliasIdentity(input: AliasIdentityInput, options?: RequestOptions): Promise<AliasResult>;
1571
1599
  forget(hints: IdentityHints, options?: RequestOptions): Promise<ForgetResult>;
1600
+ /**
1601
+ * Stamp an outbound email link so its click binds to the recipient on landing.
1602
+ *
1603
+ * You know the recipient's email at send time; pass it with the link's destination
1604
+ * and Crossdeck returns the same URL carrying a one-time arrival ticket (`cd_ref`).
1605
+ * Put the returned URL in the email. When the recipient clicks and lands on a page
1606
+ * running `@cross-deck/web`, the SDK reads the ticket automatically and binds that
1607
+ * session to the person — the post-click journey then joins their identity.
1608
+ *
1609
+ * Rail-agnostic: works for Resend, HubSpot, your own SMTP, anything. Server-side
1610
+ * only (it mints an identity bind, so a secret key is required). If the landing page
1611
+ * has no Crossdeck web SDK, the deep session stitch softens but the server-side
1612
+ * email funnel (via the rail adapter) still lands by identity.
1613
+ */
1614
+ campaignLink(input: CampaignLinkInput, options?: RequestOptions): Promise<CampaignLinkResult>;
1572
1615
  /**
1573
1616
  * Fetch a customer's entitlements from Crossdeck and warm the cache.
1574
1617
  *
@@ -2119,4 +2162,4 @@ declare class CrossdeckServer extends EventEmitter {
2119
2162
  private normalizeIngestEvent;
2120
2163
  }
2121
2164
 
2122
- export { type IngestOptions as $, type AliasIdentityInput as A, type BlockVerdict as B, CROSSDECK_API_VERSION as C, DEFAULT_BASE_URL as D, DEFAULT_TIMEOUT_MS as E, type Diagnostics as F, type EntitlementCacheOptions as G, type EntitlementMutationResult as H, type EntitlementStore as I, type EntitlementsListResponse as J, type EntitlementsListener as K, type Environment as L, type ErrorCaptureConfig as M, type ErrorLevel as N, type EventProperties as O, type ForgetResult as P, type GateInput as Q, type GateVerdict as R, type GrantDuration as S, type GrantEntitlementInput as T, type GroupMembership as U, type HeartbeatResponse as V, type HttpRequestInfo as W, type HttpResponseInfo as X, type HttpRetriesConfig as Y, type IdentifyOptions as Z, type IdentityHints as _, type AliasResult as a, type IngestResponse as a0, type OwnerStatusInput as a1, type PublicEntitlement as a2, type PurchaseResult as a3, type RequestOptions as a4, type ResolveInput as a5, type ResolveResult as a6, type RevokeEntitlementInput as a7, type RuntimeHost as a8, type RuntimeInfo as a9, type ServerEvent as aa, type StackFrame as ab, type StoredEntitlements as ac, type SyncPurchaseInput as ad, makeCrossdeckError as ae, type AuditDecision as b, type AuditEntry as c, type Breadcrumb as d, type BreadcrumbCategory as e, type BreadcrumbLevel as f, type CapturedError as g, type Contract as h, type ContractAppliesTo as i, type ContractFailureInput as j, type ContractPillar as k, type ContractStatus as l, type ContractTestRef as m, CrossdeckAuthenticationError as n, CrossdeckConfigurationError as o, CrossdeckContracts as p, CrossdeckError as q, type CrossdeckErrorPayload as r, type CrossdeckErrorType as s, CrossdeckInternalError as t, CrossdeckNetworkError as u, CrossdeckPermissionError as v, CrossdeckRateLimitError as w, CrossdeckServer as x, type CrossdeckServerOptions as y, CrossdeckValidationError as z };
2165
+ export { type IdentifyOptions as $, type AliasIdentityInput as A, type BlockVerdict as B, CROSSDECK_API_VERSION as C, type CrossdeckServerOptions as D, CrossdeckValidationError as E, DEFAULT_BASE_URL as F, DEFAULT_TIMEOUT_MS as G, type Diagnostics as H, type EntitlementCacheOptions as I, type EntitlementMutationResult as J, type EntitlementStore as K, type EntitlementsListResponse as L, type EntitlementsListener as M, type Environment as N, type ErrorCaptureConfig as O, type ErrorLevel as P, type EventProperties as Q, type ForgetResult as R, type GateInput as S, type GateVerdict as T, type GrantDuration as U, type GrantEntitlementInput as V, type GroupMembership as W, type HeartbeatResponse as X, type HttpRequestInfo as Y, type HttpResponseInfo as Z, type HttpRetriesConfig as _, type AliasResult as a, type IdentityHints as a0, type IngestOptions as a1, type IngestResponse as a2, type OwnerStatusInput as a3, type PublicEntitlement as a4, type PurchaseResult as a5, type RequestOptions as a6, type ResolveInput as a7, type ResolveResult as a8, type RevokeEntitlementInput as a9, type RuntimeHost as aa, type RuntimeInfo as ab, type ServerEvent as ac, type StackFrame as ad, type StoredEntitlements as ae, type SyncPurchaseInput as af, makeCrossdeckError as ag, type AuditDecision as b, type AuditEntry as c, type Breadcrumb as d, type BreadcrumbCategory as e, type BreadcrumbLevel as f, type CampaignLinkInput as g, type CampaignLinkResult as h, type CapturedError as i, type Contract as j, type ContractAppliesTo as k, type ContractFailureInput as l, type ContractPillar as m, type ContractStatus as n, type ContractTestRef as o, CrossdeckAuthenticationError as p, CrossdeckConfigurationError as q, CrossdeckContracts as r, CrossdeckError as s, type CrossdeckErrorPayload as t, type CrossdeckErrorType as u, CrossdeckInternalError as v, CrossdeckNetworkError as w, CrossdeckPermissionError as x, CrossdeckRateLimitError as y, CrossdeckServer as z };
package/dist/index.cjs CHANGED
@@ -387,7 +387,7 @@ function byteLength(s) {
387
387
  var https = __toESM(require("https"));
388
388
 
389
389
  // src/_version.ts
390
- var SDK_VERSION = "1.12.1";
390
+ var SDK_VERSION = "1.14.0";
391
391
  var SDK_NAME = "@cross-deck/node";
392
392
 
393
393
  // src/_diagnostic-telemetry.ts
@@ -3048,6 +3048,43 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
3048
3048
  timeoutMs: options?.timeoutMs
3049
3049
  });
3050
3050
  }
3051
+ /**
3052
+ * Stamp an outbound email link so its click binds to the recipient on landing.
3053
+ *
3054
+ * You know the recipient's email at send time; pass it with the link's destination
3055
+ * and Crossdeck returns the same URL carrying a one-time arrival ticket (`cd_ref`).
3056
+ * Put the returned URL in the email. When the recipient clicks and lands on a page
3057
+ * running `@cross-deck/web`, the SDK reads the ticket automatically and binds that
3058
+ * session to the person — the post-click journey then joins their identity.
3059
+ *
3060
+ * Rail-agnostic: works for Resend, HubSpot, your own SMTP, anything. Server-side
3061
+ * only (it mints an identity bind, so a secret key is required). If the landing page
3062
+ * has no Crossdeck web SDK, the deep session stitch softens but the server-side
3063
+ * email funnel (via the rail adapter) still lands by identity.
3064
+ */
3065
+ async campaignLink(input, options) {
3066
+ if (!input.email) {
3067
+ throw new CrossdeckError({
3068
+ type: "invalid_request_error",
3069
+ code: "missing_email",
3070
+ message: "campaignLink requires the recipient's email."
3071
+ });
3072
+ }
3073
+ if (!input.url) {
3074
+ throw new CrossdeckError({
3075
+ type: "invalid_request_error",
3076
+ code: "missing_url",
3077
+ message: "campaignLink requires the link's destination url."
3078
+ });
3079
+ }
3080
+ const body = { email: input.email, url: input.url };
3081
+ if (input.rail) body.rail = input.rail;
3082
+ return this.http.request("POST", "/campaign/email-link", {
3083
+ body,
3084
+ signal: options?.signal,
3085
+ timeoutMs: options?.timeoutMs
3086
+ });
3087
+ }
3051
3088
  // ============================================================
3052
3089
  // Entitlements — direct HTTP + TTL cache (v1.0.0+)
3053
3090
  //
@@ -3309,7 +3346,11 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
3309
3346
  ...input.email ? { email: input.email } : {},
3310
3347
  ...input.ip ? { ip: input.ip } : {},
3311
3348
  ...input.domain ? { domain: input.domain } : {},
3312
- ...input.fingerprint ? { fingerprint: input.fingerprint } : {}
3349
+ ...input.fingerprint ? { fingerprint: input.fingerprint } : {},
3350
+ // The human-proof attestation from the Crossdeck Trust browser panel.
3351
+ // Verified server-side at the gate; its absence is scored, never a hard
3352
+ // block on its own (fail-open). Only sent when the client supplied one.
3353
+ ...input.token ? { challengeToken: input.token } : {}
3313
3354
  },
3314
3355
  signal: options?.signal,
3315
3356
  timeoutMs: options?.timeoutMs