@cross-deck/node 1.10.0 → 1.11.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,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.11.0] — 2026-06-30
10
+
11
+ **Added — block `reference` and `supportEmail` on the trust surface (still v2 preview).**
12
+ `resolve()` and `gate()` now surface two fields on a block:
13
+
14
+ - `reference` — a short, opaque, human-quotable support handle (the "Ray ID", `xxxx-xxxx`).
15
+ Show it on your suspended screen; Crossdeck stores the same string on the block event, so
16
+ support resolves a quoted reference to the exact block in the admin Caught feed.
17
+ - `supportEmail` — the email you registered with Crossdeck at signup, so your suspended
18
+ screen's "Contact support" reaches the real operator by default instead of a placeholder.
19
+
20
+ Both were typed on `ResolveResult` / `GateVerdict` but the client previously **dropped**
21
+ them when building the result object — now passed through. Additive, non-breaking. Opaque +
22
+ non-PII (never the score or rule logic).
23
+
24
+ ## [1.10.1] — 2026-06-30
25
+
26
+ **Fixed — the blocking surface now works for the documented paths (still v2 preview).**
27
+
28
+ - `resolve()` / `isBlocked()` now accept a **bare `userId`** — the server-trusted
29
+ recognition path the backend honours (rides the identity backbone, matches on the email
30
+ Crossdeck already holds). 1.10.0's guard rejected it and fail-opened *without calling the
31
+ API*, so a known-user block silently never fired.
32
+ - **Added `gate({ email, ip })`** — the brand-new-signup door (`POST /v1/trust/gate`), the
33
+ only path that catches a farm Crossdeck has never seen. Fail-open by contract. New
34
+ `GateInput` / `GateVerdict` types.
35
+ - README now documents the blocking surface (the three doors + the verify-locally pattern).
36
+
37
+ Still `@experimental` — Crossdeck Trust ships with Crossdeck v2.
38
+
39
+
9
40
  ## [1.10.0] — 2026-06-30
10
41
 
11
42
  **Added — the blocking surface (Crossdeck Trust, v2 preview).** Blocking is
