@cross-deck/node 1.9.1 → 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 +39 -0
- package/README.md +47 -2
- package/dist/auto-events/index.d.mts +1 -1
- package/dist/auto-events/index.d.ts +1 -1
- package/dist/{crossdeck-server-Q3CYJC4Z.d.mts → crossdeck-server-BpBVFC4O.d.mts} +161 -3
- package/dist/{crossdeck-server-Q3CYJC4Z.d.ts → crossdeck-server-BpBVFC4O.d.ts} +161 -3
- package/dist/index.cjs +192 -14
- 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 +192 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,45 @@ 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
|
+
|
|
25
|
+
## [1.10.0] — 2026-06-30
|
|
26
|
+
|
|
27
|
+
**Added — the blocking surface (Crossdeck Trust, v2 preview).** Blocking is
|
|
28
|
+
"entitlements, inverted": `getEntitlements()` answers *can they access Pro?*; the new
|
|
29
|
+
methods answer *should they be here at all?* — the same `/v1/resolve` plumbing, read for
|
|
30
|
+
`blocked`. Additive and non-breaking.
|
|
31
|
+
|
|
32
|
+
- **`resolve(input)`** → `ResolveResult` — the block verdict plus identity/entitlement
|
|
33
|
+
context in one call. **Fail-open by contract:** never throws for the verdict and never
|
|
34
|
+
returns `blocked: true` on uncertainty; any error → `{ blocked: false, degraded: true }`,
|
|
35
|
+
so a glitch can never lock out a real user (the mirror of entitlements, which fail
|
|
36
|
+
closed). Forward the end-user's `ip` (server-to-server callers otherwise expose only
|
|
37
|
+
their own server IP, so ip-rules can't match) and the verified identity
|
|
38
|
+
(`userId` + `idToken`) so domain/email rules can match.
|
|
39
|
+
- **`isBlocked(input)`** → `boolean` — convenience over `resolve()`; same fail-open.
|
|
40
|
+
- **`getOwnerStatus(input)`** → `BlockVerdict` — the public-page path: *"is the owner of
|
|
41
|
+
this page blocked?"*, no session token, for cache invalidation on link-in-bio / content
|
|
42
|
+
platforms (Pattern B).
|
|
43
|
+
|
|
44
|
+
New exported types: `ResolveInput`, `ResolveResult`, `BlockVerdict`, `OwnerStatusInput`.
|
|
45
|
+
All marked `@experimental` — Crossdeck Trust ships with Crossdeck v2; the shape is stable,
|
|
46
|
+
the surface is not yet GA.
|
|
47
|
+
|
|
9
48
|
## [1.9.1] — 2026-06-30
|
|
10
49
|
|
|
11
50
|
**Docs — knowledge-backbone governance release.** No runtime API change. This
|
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)
|
|
@@ -614,8 +659,8 @@ CrossdeckContracts.byId("idempotency-key-deterministic");
|
|
|
614
659
|
CrossdeckContracts.byPillar("revenue");
|
|
615
660
|
CrossdeckContracts.withStatus("proposed");
|
|
616
661
|
CrossdeckContracts.findByTestName("rail namespacing prevents cross-rail collisions");
|
|
617
|
-
CrossdeckContracts.sdkVersion; // "1.
|
|
618
|
-
CrossdeckContracts.bundledIn; // "@cross-deck/node@1.
|
|
662
|
+
CrossdeckContracts.sdkVersion; // "1.10.0"
|
|
663
|
+
CrossdeckContracts.bundledIn; // "@cross-deck/node@1.10.0"
|
|
619
664
|
```
|
|
620
665
|
|
|
621
666
|
The `Contract` type is exported alongside; the binary-stability promise is documented in [`contracts/README.md`](https://github.com/VistaApps-za/crossdeck/blob/main/contracts/README.md).
|
|
@@ -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.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
|
|
@@ -438,6 +438,117 @@ interface EntitlementsListResponse {
|
|
|
438
438
|
crossdeckCustomerId: string;
|
|
439
439
|
env: Environment;
|
|
440
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Identity for {@link CrossdeckServer.resolve} / {@link CrossdeckServer.isBlocked}.
|
|
443
|
+
* Send the END USER's identity, not your server's.
|
|
444
|
+
*
|
|
445
|
+
* @experimental Preview — Crossdeck Trust ships with Crossdeck v2. Shape is stable; the
|
|
446
|
+
* surface is not yet GA.
|
|
447
|
+
*/
|
|
448
|
+
interface ResolveInput {
|
|
449
|
+
/** Your signed-in user id. */
|
|
450
|
+
userId?: string;
|
|
451
|
+
/** A SIGNED proof of that user (e.g. their Firebase ID token). Required alongside `userId` for the verified path. */
|
|
452
|
+
idToken?: string;
|
|
453
|
+
/** Anonymous device id (the Web SDK's `getAnonymousId()`). Folds a pre-login device. */
|
|
454
|
+
anonymousId?: string;
|
|
455
|
+
/**
|
|
456
|
+
* The END USER's IP. Forward it — you call resolve server-to-server, so without it
|
|
457
|
+
* Crossdeck sees YOUR server's IP and an ip-rule can't match the visitor.
|
|
458
|
+
*/
|
|
459
|
+
ip?: string;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* The block verdict. Fail-open: on any error these come back `{ blocked: false }`,
|
|
463
|
+
* never an exception — a glitch can never lock out a real user (the mirror of
|
|
464
|
+
* entitlements, which fail closed).
|
|
465
|
+
*
|
|
466
|
+
* @experimental Preview — see {@link ResolveInput}.
|
|
467
|
+
*/
|
|
468
|
+
interface BlockVerdict {
|
|
469
|
+
/** True ONLY when Crossdeck holds an explicit block on this identity. */
|
|
470
|
+
blocked: boolean;
|
|
471
|
+
/** Why, when blocked: e.g. `"blocklist:domain"` | `"blocklist:ip"` | `"blocked"`. */
|
|
472
|
+
blockReason: string | null;
|
|
473
|
+
/** The exact rule that fired: `"domain:spartan.net"` | `"ip:1.2.3.4"` | `"manual"` | null. */
|
|
474
|
+
blockedKey: string | null;
|
|
475
|
+
/**
|
|
476
|
+
* True when this is the fail-open default (Crossdeck unreachable / identity unresolved).
|
|
477
|
+
* `blocked` is `false`; log it for observability, never treat it as a real "not blocked".
|
|
478
|
+
*/
|
|
479
|
+
degraded?: boolean;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* The full {@link CrossdeckServer.resolve} result — the block verdict plus the identity /
|
|
483
|
+
* entitlement context, so one call answers "should they be here?" and "what can they
|
|
484
|
+
* access?" together.
|
|
485
|
+
*
|
|
486
|
+
* @experimental Preview — see {@link ResolveInput}.
|
|
487
|
+
*/
|
|
488
|
+
interface ResolveResult extends BlockVerdict {
|
|
489
|
+
/** `"active"` | `"anonymous"` | … the resolved identity status. */
|
|
490
|
+
status: string;
|
|
491
|
+
/** Active entitlement keys for this identity (e.g. `["pro"]`). */
|
|
492
|
+
entitlements: string[];
|
|
493
|
+
/** The canonical Crossdeck customer id, when resolved. */
|
|
494
|
+
crossdeckCustomerId: string | null;
|
|
495
|
+
/** The anonymous id Crossdeck resolved / assigned. */
|
|
496
|
+
anonymousId: string | null;
|
|
497
|
+
/**
|
|
498
|
+
* Set when a verified identity was sent but couldn't be checked (e.g. the auth issuer
|
|
499
|
+
* isn't registered). If you sent `userId`+`idToken` and `status` is `"anonymous"`, read this.
|
|
500
|
+
*/
|
|
501
|
+
identityError: {
|
|
502
|
+
reason: string;
|
|
503
|
+
} | null;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Owner identifiers for {@link CrossdeckServer.getOwnerStatus} — the public-page path.
|
|
507
|
+
* Pass any subset; for a page-offline check the `ip` is the owner's CREATION ip.
|
|
508
|
+
*
|
|
509
|
+
* @experimental Preview — see {@link ResolveInput}.
|
|
510
|
+
*/
|
|
511
|
+
interface OwnerStatusInput {
|
|
512
|
+
userId?: string;
|
|
513
|
+
email?: string;
|
|
514
|
+
domain?: string;
|
|
515
|
+
ip?: string;
|
|
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
|
+
}
|
|
441
552
|
/**
|
|
442
553
|
* Snapshot of one customer's last-known-good entitlements, as written
|
|
443
554
|
* to / read from a durable `EntitlementStore`. Versioned for forward-
|
|
@@ -1456,6 +1567,53 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1456
1567
|
*/
|
|
1457
1568
|
getEntitlements(hints: IdentityHints, options?: RequestOptions): Promise<EntitlementsListResponse>;
|
|
1458
1569
|
getCustomerEntitlements(customerId: string, options?: RequestOptions): Promise<EntitlementsListResponse>;
|
|
1570
|
+
/**
|
|
1571
|
+
* Resolve a user's BLOCK verdict (plus identity / entitlement context) — the same
|
|
1572
|
+
* `/v1/resolve` you'd call for entitlements, read for `blocked`. Wire it into signup
|
|
1573
|
+
* and every visit; on `blocked`, sign the user out (and, for public content, take
|
|
1574
|
+
* their pages offline — see the Blocking developer guide, Pattern B).
|
|
1575
|
+
*
|
|
1576
|
+
* **Fail-open is the contract.** This never throws for the block decision and never
|
|
1577
|
+
* returns `blocked: true` on uncertainty. Any error — Crossdeck unreachable, timeout,
|
|
1578
|
+
* unresolved identity — yields `{ blocked: false, degraded: true }`, so a glitch can
|
|
1579
|
+
* never lock out a real user.
|
|
1580
|
+
*
|
|
1581
|
+
* Forward the END USER's `ip` (you call this server-to-server, so otherwise Crossdeck
|
|
1582
|
+
* sees your server's IP and an ip-rule can't match the visitor), and the verified
|
|
1583
|
+
* identity (`userId` + `idToken`) so a `domain`/`email` rule can match the email.
|
|
1584
|
+
*
|
|
1585
|
+
* @experimental Preview — Crossdeck Trust ships with Crossdeck v2. Stable shape, not yet GA.
|
|
1586
|
+
*/
|
|
1587
|
+
resolve(input: ResolveInput, options?: RequestOptions): Promise<ResolveResult>;
|
|
1588
|
+
/**
|
|
1589
|
+
* Convenience over {@link resolve}: just the boolean. Fail-open (false on any error).
|
|
1590
|
+
*
|
|
1591
|
+
* @experimental Preview — see {@link resolve}.
|
|
1592
|
+
*/
|
|
1593
|
+
isBlocked(input: ResolveInput, options?: RequestOptions): Promise<boolean>;
|
|
1594
|
+
/**
|
|
1595
|
+
* The OWNER's block verdict for the public-page path (Pattern B / §5b of the Blocking
|
|
1596
|
+
* developer guide) — "is the owner of this page blocked?", no session token required.
|
|
1597
|
+
* Pass the owner's recorded identifiers (`userId`/`email`/`domain`/`ip`; the `ip` is
|
|
1598
|
+
* their CREATION ip, not a live visitor's). Poll on a short TTL to invalidate your
|
|
1599
|
+
* public-page cache. Fail-open — any error → `{ blocked: false, degraded: true }`.
|
|
1600
|
+
*
|
|
1601
|
+
* @experimental Preview — see {@link resolve}.
|
|
1602
|
+
*/
|
|
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>;
|
|
1459
1617
|
/**
|
|
1460
1618
|
* Synchronous entitlement check. Returns `true` iff the customer
|
|
1461
1619
|
* has the entitlement AND the cache entry is fresh (within
|
|
@@ -1935,4 +2093,4 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1935
2093
|
private normalizeIngestEvent;
|
|
1936
2094
|
}
|
|
1937
2095
|
|
|
1938
|
-
export { type
|
|
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.
|
|
187
|
-
readonly bundledIn: "@cross-deck/node@1.
|
|
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
|
|
@@ -438,6 +438,117 @@ interface EntitlementsListResponse {
|
|
|
438
438
|
crossdeckCustomerId: string;
|
|
439
439
|
env: Environment;
|
|
440
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Identity for {@link CrossdeckServer.resolve} / {@link CrossdeckServer.isBlocked}.
|
|
443
|
+
* Send the END USER's identity, not your server's.
|
|
444
|
+
*
|
|
445
|
+
* @experimental Preview — Crossdeck Trust ships with Crossdeck v2. Shape is stable; the
|
|
446
|
+
* surface is not yet GA.
|
|
447
|
+
*/
|
|
448
|
+
interface ResolveInput {
|
|
449
|
+
/** Your signed-in user id. */
|
|
450
|
+
userId?: string;
|
|
451
|
+
/** A SIGNED proof of that user (e.g. their Firebase ID token). Required alongside `userId` for the verified path. */
|
|
452
|
+
idToken?: string;
|
|
453
|
+
/** Anonymous device id (the Web SDK's `getAnonymousId()`). Folds a pre-login device. */
|
|
454
|
+
anonymousId?: string;
|
|
455
|
+
/**
|
|
456
|
+
* The END USER's IP. Forward it — you call resolve server-to-server, so without it
|
|
457
|
+
* Crossdeck sees YOUR server's IP and an ip-rule can't match the visitor.
|
|
458
|
+
*/
|
|
459
|
+
ip?: string;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* The block verdict. Fail-open: on any error these come back `{ blocked: false }`,
|
|
463
|
+
* never an exception — a glitch can never lock out a real user (the mirror of
|
|
464
|
+
* entitlements, which fail closed).
|
|
465
|
+
*
|
|
466
|
+
* @experimental Preview — see {@link ResolveInput}.
|
|
467
|
+
*/
|
|
468
|
+
interface BlockVerdict {
|
|
469
|
+
/** True ONLY when Crossdeck holds an explicit block on this identity. */
|
|
470
|
+
blocked: boolean;
|
|
471
|
+
/** Why, when blocked: e.g. `"blocklist:domain"` | `"blocklist:ip"` | `"blocked"`. */
|
|
472
|
+
blockReason: string | null;
|
|
473
|
+
/** The exact rule that fired: `"domain:spartan.net"` | `"ip:1.2.3.4"` | `"manual"` | null. */
|
|
474
|
+
blockedKey: string | null;
|
|
475
|
+
/**
|
|
476
|
+
* True when this is the fail-open default (Crossdeck unreachable / identity unresolved).
|
|
477
|
+
* `blocked` is `false`; log it for observability, never treat it as a real "not blocked".
|
|
478
|
+
*/
|
|
479
|
+
degraded?: boolean;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* The full {@link CrossdeckServer.resolve} result — the block verdict plus the identity /
|
|
483
|
+
* entitlement context, so one call answers "should they be here?" and "what can they
|
|
484
|
+
* access?" together.
|
|
485
|
+
*
|
|
486
|
+
* @experimental Preview — see {@link ResolveInput}.
|
|
487
|
+
*/
|
|
488
|
+
interface ResolveResult extends BlockVerdict {
|
|
489
|
+
/** `"active"` | `"anonymous"` | … the resolved identity status. */
|
|
490
|
+
status: string;
|
|
491
|
+
/** Active entitlement keys for this identity (e.g. `["pro"]`). */
|
|
492
|
+
entitlements: string[];
|
|
493
|
+
/** The canonical Crossdeck customer id, when resolved. */
|
|
494
|
+
crossdeckCustomerId: string | null;
|
|
495
|
+
/** The anonymous id Crossdeck resolved / assigned. */
|
|
496
|
+
anonymousId: string | null;
|
|
497
|
+
/**
|
|
498
|
+
* Set when a verified identity was sent but couldn't be checked (e.g. the auth issuer
|
|
499
|
+
* isn't registered). If you sent `userId`+`idToken` and `status` is `"anonymous"`, read this.
|
|
500
|
+
*/
|
|
501
|
+
identityError: {
|
|
502
|
+
reason: string;
|
|
503
|
+
} | null;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Owner identifiers for {@link CrossdeckServer.getOwnerStatus} — the public-page path.
|
|
507
|
+
* Pass any subset; for a page-offline check the `ip` is the owner's CREATION ip.
|
|
508
|
+
*
|
|
509
|
+
* @experimental Preview — see {@link ResolveInput}.
|
|
510
|
+
*/
|
|
511
|
+
interface OwnerStatusInput {
|
|
512
|
+
userId?: string;
|
|
513
|
+
email?: string;
|
|
514
|
+
domain?: string;
|
|
515
|
+
ip?: string;
|
|
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
|
+
}
|
|
441
552
|
/**
|
|
442
553
|
* Snapshot of one customer's last-known-good entitlements, as written
|
|
443
554
|
* to / read from a durable `EntitlementStore`. Versioned for forward-
|
|
@@ -1456,6 +1567,53 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1456
1567
|
*/
|
|
1457
1568
|
getEntitlements(hints: IdentityHints, options?: RequestOptions): Promise<EntitlementsListResponse>;
|
|
1458
1569
|
getCustomerEntitlements(customerId: string, options?: RequestOptions): Promise<EntitlementsListResponse>;
|
|
1570
|
+
/**
|
|
1571
|
+
* Resolve a user's BLOCK verdict (plus identity / entitlement context) — the same
|
|
1572
|
+
* `/v1/resolve` you'd call for entitlements, read for `blocked`. Wire it into signup
|
|
1573
|
+
* and every visit; on `blocked`, sign the user out (and, for public content, take
|
|
1574
|
+
* their pages offline — see the Blocking developer guide, Pattern B).
|
|
1575
|
+
*
|
|
1576
|
+
* **Fail-open is the contract.** This never throws for the block decision and never
|
|
1577
|
+
* returns `blocked: true` on uncertainty. Any error — Crossdeck unreachable, timeout,
|
|
1578
|
+
* unresolved identity — yields `{ blocked: false, degraded: true }`, so a glitch can
|
|
1579
|
+
* never lock out a real user.
|
|
1580
|
+
*
|
|
1581
|
+
* Forward the END USER's `ip` (you call this server-to-server, so otherwise Crossdeck
|
|
1582
|
+
* sees your server's IP and an ip-rule can't match the visitor), and the verified
|
|
1583
|
+
* identity (`userId` + `idToken`) so a `domain`/`email` rule can match the email.
|
|
1584
|
+
*
|
|
1585
|
+
* @experimental Preview — Crossdeck Trust ships with Crossdeck v2. Stable shape, not yet GA.
|
|
1586
|
+
*/
|
|
1587
|
+
resolve(input: ResolveInput, options?: RequestOptions): Promise<ResolveResult>;
|
|
1588
|
+
/**
|
|
1589
|
+
* Convenience over {@link resolve}: just the boolean. Fail-open (false on any error).
|
|
1590
|
+
*
|
|
1591
|
+
* @experimental Preview — see {@link resolve}.
|
|
1592
|
+
*/
|
|
1593
|
+
isBlocked(input: ResolveInput, options?: RequestOptions): Promise<boolean>;
|
|
1594
|
+
/**
|
|
1595
|
+
* The OWNER's block verdict for the public-page path (Pattern B / §5b of the Blocking
|
|
1596
|
+
* developer guide) — "is the owner of this page blocked?", no session token required.
|
|
1597
|
+
* Pass the owner's recorded identifiers (`userId`/`email`/`domain`/`ip`; the `ip` is
|
|
1598
|
+
* their CREATION ip, not a live visitor's). Poll on a short TTL to invalidate your
|
|
1599
|
+
* public-page cache. Fail-open — any error → `{ blocked: false, degraded: true }`.
|
|
1600
|
+
*
|
|
1601
|
+
* @experimental Preview — see {@link resolve}.
|
|
1602
|
+
*/
|
|
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>;
|
|
1459
1617
|
/**
|
|
1460
1618
|
* Synchronous entitlement check. Returns `true` iff the customer
|
|
1461
1619
|
* has the entitlement AND the cache entry is fresh (within
|
|
@@ -1935,4 +2093,4 @@ declare class CrossdeckServer extends EventEmitter {
|
|
|
1935
2093
|
private normalizeIngestEvent;
|
|
1936
2094
|
}
|
|
1937
2095
|
|
|
1938
|
-
export { type
|
|
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 };
|