@cross-deck/node 1.10.0 → 1.10.1

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,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.10.1] — 2026-06-30
10
+
11
+ **Fixed — the blocking surface now works for the documented paths (still v2 preview).**
12
+
13
+ - `resolve()` / `isBlocked()` now accept a **bare `userId`** — the server-trusted
14
+ recognition path the backend honours (rides the identity backbone, matches on the email
15
+ Crossdeck already holds). 1.10.0's guard rejected it and fail-opened *without calling the
16
+ API*, so a known-user block silently never fired.
17
+ - **Added `gate({ email, ip })`** — the brand-new-signup door (`POST /v1/trust/gate`), the
18
+ only path that catches a farm Crossdeck has never seen. Fail-open by contract. New
19
+ `GateInput` / `GateVerdict` types.
20
+ - README now documents the blocking surface (the three doors + the verify-locally pattern).
21
+
22
+ Still `@experimental` — Crossdeck Trust ships with Crossdeck v2.
23
+
24
+
9
25
  ## [1.10.0] — 2026-06-30
10
26
 
11
27
  **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-BpBVFC4O.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-BpBVFC4O.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.10.1";
187
+ readonly bundledIn: "@cross-deck/node@1.10.1";
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
@@ -514,6 +514,41 @@ interface OwnerStatusInput {
514
514
  domain?: string;
515
515
  ip?: string;
516
516
  }
517
+ /**
518
+ * Input for {@link CrossdeckServer.gate} — the brand-new-signup door. Pass the two strings
519
+ * you have at signup: `email` and `ip`. (Optional `domain`/`fingerprint` sharpen the score.)
520
+ *
521
+ * @experimental Preview — see {@link ResolveInput}.
522
+ */
523
+ interface GateInput {
524
+ /** The signing-up user's email. Matched against email + domain blocklist rules. */
525
+ email?: string;
526
+ /** The signing-up request's IP. Matched against ip rules + datacenter/velocity signals. */
527
+ ip?: string;
528
+ /** Optional explicit domain (defaults to the email's domain). */
529
+ domain?: string;
530
+ /** Optional device fingerprint — sharpens the composite signup score. */
531
+ fingerprint?: string;
532
+ }
533
+ /**
534
+ * The signup-gate verdict from {@link CrossdeckServer.gate}. Fail-open: on any error these
535
+ * come back `{ action: "allow", allow: true, degraded: true }` — a glitch never rejects a
536
+ * real signup.
537
+ *
538
+ * @experimental Preview — see {@link ResolveInput}.
539
+ */
540
+ interface GateVerdict {
541
+ /** `"allow"` | `"review"` | `"block"`. Reject the account only on `"block"`. */
542
+ action: "allow" | "review" | "block";
543
+ /** Convenience: `false` only when `action` is `"block"`. */
544
+ allow: boolean;
545
+ /** Why, when blocked: e.g. `"disposable_domain"` | `"blocklist:ip"` | `"blocklist:domain"`. */
546
+ blockReason: string | null;
547
+ /** The exact rule/key that fired, when blocked. */
548
+ blockedKey: string | null;
549
+ /** True when this is the fail-open default (Crossdeck unreachable). `allow` is `true`. */
550
+ degraded?: boolean;
551
+ }
517
552
  /**
518
553
  * Snapshot of one customer's last-known-good entitlements, as written
519
554
  * to / read from a durable `EntitlementStore`. Versioned for forward-
@@ -1566,6 +1601,19 @@ declare class CrossdeckServer extends EventEmitter {
1566
1601
  * @experimental Preview — see {@link resolve}.
1567
1602
  */
1568
1603
  getOwnerStatus(input: OwnerStatusInput, options?: RequestOptions): Promise<BlockVerdict>;
