@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 +8 -0
- package/dist/auto-events/index.d.mts +1 -1
- package/dist/auto-events/index.d.ts +1 -1
- package/dist/{crossdeck-server-CuTGwrwx.d.mts → crossdeck-server-NL1p6hlR.d.mts} +44 -1
- package/dist/{crossdeck-server-CuTGwrwx.d.ts → crossdeck-server-NL1p6hlR.d.ts} +44 -1
- package/dist/index.cjs +43 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +43 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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.)
|
|
@@ -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
|
|
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
|
|
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.
|
|
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
|