package/README.md CHANGED
@@ -211,6 +211,51 @@ app.post("/crossdeck-webhook", express.raw({ type: "application/json" }), (req,
211
211
 
212
212
  For test fixtures that need to mint signed webhooks against the same scheme, `signWebhookPayload(payload, secret, timestampSec)` is exported.
213
213
 
214
+ ### Blocking (Crossdeck Trust) — <sup>v2 preview</sup>
215
+
216
+ > **Preview — not GA.** The blocking surface launches **end-to-end with Crossdeck v2.**
217
+ > The shape is stable and all methods are `@experimental`, so it's documented and usable
218
+ > now (dogfooding / early access) — but it is **not** a launched feature yet, and this is
219
+ > not a fourth shipped USP. Treat it as preview until the v2 launch.
220
+
221
+ Blocking is **entitlements, inverted**: `getEntitlements()` asks *"can this user access Pro?"*; these ask *"should this user be here at all?"* — the **same identity backbone**, read for `blocked`. Every method is **fail-open by contract**: it never throws and never returns blocked on uncertainty, so a glitch can never lock out a real user.
222
+
223
+ There are three doors. Pick by who's at it:
224
+
225
+ ```ts
226
+ // 1. A user you ALREADY KNOW — a bare userId rides the backbone (no token, no setup).
227
+ // Crossdeck matches on the email it already holds. The common case.
228
+ const { blocked, blockReason } = await crossdeck.resolve({ userId, ip: req.ip });
229
+ if (blocked) { await signOut(); return showSuspended(blockReason); }
230
+ // or just the boolean: if (await crossdeck.isBlocked({ userId })) …
231
+
232
+ // 2. A BRAND-NEW signup Crossdeck has never seen — block by the two strings you have
233
+ // at signup, their email + ip. Call it BEFORE you create the account.
234
+ const gate = await crossdeck.gate({ email, ip: req.ip });
235
+ if (gate.action === "block") return rejectSignup(gate.blockReason);
236
+
237
+ // 3. A PUBLIC PAGE — "is the owner of this page blocked?" (no session token). Poll on a
238
+ // short TTL to invalidate your cache; the ip is the owner's CREATION ip.
239
+ const owner = await crossdeck.getOwnerStatus({ userId: ownerId });
240
+ if (owner.blocked) await takePageOffline(ownerId);
241
+ ```
242
+
243
+ **Recommended server pattern (no issuer registration):** if your backend runs Firebase
244
+ Admin, verify the ID token **locally** for the authoritative uid, then send that as a bare
245
+ `userId` — you keep the cryptographic proof on your side and Crossdeck needs zero setup:
246
+
247
+ ```ts
248
+ const { uid } = await getAuth().verifyIdToken(idToken); // your Firebase Admin
249
+ const { blocked } = await crossdeck.resolve({ userId: uid, ip: req.ip });
250
+ ```
251
+
252
+ Send `userId` + `idToken` to Crossdeck instead only if you *can't* verify locally — that's
253
+ the verified Tier-3 path, and the only one that needs a one-time auth-issuer registration.
254
+ Setting *who* is blocked is a rule in your Crossdeck admin. Full integration guide
255
+ (suspension pages, public-content takedown, the backfill sweep): `CROSSDECK_BLOCKING_DEVELOPER_GUIDE.md`.
256
+
257
+ Types: `ResolveInput` / `ResolveResult`, `GateInput` / `GateVerdict`, `OwnerStatusInput` / `BlockVerdict`.
258
+
214
259
  ## Cross-cutting
215
260
 
216
261
  ### Read-cost cross-match (Buckets)
@@ -1,4 +1,4 @@
1
- import { x as CrossdeckServer } from '../crossdeck-server-C4nWPQzH.mjs';
1
+ import { x as CrossdeckServer } from '../crossdeck-server-DAQX_7rm.mjs';
2
2
  import 'node:events';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { x as CrossdeckServer } from '../crossdeck-server-C4nWPQzH.js';
1
+ import { x as CrossdeckServer } from '../crossdeck-server-DAQX_7rm.js';
2
2
  import 'node:events';
3
3
 
4
4
  /**
@@ -183,8 +183,8 @@ declare const CrossdeckContracts: {
183
183
  readonly byId: (id: string) => Contract | undefined;
184
184
  readonly byPillar: (pillar: ContractPillar) => readonly Contract[];
185
185
  readonly withStatus: (status: ContractStatus) => readonly Contract[];
186
- readonly sdkVersion: "1.10.0";
187
- readonly bundledIn: "@cross-deck/node@1.10.0";
186
+ readonly sdkVersion: "1.11.0";
187
+ readonly bundledIn: "@cross-deck/node@1.11.0";
188
188
  /**
189
189
  * Resolve a failing test back to the contract it exercises.
190
190
  * Used by test-framework hooks to find the contract id of a
@@ -472,6 +472,18 @@ interface BlockVerdict {
472
472
  blockReason: string | null;
473
473
  /** The exact rule that fired: `"domain:spartan.net"` | `"ip:1.2.3.4"` | `"manual"` | null. */
474
474
  blockedKey: string | null;
475
+ /**
476
+ * Human-quotable support handle for this verdict (the "Ray ID"), formatted `xxxx-xxxx`.
477
+ * Show it on your suspended screen ("reference: blocklist:domain · a1f9-7c2e"); Crossdeck
478
+ * stores the same string on the block event, so support resolves it in the Caught feed.
479
+ */
480
+ reference?: string | null;
481
+ /**
482
+ * The recorded support contact (the project owner's signup email), present only when
483
+ * blocked — render it as the suspended screen's "Contact support" so appeals reach the
484
+ * real operator by default. Override with a dedicated address if you have one.
485
+ */
486
+ supportEmail?: string | null;
475
487
  /**
476
488
  * True when this is the fail-open default (Crossdeck unreachable / identity unresolved).
477
489
  * `blocked` is `false`; log it for observability, never treat it as a real "not blocked".
@@ -514,6 +526,45 @@ interface OwnerStatusInput {
514
526
  domain?: string;
515
527
  ip?: string;
516
528
  }
529
+ /**
530
+ * Input for {@link CrossdeckServer.gate} — the brand-new-signup door. Pass the two strings
531
+ * you have at signup: `email` and `ip`. (Optional `domain`/`fingerprint` sharpen the score.)
532
+ *
533
+ * @experimental Preview — see {@link ResolveInput}.
534
+ */
535
+ interface GateInput {
536
+ /** The signing-up user's email. Matched against email + domain blocklist rules. */
537
+ email?: string;
538
+ /** The signing-up request's IP. Matched against ip rules + datacenter/velocity signals. */
539
+ ip?: string;
540
+ /** Optional explicit domain (defaults to the email's domain). */
541
+ domain?: string;
542
+ /** Optional device fingerprint — sharpens the composite signup score. */
543
+ fingerprint?: string;
544
+ }
545
+ /**
546
+ * The signup-gate verdict from {@link CrossdeckServer.gate}. Fail-open: on any error these
547
+ * come back `{ action: "allow", allow: true, degraded: true }` — a glitch never rejects a
548
+ * real signup.
549
+ *
550
+ * @experimental Preview — see {@link ResolveInput}.
551
+ */
552
+ interface GateVerdict {
553
+ /** `"allow"` | `"review"` | `"block"`. Reject the account only on `"block"`. */
554
+ action: "allow" | "review" | "block";
555
+ /** Convenience: `false` only when `action` is `"block"`. */
556
+ allow: boolean;
557
+ /** Why, when blocked: e.g. `"disposable_domain"` | `"blocklist:ip"` | `"blocklist:domain"`. */
558
+ blockReason: string | null;
559
+ /** The exact rule/key that fired, when blocked. */
560
+ blockedKey: string | null;
561
+ /** Human-quotable support handle (the "Ray ID"), formatted `xxxx-xxxx` — show it on the blocked screen. */
562
+ reference?: string | null;
563
+ /** Recorded support contact (project owner's signup email), present only on `block` — the blocked screen's "Contact support" default. */
564
+ supportEmail?: string | null;
565
+ /** True when this is the fail-open default (Crossdeck unreachable). `allow` is `true`. */
566
+ degraded?: boolean;
567
+ }
517
568
  /**
518
569
  * Snapshot of one customer's last-known-good entitlements, as written
519
570
  * to / read from a durable `EntitlementStore`. Versioned for forward-
@@ -1566,6 +1617,19 @@ declare class CrossdeckServer extends EventEmitter {
1566
1617
  * @experimental Preview — see {@link resolve}.
1567
1618
  */
1568
1619
  getOwnerStatus(input: OwnerStatusInput, options?: RequestOptions): Promise<BlockVerdict>;
1620
+ /**
1621
+ * The brand-new-signup door (§3 of the Blocking developer guide). Block a user Crossdeck
1622
+ * has **never seen** — at the instant they sign up — by the two strings you have then:
1623
+ * their `email` and `ip`. There's no identity to ride yet, so {@link resolve} can't catch
1624
+ * them; this matches `email` / domain / `ip` blocklist rules directly. Call it BEFORE you
1625
+ * create the account; on `action: "block"`, reject the signup.
1626
+ *
1627
+ * **Fail-open by contract** — never throws, never blocks on uncertainty: any error →
1628
+ * `{ action: "allow", allow: true, degraded: true }`, so a glitch can't reject a real signup.
1629
+ *
1630
+ * @experimental Preview — Crossdeck Trust ships with Crossdeck v2.
1631
+ */
1632
+ gate(input: GateInput, options?: RequestOptions): Promise<GateVerdict>;
1569
1633
  /**
1570
1634
  * Synchronous entitlement check. Returns `true` iff the customer
1571
1635
  * has the entitlement AND the cache entry is fresh (within
@@ -2045,4 +2109,4 @@ declare class CrossdeckServer extends EventEmitter {
2045
2109
  private normalizeIngestEvent;
2046
2110
  }
2047
2111
 
2048
- export { type OwnerStatusInput 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 GrantDuration as Q, type GrantEntitlementInput as R, type GroupMembership as S, type HeartbeatResponse as T, type HttpRequestInfo as U, type HttpResponseInfo as V, type HttpRetriesConfig as W, type IdentifyOptions as X, type IdentityHints as Y, type IngestOptions as Z, type IngestResponse as _, type AliasResult as a, type PublicEntitlement as a0, type PurchaseResult as a1, type RequestOptions as a2, type ResolveInput as a3, type ResolveResult as a4, type RevokeEntitlementInput as a5, type RuntimeHost as a6, type RuntimeInfo as a7, type ServerEvent as a8, type StackFrame as a9, type StoredEntitlements as aa, type SyncPurchaseInput as ab, makeCrossdeckError as ac, 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 };
2112
+ 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 };
@@ -183,8 +183,8 @@ declare const CrossdeckContracts: {
183
183
  readonly byId: (id: string) => Contract | undefined;
184
184
  readonly byPillar: (pillar: ContractPillar) => readonly Contract[];
185
185
  readonly withStatus: (status: ContractStatus) => readonly Contract[];
186
- readonly sdkVersion: "1.10.0";
187
- readonly bundledIn: "@cross-deck/node@1.10.0";
186
+ readonly sdkVersion: "1.11.0";
187
+ readonly bundledIn: "@cross-deck/node@1.11.0";
188
188
  /**
189
189
  * Resolve a failing test back to the contract it exercises.
190
190
  * Used by test-framework hooks to find the contract id of a
@@ -472,6 +472,18 @@ interface BlockVerdict {
472
472
  blockReason: string | null;
473
473
  /** The exact rule that fired: `"domain:spartan.net"` | `"ip:1.2.3.4"` | `"manual"` | null. */
474
474
  blockedKey: string | null;
475
+ /**
476
+ * Human-quotable support handle for this verdict (the "Ray ID"), formatted `xxxx-xxxx`.
477
+ * Show it on your suspended screen ("reference: blocklist:domain · a1f9-7c2e"); Crossdeck
478
+ * stores the same string on the block event, so support resolves it in the Caught feed.
479
+ */
480
+ reference?: string | null;
481
+ /**
482
+ * The recorded support contact (the project owner's signup email), present only when
483
+ * blocked — render it as the suspended screen's "Contact support" so appeals reach the
484
+ * real operator by default. Override with a dedicated address if you have one.
485
+ */
486
+ supportEmail?: string | null;
475
487
  /**
476
488
  * True when this is the fail-open default (Crossdeck unreachable / identity unresolved).
477
489
  * `blocked` is `false`; log it for observability, never treat it as a real "not blocked".
@@ -514,6 +526,45 @@ interface OwnerStatusInput {
514
526
  domain?: string;
515
527
  ip?: string;
516
528
  }
529
+ /**
530
+ * Input for {@link CrossdeckServer.gate} — the brand-new-signup door. Pass the two strings
531
+ * you have at signup: `email` and `ip`. (Optional `domain`/`fingerprint` sharpen the score.)
532
+ *
533
+ * @experimental Preview — see {@link ResolveInput}.
534
+ */
535
+ interface GateInput {
536
+ /** The signing-up user's email. Matched against email + domain blocklist rules. */
537
+ email?: string;
538
+ /** The signing-up request's IP. Matched against ip rules + datacenter/velocity signals. */
539
+ ip?: string;
540
+ /** Optional explicit domain (defaults to the email's domain). */
541
+ domain?: string;
542
+ /** Optional device fingerprint — sharpens the composite signup score. */
543
+ fingerprint?: string;
544
+ }
545
+ /**
546
+ * The signup-gate verdict from {@link CrossdeckServer.gate}. Fail-open: on any error these
547
+ * come back `{ action: "allow", allow: true, degraded: true }` — a glitch never rejects a
548
+ * real signup.
549
+ *
550
+ * @experimental Preview — see {@link ResolveInput}.
551
+ */
552
+ interface GateVerdict {
553
+ /** `"allow"` | `"review"` | `"block"`. Reject the account only on `"block"`. */
554
+ action: "allow" | "review" | "block";
555
+ /** Convenience: `false` only when `action` is `"block"`. */
556
+ allow: boolean;
557
+ /** Why, when blocked: e.g. `"disposable_domain"` | `"blocklist:ip"` | `"blocklist:domain"`. */
558
+ blockReason: string | null;
559
+ /** The exact rule/key that fired, when blocked. */
560
+ blockedKey: string | null;
561
+ /** Human-quotable support handle (the "Ray ID"), formatted `xxxx-xxxx` — show it on the blocked screen. */
562
+ reference?: string | null;
563
+ /** Recorded support contact (project owner's signup email), present only on `block` — the blocked screen's "Contact support" default. */
564
+ supportEmail?: string | null;
565
+ /** True when this is the fail-open default (Crossdeck unreachable). `allow` is `true`. */
566
+ degraded?: boolean;
567
+ }
517
568
  /**
518
569
  * Snapshot of one customer's last-known-good entitlements, as written
519
570
  * to / read from a durable `EntitlementStore`. Versioned for forward-
@@ -1566,6 +1617,19 @@ declare class CrossdeckServer extends EventEmitter {
1566
1617
  * @experimental Preview — see {@link resolve}.
1567
1618
  */
1568
1619
  getOwnerStatus(input: OwnerStatusInput, options?: RequestOptions): Promise<BlockVerdict>;
1620
+ /**
1621
+ * The brand-new-signup door (§3 of the Blocking developer guide). Block a user Crossdeck
1622
+ * has **never seen** — at the instant they sign up — by the two strings you have then:
1623
+ * their `email` and `ip`. There's no identity to ride yet, so {@link resolve} can't catch
1624
+ * them; this matches `email` / domain / `ip` blocklist rules directly. Call it BEFORE you
1625
+ * create the account; on `action: "block"`, reject the signup.
1626
+ *
1627
+ * **Fail-open by contract** — never throws, never blocks on uncertainty: any error →
1628
+ * `{ action: "allow", allow: true, degraded: true }`, so a glitch can't reject a real signup.
1629
+ *
1630
+ * @experimental Preview — Crossdeck Trust ships with Crossdeck v2.
1631
+ */
1632
+ gate(input: GateInput, options?: RequestOptions): Promise<GateVerdict>;
1569
1633
  /**
1570
1634
  * Synchronous entitlement check. Returns `true` iff the customer
1571
1635
  * has the entitlement AND the cache entry is fresh (within
@@ -2045,4 +2109,4 @@ declare class CrossdeckServer extends EventEmitter {
2045
2109
  private normalizeIngestEvent;
2046
2110
  }
2047
2111
 
2048
- export { type OwnerStatusInput 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 GrantDuration as Q, type GrantEntitlementInput as R, type GroupMembership as S, type HeartbeatResponse as T, type HttpRequestInfo as U, type HttpResponseInfo as V, type HttpRetriesConfig as W, type IdentifyOptions as X, type IdentityHints as Y, type IngestOptions as Z, type IngestResponse as _, type AliasResult as a, type PublicEntitlement as a0, type PurchaseResult as a1, type RequestOptions as a2, type ResolveInput as a3, type ResolveResult as a4, type RevokeEntitlementInput as a5, type RuntimeHost as a6, type RuntimeInfo as a7, type ServerEvent as a8, type StackFrame as a9, type StoredEntitlements as aa, type SyncPurchaseInput as ab, makeCrossdeckError as ac, 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 };
2112
+ 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 };
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.10.0";
390
+ var SDK_VERSION = "1.11.0";
391
391
  var SDK_NAME = "@cross-deck/node";
392
392
 
393
393
  // src/_diagnostic-telemetry.ts
@@ -3185,10 +3185,10 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
3185
3185
  anonymousId: input?.anonymousId ?? null,
3186
3186
  identityError: null
3187
3187
  });
3188
- if (!input || !input.anonymousId && !(input.userId && input.idToken)) {
3188
+ if (!input || !input.anonymousId && !input.userId) {
3189
3189
  this.debug.emit(
3190
3190
  "sdk.resolve_missing_identity",
3191
- "resolve() needs anonymousId OR (userId + idToken); returning fail-open blocked:false."
3191
+ "resolve() needs an anonymousId or a userId; returning fail-open blocked:false."
3192
3192
  );
3193
3193
  return failOpen();
3194
3194
  }
@@ -3207,6 +3207,8 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
3207
3207
  blocked: raw.blocked === true,
3208
3208
  blockReason: raw.blockReason ?? null,
3209
3209
  blockedKey: raw.blockedKey ?? null,
3210
+ reference: raw.reference ?? null,
3211
+ supportEmail: raw.supportEmail ?? null,
3210
3212
  status: typeof raw.status === "string" ? raw.status : "active",
3211
3213
  entitlements: Array.isArray(raw.entitlements) ? raw.entitlements.filter((e) => typeof e === "string") : [],
3212
3214
  crossdeckCustomerId: raw.crossdeckCustomerId ?? raw.user?.id ?? null,
@@ -3273,6 +3275,62 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
3273
3275
  return { blocked: false, blockReason: null, blockedKey: null, degraded: true };
3274
3276
  }
3275
3277
  }
3278
+ /**
3279
+ * The brand-new-signup door (§3 of the Blocking developer guide). Block a user Crossdeck
3280
+ * has **never seen** — at the instant they sign up — by the two strings you have then:
3281
+ * their `email` and `ip`. There's no identity to ride yet, so {@link resolve} can't catch
3282
+ * them; this matches `email` / domain / `ip` blocklist rules directly. Call it BEFORE you
3283
+ * create the account; on `action: "block"`, reject the signup.
3284
+ *
3285
+ * **Fail-open by contract** — never throws, never blocks on uncertainty: any error →
3286
+ * `{ action: "allow", allow: true, degraded: true }`, so a glitch can't reject a real signup.
3287
+ *
3288
+ * @experimental Preview — Crossdeck Trust ships with Crossdeck v2.
3289
+ */
3290
+ async gate(input, options) {
3291
+ const failOpen = () => ({
3292
+ action: "allow",
3293
+ allow: true,
3294
+ blockReason: null,
3295
+ blockedKey: null,
3296
+ degraded: true
3297
+ });
3298
+ if (!input || !input.email && !input.ip && !input.domain) {
3299
+ this.debug.emit(
3300
+ "sdk.gate_missing_input",
3301
+ "gate() needs at least one of email / ip / domain; returning fail-open allow:true."
3302
+ );
3303
+ return failOpen();
3304
+ }
3305
+ try {
3306
+ const raw = await this.http.request("POST", "/trust/gate", {
3307
+ body: {
3308
+ ...input.email ? { email: input.email } : {},
3309
+ ...input.ip ? { ip: input.ip } : {},
3310
+ ...input.domain ? { domain: input.domain } : {},
3311
+ ...input.fingerprint ? { fingerprint: input.fingerprint } : {}
3312
+ },
3313
+ signal: options?.signal,
3314
+ timeoutMs: options?.timeoutMs
3315
+ });
3316
+ const action = raw.action === "block" || raw.action === "review" ? raw.action : "allow";
3317
+ return {
3318
+ action,
3319
+ allow: raw.allow !== false && action !== "block",
3320
+ blockReason: raw.blockReason ?? null,
3321
+ blockedKey: raw.blockedKey ?? null,
3322
+ reference: raw.reference ?? null,
3323
+ supportEmail: raw.supportEmail ?? null
3324
+ };
3325
+ } catch (err) {
3326
+ this.debug.emit(
3327
+ "sdk.gate_failed",
3328
+ `gate() failed \u2014 fail-open allow:true. ${err instanceof Error ? err.message : String(err)}`,
3329
+ { error: err instanceof Error ? err.message : String(err) }
3330
+ );
3331
+ return failOpen();
3332
+ }
3333
+ }
3276
3334
  /**
3277
3335
  * Synchronous entitlement check. Returns `true` iff the customer
3278
3336
  * has the entitlement AND the cache entry is fresh (within
@@ -4720,8 +4778,8 @@ function normaliseSecrets(input) {
4720
4778
  }
4721
4779
 
4722
4780
  // src/_contracts-bundled.ts
4723
- var BUNDLED_IN = "@cross-deck/node@1.10.0";
4724
- var SDK_VERSION2 = "1.10.0";
4781
+ var BUNDLED_IN = "@cross-deck/node@1.11.0";
4782
+ var SDK_VERSION2 = "1.11.0";
4725
4783
  var BUNDLED_CONTRACTS = Object.freeze([
4726
4784
  {
4727
4785
  "id": "contract-failed-payload-schema-lock",
@@ -4843,7 +4901,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
4843
4901
  "legal/security/index.html#diagnostic",
4844
4902
  "legal/sdk-data/index.html#b-diagnostic"
4845
4903
  ],
4846
- "bundledIn": "@cross-deck/node@1.10.0"
4904
+ "bundledIn": "@cross-deck/node@1.11.0"
4847
4905
  },
4848
4906
  {
4849
4907
  "id": "documentation-honesty",
@@ -4875,7 +4933,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
4875
4933
  ],
4876
4934
  "registeredAt": "2026-05-26",
4877
4935
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.1",
4878
- "bundledIn": "@cross-deck/node@1.10.0"
4936
+ "bundledIn": "@cross-deck/node@1.11.0"
4879
4937
  },
4880
4938
  {
4881
4939
  "id": "error-envelope-shape",
@@ -4914,7 +4972,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
4914
4972
  ],
4915
4973
  "registeredAt": "2026-05-26",
4916
4974
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
4917
- "bundledIn": "@cross-deck/node@1.10.0"
4975
+ "bundledIn": "@cross-deck/node@1.11.0"
4918
4976
  },
4919
4977
  {
4920
4978
  "id": "flush-interval-parity",
@@ -4959,7 +5017,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
4959
5017
  ],
4960
5018
  "registeredAt": "2026-05-26",
4961
5019
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
4962
- "bundledIn": "@cross-deck/node@1.10.0"
5020
+ "bundledIn": "@cross-deck/node@1.11.0"
4963
5021
  },
4964
5022
  {
4965
5023
  "id": "idempotency-key-deterministic",
@@ -5064,7 +5122,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5064
5122
  ],
5065
5123
  "registeredAt": "2026-05-26",
5066
5124
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
5067
- "bundledIn": "@cross-deck/node@1.10.0"
5125
+ "bundledIn": "@cross-deck/node@1.11.0"
5068
5126
  },
5069
5127
  {
5070
5128
  "id": "invalid-input-rejected-natively",
@@ -5120,7 +5178,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5120
5178
  ],
5121
5179
  "registeredAt": "2026-06-11",
5122
5180
  "firstRegisteredIn": "swift trap-on-input class fix \u2014 first machine-tested Swift release",
5123
- "bundledIn": "@cross-deck/node@1.10.0"
5181
+ "bundledIn": "@cross-deck/node@1.11.0"
5124
5182
  },
5125
5183
  {
5126
5184
  "id": "node-pii-scrubber",
@@ -5159,7 +5217,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5159
5217
  ],
5160
5218
  "registeredAt": "2026-05-26",
5161
5219
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.1",
5162
- "bundledIn": "@cross-deck/node@1.10.0"
5220
+ "bundledIn": "@cross-deck/node@1.11.0"
5163
5221
  },
5164
5222
  {
5165
5223
  "id": "node-shutdown-awaits-flush",
@@ -5192,7 +5250,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5192
5250
  ],
5193
5251
  "registeredAt": "2026-05-26",
5194
5252
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.4",
5195
- "bundledIn": "@cross-deck/node@1.10.0"
5253
+ "bundledIn": "@cross-deck/node@1.11.0"
5196
5254
  },
5197
5255
  {
5198
5256
  "id": "sdk-error-codes-catalogue",
@@ -5237,7 +5295,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5237
5295
  ],
5238
5296
  "registeredAt": "2026-05-26",
5239
5297
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
5240
- "bundledIn": "@cross-deck/node@1.10.0"
5298
+ "bundledIn": "@cross-deck/node@1.11.0"
5241
5299
  },
5242
5300
  {
5243
5301
  "id": "sync-purchases-funnel-parity",
@@ -5270,7 +5328,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5270
5328
  ],
5271
5329
  "registeredAt": "2026-05-26",
5272
5330
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
5273
- "bundledIn": "@cross-deck/node@1.10.0"
5331
+ "bundledIn": "@cross-deck/node@1.11.0"
5274
5332
  },
5275
5333
  {
5276
5334
  "id": "verifier-timestamp-mandatory",
@@ -5324,7 +5382,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5324
5382
  ],
5325
5383
  "registeredAt": "2026-05-26",
5326
5384
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.2",
5327
- "bundledIn": "@cross-deck/node@1.10.0"
5385
+ "bundledIn": "@cross-deck/node@1.11.0"
5328
5386
  }
5329
5387
  ]);
5330
5388