1604
+ /**
1605
+ * The brand-new-signup door (§3 of the Blocking developer guide). Block a user Crossdeck
1606
+ * has **never seen** — at the instant they sign up — by the two strings you have then:
1607
+ * their `email` and `ip`. There's no identity to ride yet, so {@link resolve} can't catch
1608
+ * them; this matches `email` / domain / `ip` blocklist rules directly. Call it BEFORE you
1609
+ * create the account; on `action: "block"`, reject the signup.
1610
+ *
1611
+ * **Fail-open by contract** — never throws, never blocks on uncertainty: any error →
1612
+ * `{ action: "allow", allow: true, degraded: true }`, so a glitch can't reject a real signup.
1613
+ *
1614
+ * @experimental Preview — Crossdeck Trust ships with Crossdeck v2.
1615
+ */
1616
+ gate(input: GateInput, options?: RequestOptions): Promise<GateVerdict>;
1569
1617
  /**
1570
1618
  * Synchronous entitlement check. Returns `true` iff the customer
1571
1619
  * has the entitlement AND the cache entry is fresh (within
@@ -2045,4 +2093,4 @@ declare class CrossdeckServer extends EventEmitter {
2045
2093
  private normalizeIngestEvent;
2046
2094
  }
2047
2095
 
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 };
2096
+ 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.10.1";
187
+ readonly bundledIn: "@cross-deck/node@1.10.1";
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
@@ -514,6 +514,41 @@ interface OwnerStatusInput {
514
514
  domain?: string;
515
515
  ip?: string;
516
516
  }
517
+ /**
518
+ * Input for {@link CrossdeckServer.gate} — the brand-new-signup door. Pass the two strings
519
+ * you have at signup: `email` and `ip`. (Optional `domain`/`fingerprint` sharpen the score.)
520
+ *
521
+ * @experimental Preview — see {@link ResolveInput}.
522
+ */
523
+ interface GateInput {
524
+ /** The signing-up user's email. Matched against email + domain blocklist rules. */
525
+ email?: string;
526
+ /** The signing-up request's IP. Matched against ip rules + datacenter/velocity signals. */
527
+ ip?: string;
528
+ /** Optional explicit domain (defaults to the email's domain). */
529
+ domain?: string;
530
+ /** Optional device fingerprint — sharpens the composite signup score. */
531
+ fingerprint?: string;
532
+ }
533
+ /**
534
+ * The signup-gate verdict from {@link CrossdeckServer.gate}. Fail-open: on any error these
535
+ * come back `{ action: "allow", allow: true, degraded: true }` — a glitch never rejects a
536
+ * real signup.
537
+ *
538
+ * @experimental Preview — see {@link ResolveInput}.
539
+ */
540
+ interface GateVerdict {
541
+ /** `"allow"` | `"review"` | `"block"`. Reject the account only on `"block"`. */
542
+ action: "allow" | "review" | "block";
543
+ /** Convenience: `false` only when `action` is `"block"`. */
544
+ allow: boolean;
545
+ /** Why, when blocked: e.g. `"disposable_domain"` | `"blocklist:ip"` | `"blocklist:domain"`. */
546
+ blockReason: string | null;
547
+ /** The exact rule/key that fired, when blocked. */
548
+ blockedKey: string | null;
549
+ /** True when this is the fail-open default (Crossdeck unreachable). `allow` is `true`. */
550
+ degraded?: boolean;
551
+ }
517
552
  /**
518
553
  * Snapshot of one customer's last-known-good entitlements, as written
519
554
  * to / read from a durable `EntitlementStore`. Versioned for forward-
@@ -1566,6 +1601,19 @@ declare class CrossdeckServer extends EventEmitter {
1566
1601
  * @experimental Preview — see {@link resolve}.
1567
1602
  */
1568
1603
  getOwnerStatus(input: OwnerStatusInput, options?: RequestOptions): Promise<BlockVerdict>;
