@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 +31 -0
- package/README.md +45 -0
- package/dist/auto-events/index.d.mts +1 -1
- package/dist/auto-events/index.d.ts +1 -1
- package/dist/{crossdeck-server-C4nWPQzH.d.mts → crossdeck-server-DAQX_7rm.d.mts} +67 -3
- package/dist/{crossdeck-server-C4nWPQzH.d.ts → crossdeck-server-DAQX_7rm.d.ts} +67 -3
- package/dist/index.cjs +74 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +74 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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)
|
|
@@ -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.
|
|
187
|
-
readonly bundledIn: "@cross-deck/node@1.
|
|
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
|
|
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.
|
|
187
|
-
readonly bundledIn: "@cross-deck/node@1.
|
|
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
|
|
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.
|
|
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 && !
|
|
3188
|
+
if (!input || !input.anonymousId && !input.userId) {
|
|
3189
3189
|
this.debug.emit(
|
|
3190
3190
|
"sdk.resolve_missing_identity",
|
|
3191
|
-
"resolve() needs anonymousId
|
|
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.
|
|
4724
|
-
var SDK_VERSION2 = "1.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
5385
|
+
"bundledIn": "@cross-deck/node@1.11.0"
|
|
5328
5386
|
}
|
|
5329
5387
|
]);
|
|
5330
5388
|
|