1604
+ /**
1605
+ * The brand-new-signup door (§3 of the Blocking developer guide). Block a user Crossdeck
1606
+ * has **never seen** — at the instant they sign up — by the two strings you have then:
1607
+ * their `email` and `ip`. There's no identity to ride yet, so {@link resolve} can't catch
1608
+ * them; this matches `email` / domain / `ip` blocklist rules directly. Call it BEFORE you
1609
+ * create the account; on `action: "block"`, reject the signup.
1610
+ *
1611
+ * **Fail-open by contract** — never throws, never blocks on uncertainty: any error →
1612
+ * `{ action: "allow", allow: true, degraded: true }`, so a glitch can't reject a real signup.
1613
+ *
1614
+ * @experimental Preview — Crossdeck Trust ships with Crossdeck v2.
1615
+ */
1616
+ gate(input: GateInput, options?: RequestOptions): Promise<GateVerdict>;
1569
1617
  /**
1570
1618
  * Synchronous entitlement check. Returns `true` iff the customer
1571
1619
  * has the entitlement AND the cache entry is fresh (within
@@ -2045,4 +2093,4 @@ declare class CrossdeckServer extends EventEmitter {
2045
2093
  private normalizeIngestEvent;
2046
2094
  }
2047
2095
 
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 };
2096
+ 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.10.1";
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
  }
@@ -3273,6 +3273,60 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
3273
3273
  return { blocked: false, blockReason: null, blockedKey: null, degraded: true };
3274
3274
  }
3275
3275
  }
3276
+ /**
3277
+ * The brand-new-signup door (§3 of the Blocking developer guide). Block a user Crossdeck
3278
+ * has **never seen** — at the instant they sign up — by the two strings you have then:
3279
+ * their `email` and `ip`. There's no identity to ride yet, so {@link resolve} can't catch
3280
+ * them; this matches `email` / domain / `ip` blocklist rules directly. Call it BEFORE you
3281
+ * create the account; on `action: "block"`, reject the signup.
3282
+ *
3283
+ * **Fail-open by contract** — never throws, never blocks on uncertainty: any error →
3284
+ * `{ action: "allow", allow: true, degraded: true }`, so a glitch can't reject a real signup.
3285
+ *
3286
+ * @experimental Preview — Crossdeck Trust ships with Crossdeck v2.
3287
+ */
3288
+ async gate(input, options) {
3289
+ const failOpen = () => ({
3290
+ action: "allow",
3291
+ allow: true,
3292
+ blockReason: null,
3293
+ blockedKey: null,
3294
+ degraded: true
3295
+ });
3296
+ if (!input || !input.email && !input.ip && !input.domain) {
3297
+ this.debug.emit(
3298
+ "sdk.gate_missing_input",
3299
+ "gate() needs at least one of email / ip / domain; returning fail-open allow:true."
3300
+ );
3301
+ return failOpen();
3302
+ }
3303
+ try {
3304
+ const raw = await this.http.request("POST", "/trust/gate", {
3305
+ body: {
3306
+ ...input.email ? { email: input.email } : {},
3307
+ ...input.ip ? { ip: input.ip } : {},
3308
+ ...input.domain ? { domain: input.domain } : {},
3309
+ ...input.fingerprint ? { fingerprint: input.fingerprint } : {}
3310
+ },
3311
+ signal: options?.signal,
3312
+ timeoutMs: options?.timeoutMs
3313
+ });
3314
+ const action = raw.action === "block" || raw.action === "review" ? raw.action : "allow";
3315
+ return {
3316
+ action,
3317
+ allow: raw.allow !== false && action !== "block",
3318
+ blockReason: raw.blockReason ?? null,
3319
+ blockedKey: raw.blockedKey ?? null
3320
+ };
3321
+ } catch (err) {
3322
+ this.debug.emit(
3323
+ "sdk.gate_failed",
3324
+ `gate() failed \u2014 fail-open allow:true. ${err instanceof Error ? err.message : String(err)}`,
3325
+ { error: err instanceof Error ? err.message : String(err) }
3326
+ );
3327
+ return failOpen();
3328
+ }
3329
+ }
3276
3330
  /**
3277
3331
  * Synchronous entitlement check. Returns `true` iff the customer
3278
3332
  * has the entitlement AND the cache entry is fresh (within
@@ -4720,8 +4774,8 @@ function normaliseSecrets(input) {
4720
4774
  }
4721
4775
 
4722
4776
  // src/_contracts-bundled.ts
4723
- var BUNDLED_IN = "@cross-deck/node@1.10.0";
4724
- var SDK_VERSION2 = "1.10.0";
4777
+ var BUNDLED_IN = "@cross-deck/node@1.10.1";
4778
+ var SDK_VERSION2 = "1.10.1";
4725
4779
  var BUNDLED_CONTRACTS = Object.freeze([
4726
4780
  {
4727
4781
  "id": "contract-failed-payload-schema-lock",
@@ -4843,7 +4897,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
4843
4897
  "legal/security/index.html#diagnostic",
4844
4898
  "legal/sdk-data/index.html#b-diagnostic"
4845
4899
  ],
4846
- "bundledIn": "@cross-deck/node@1.10.0"
4900
+ "bundledIn": "@cross-deck/node@1.10.1"
4847
4901
  },
4848
4902
  {
4849
4903
  "id": "documentation-honesty",
@@ -4875,7 +4929,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
4875
4929
  ],
4876
4930
  "registeredAt": "2026-05-26",
4877
4931
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.1",
4878
- "bundledIn": "@cross-deck/node@1.10.0"
4932
+ "bundledIn": "@cross-deck/node@1.10.1"
4879
4933
  },
4880
4934
  {
4881
4935
  "id": "error-envelope-shape",
@@ -4914,7 +4968,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
4914
4968
  ],
4915
4969
  "registeredAt": "2026-05-26",
4916
4970
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
4917
- "bundledIn": "@cross-deck/node@1.10.0"
4971
+ "bundledIn": "@cross-deck/node@1.10.1"
4918
4972
  },
4919
4973
  {
4920
4974
  "id": "flush-interval-parity",
@@ -4959,7 +5013,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
4959
5013
  ],
4960
5014
  "registeredAt": "2026-05-26",
4961
5015
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
4962
- "bundledIn": "@cross-deck/node@1.10.0"
5016
+ "bundledIn": "@cross-deck/node@1.10.1"
4963
5017
  },
4964
5018
  {
4965
5019
  "id": "idempotency-key-deterministic",
@@ -5064,7 +5118,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5064
5118
  ],
5065
5119
  "registeredAt": "2026-05-26",
5066
5120
  "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"
5121
+ "bundledIn": "@cross-deck/node@1.10.1"
5068
5122
  },
5069
5123
  {
5070
5124
  "id": "invalid-input-rejected-natively",
@@ -5120,7 +5174,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5120
5174
  ],
5121
5175
  "registeredAt": "2026-06-11",
5122
5176
  "firstRegisteredIn": "swift trap-on-input class fix \u2014 first machine-tested Swift release",
5123
- "bundledIn": "@cross-deck/node@1.10.0"
5177
+ "bundledIn": "@cross-deck/node@1.10.1"
5124
5178
  },
5125
5179
  {
5126
5180
  "id": "node-pii-scrubber",
@@ -5159,7 +5213,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5159
5213
  ],
5160
5214
  "registeredAt": "2026-05-26",
5161
5215
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.1",
5162
- "bundledIn": "@cross-deck/node@1.10.0"
5216
+ "bundledIn": "@cross-deck/node@1.10.1"
5163
5217
  },
5164
5218
  {
5165
5219
  "id": "node-shutdown-awaits-flush",
@@ -5192,7 +5246,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5192
5246
  ],
5193
5247
  "registeredAt": "2026-05-26",
5194
5248
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.4",
5195
- "bundledIn": "@cross-deck/node@1.10.0"
5249
+ "bundledIn": "@cross-deck/node@1.10.1"
5196
5250
  },
5197
5251
  {
5198
5252
  "id": "sdk-error-codes-catalogue",
@@ -5237,7 +5291,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5237
5291
  ],
5238
5292
  "registeredAt": "2026-05-26",
5239
5293
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
5240
- "bundledIn": "@cross-deck/node@1.10.0"
5294
+ "bundledIn": "@cross-deck/node@1.10.1"
5241
5295
  },
5242
5296
  {
5243
5297
  "id": "sync-purchases-funnel-parity",
@@ -5270,7 +5324,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5270
5324
  ],
5271
5325
  "registeredAt": "2026-05-26",
5272
5326
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
5273
- "bundledIn": "@cross-deck/node@1.10.0"
5327
+ "bundledIn": "@cross-deck/node@1.10.1"
5274
5328
  },
5275
5329
  {
5276
5330
  "id": "verifier-timestamp-mandatory",
@@ -5324,7 +5378,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5324
5378
  ],
5325
5379
  "registeredAt": "2026-05-26",
5326
5380
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.2",
5327
- "bundledIn": "@cross-deck/node@1.10.0"
5381
+ "bundledIn": "@cross-deck/node@1.10.1"
5328
5382
  }
5329
5383
  ]);
5330
5384