@cross-deck/node 1.11.0 → 1.12.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 +22 -0
- package/README.md +25 -3
- package/dist/auto-events/index.d.mts +1 -1
- package/dist/auto-events/index.d.ts +1 -1
- package/dist/{crossdeck-server-DAQX_7rm.d.mts → crossdeck-server-CuTGwrwx.d.mts} +12 -2
- package/dist/{crossdeck-server-DAQX_7rm.d.ts → crossdeck-server-CuTGwrwx.d.ts} +12 -2
- package/dist/index.cjs +17 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +17 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.12.0] — 2026-07-01
|
|
10
|
+
|
|
11
|
+
**Added — `blockEventId`: the Crossdeck-hosted block page (v2 preview).**
|
|
12
|
+
On a block, `resolve()` and `gate()` now return `blockEventId` — the id of the
|
|
13
|
+
canonical block interstitial Crossdeck hosts for that exact verdict. The
|
|
14
|
+
recommended dead-end becomes one line:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
if (blocked && blockEventId) {
|
|
18
|
+
res.redirect(`https://api.cross-deck.com/v1/trust/page/${blockEventId}`);
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Crossdeck serves the branded "Access paused" page — a verification receipt
|
|
23
|
+
(application name, timestamp, the quotable `reference`) and a **Contact
|
|
24
|
+
support** action that mails the project owner's real recorded email — so you
|
|
25
|
+
never hand-build, fork, or drift the block screen. The id is opaque
|
|
26
|
+
(`cd_blk_…`, no PII in the URL) and present only on an explicit block;
|
|
27
|
+
fail-open behaviour is unchanged (never redirect on error/timeout).
|
|
28
|
+
`ResolveResult` and `GateVerdict` carry the new optional field — additive,
|
|
29
|
+
non-breaking. Still `@experimental` until the Crossdeck v2 launch.
|
|
30
|
+
|
|
9
31
|
## [1.11.0] — 2026-06-30
|
|
10
32
|
|
|
11
33
|
**Added — block `reference` and `supportEmail` on the trust surface (still v2 preview).**
|
package/README.md
CHANGED
|
@@ -223,16 +223,29 @@ Blocking is **entitlements, inverted**: `getEntitlements()` asks *"can this user
|
|
|
223
223
|
There are three doors. Pick by who's at it:
|
|
224
224
|
|
|
225
225
|
```ts
|
|
226
|
+
// The Crossdeck-hosted block page — the recommended dead-end for doors 1 + 2. One
|
|
227
|
+
// constant today; swaps to https://block.cross-deck.com when the alias goes live.
|
|
228
|
+
const BLOCK_PAGE = "https://api.cross-deck.com/v1/trust/page";
|
|
229
|
+
|
|
226
230
|
// 1. A user you ALREADY KNOW — a bare userId rides the backbone (no token, no setup).
|
|
227
231
|
// Crossdeck matches on the email it already holds. The common case.
|
|
228
|
-
const { blocked,
|
|
229
|
-
if (blocked) {
|
|
232
|
+
const { blocked, blockEventId } = await crossdeck.resolve({ userId, ip: req.ip });
|
|
233
|
+
if (blocked) {
|
|
234
|
+
await signOut();
|
|
235
|
+
return blockEventId
|
|
236
|
+
? res.redirect(`${BLOCK_PAGE}/${blockEventId}`) // Crossdeck serves the branded page
|
|
237
|
+
: res.redirect("/suspended"); // rare: mint unavailable → your fallback
|
|
238
|
+
}
|
|
230
239
|
// or just the boolean: if (await crossdeck.isBlocked({ userId })) …
|
|
231
240
|
|
|
232
241
|
// 2. A BRAND-NEW signup Crossdeck has never seen — block by the two strings you have
|
|
233
242
|
// at signup, their email + ip. Call it BEFORE you create the account.
|
|
234
243
|
const gate = await crossdeck.gate({ email, ip: req.ip });
|
|
235
|
-
if (gate.action === "block")
|
|
244
|
+
if (gate.action === "block") {
|
|
245
|
+
return gate.blockEventId
|
|
246
|
+
? res.redirect(`${BLOCK_PAGE}/${gate.blockEventId}`)
|
|
247
|
+
: rejectSignup(gate.blockReason);
|
|
248
|
+
}
|
|
236
249
|
|
|
237
250
|
// 3. A PUBLIC PAGE — "is the owner of this page blocked?" (no session token). Poll on a
|
|
238
251
|
// short TTL to invalidate your cache; the ip is the owner's CREATION ip.
|
|
@@ -240,6 +253,15 @@ const owner = await crossdeck.getOwnerStatus({ userId: ownerId });
|
|
|
240
253
|
if (owner.blocked) await takePageOffline(ownerId);
|
|
241
254
|
```
|
|
242
255
|
|
|
256
|
+
**Why redirect instead of building your own screen?** Crossdeck hosts the canonical block
|
|
257
|
+
interstitial — "Access paused", a verification receipt (application name, timestamp, an
|
|
258
|
+
opaque `reference` the person can quote to support), and a **Contact support** action that
|
|
259
|
+
mails the project owner's real recorded email. One page, centrally maintained, updated for
|
|
260
|
+
every integrator at once — you never fork it, restyle it, or let it drift. Redirect **only
|
|
261
|
+
on an explicit block** (`blocked === true` / `action === "block"`): on error or timeout the
|
|
262
|
+
SDK fails open and you let the user through — never send a real user to a block page over
|
|
263
|
+
a network blip.
|
|
264
|
+
|
|
243
265
|
**Recommended server pattern (no issuer registration):** if your backend runs Firebase
|
|
244
266
|
Admin, verify the ID token **locally** for the authoritative uid, then send that as a bare
|
|
245
267
|
`userId` — you keep the cryptographic proof on your side and Crossdeck needs zero setup:
|
|
@@ -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.12.0";
|
|
187
|
+
readonly bundledIn: "@cross-deck/node@1.12.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
|
|
@@ -484,6 +484,14 @@ interface BlockVerdict {
|
|
|
484
484
|
* real operator by default. Override with a dedicated address if you have one.
|
|
485
485
|
*/
|
|
486
486
|
supportEmail?: string | null;
|
|
487
|
+
/**
|
|
488
|
+
* The id of the CD-hosted block interstitial minted for THIS verdict — present only when
|
|
489
|
+
* blocked. The recommended dead-end is one line:
|
|
490
|
+
* `redirect(\`https://api.cross-deck.com/v1/trust/page/${blockEventId}\`)` — Crossdeck
|
|
491
|
+
* serves the canonical branded page (receipt + reference + faithful support contact), so
|
|
492
|
+
* you never hand-build or fork the block screen. Opaque (`cd_blk_…`), no PII in the URL.
|
|
493
|
+
*/
|
|
494
|
+
blockEventId?: string | null;
|
|
487
495
|
/**
|
|
488
496
|
* True when this is the fail-open default (Crossdeck unreachable / identity unresolved).
|
|
489
497
|
* `blocked` is `false`; log it for observability, never treat it as a real "not blocked".
|
|
@@ -562,6 +570,8 @@ interface GateVerdict {
|
|
|
562
570
|
reference?: string | null;
|
|
563
571
|
/** Recorded support contact (project owner's signup email), present only on `block` — the blocked screen's "Contact support" default. */
|
|
564
572
|
supportEmail?: string | null;
|
|
573
|
+
/** CD-hosted block-page id (`cd_blk_…`), present only on `block` — redirect the rejected signup to `https://api.cross-deck.com/v1/trust/page/${blockEventId}` and Crossdeck serves the canonical branded dead-end. */
|
|
574
|
+
blockEventId?: string | null;
|
|
565
575
|
/** True when this is the fail-open default (Crossdeck unreachable). `allow` is `true`. */
|
|
566
576
|
degraded?: boolean;
|
|
567
577
|
}
|
|
@@ -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.12.0";
|
|
187
|
+
readonly bundledIn: "@cross-deck/node@1.12.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
|
|
@@ -484,6 +484,14 @@ interface BlockVerdict {
|
|
|
484
484
|
* real operator by default. Override with a dedicated address if you have one.
|
|
485
485
|
*/
|
|
486
486
|
supportEmail?: string | null;
|
|
487
|
+
/**
|
|
488
|
+
* The id of the CD-hosted block interstitial minted for THIS verdict — present only when
|
|
489
|
+
* blocked. The recommended dead-end is one line:
|
|
490
|
+
* `redirect(\`https://api.cross-deck.com/v1/trust/page/${blockEventId}\`)` — Crossdeck
|
|
491
|
+
* serves the canonical branded page (receipt + reference + faithful support contact), so
|
|
492
|
+
* you never hand-build or fork the block screen. Opaque (`cd_blk_…`), no PII in the URL.
|
|
493
|
+
*/
|
|
494
|
+
blockEventId?: string | null;
|
|
487
495
|
/**
|
|
488
496
|
* True when this is the fail-open default (Crossdeck unreachable / identity unresolved).
|
|
489
497
|
* `blocked` is `false`; log it for observability, never treat it as a real "not blocked".
|
|
@@ -562,6 +570,8 @@ interface GateVerdict {
|
|
|
562
570
|
reference?: string | null;
|
|
563
571
|
/** Recorded support contact (project owner's signup email), present only on `block` — the blocked screen's "Contact support" default. */
|
|
564
572
|
supportEmail?: string | null;
|
|
573
|
+
/** CD-hosted block-page id (`cd_blk_…`), present only on `block` — redirect the rejected signup to `https://api.cross-deck.com/v1/trust/page/${blockEventId}` and Crossdeck serves the canonical branded dead-end. */
|
|
574
|
+
blockEventId?: string | null;
|
|
565
575
|
/** True when this is the fail-open default (Crossdeck unreachable). `allow` is `true`. */
|
|
566
576
|
degraded?: boolean;
|
|
567
577
|
}
|
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.12.0";
|
|
391
391
|
var SDK_NAME = "@cross-deck/node";
|
|
392
392
|
|
|
393
393
|
// src/_diagnostic-telemetry.ts
|
|
@@ -3209,6 +3209,7 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
|
|
|
3209
3209
|
blockedKey: raw.blockedKey ?? null,
|
|
3210
3210
|
reference: raw.reference ?? null,
|
|
3211
3211
|
supportEmail: raw.supportEmail ?? null,
|
|
3212
|
+
blockEventId: raw.blockEventId ?? null,
|
|
3212
3213
|
status: typeof raw.status === "string" ? raw.status : "active",
|
|
3213
3214
|
entitlements: Array.isArray(raw.entitlements) ? raw.entitlements.filter((e) => typeof e === "string") : [],
|
|
3214
3215
|
crossdeckCustomerId: raw.crossdeckCustomerId ?? raw.user?.id ?? null,
|
|
@@ -3320,7 +3321,8 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
|
|
|
3320
3321
|
blockReason: raw.blockReason ?? null,
|
|
3321
3322
|
blockedKey: raw.blockedKey ?? null,
|
|
3322
3323
|
reference: raw.reference ?? null,
|
|
3323
|
-
supportEmail: raw.supportEmail ?? null
|
|
3324
|
+
supportEmail: raw.supportEmail ?? null,
|
|
3325
|
+
blockEventId: raw.blockEventId ?? null
|
|
3324
3326
|
};
|
|
3325
3327
|
} catch (err) {
|
|
3326
3328
|
this.debug.emit(
|
|
@@ -4778,8 +4780,8 @@ function normaliseSecrets(input) {
|
|
|
4778
4780
|
}
|
|
4779
4781
|
|
|
4780
4782
|
// src/_contracts-bundled.ts
|
|
4781
|
-
var BUNDLED_IN = "@cross-deck/node@1.
|
|
4782
|
-
var SDK_VERSION2 = "1.
|
|
4783
|
+
var BUNDLED_IN = "@cross-deck/node@1.12.0";
|
|
4784
|
+
var SDK_VERSION2 = "1.12.0";
|
|
4783
4785
|
var BUNDLED_CONTRACTS = Object.freeze([
|
|
4784
4786
|
{
|
|
4785
4787
|
"id": "contract-failed-payload-schema-lock",
|
|
@@ -4901,7 +4903,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4901
4903
|
"legal/security/index.html#diagnostic",
|
|
4902
4904
|
"legal/sdk-data/index.html#b-diagnostic"
|
|
4903
4905
|
],
|
|
4904
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4906
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
4905
4907
|
},
|
|
4906
4908
|
{
|
|
4907
4909
|
"id": "documentation-honesty",
|
|
@@ -4933,7 +4935,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4933
4935
|
],
|
|
4934
4936
|
"registeredAt": "2026-05-26",
|
|
4935
4937
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.1",
|
|
4936
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4938
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
4937
4939
|
},
|
|
4938
4940
|
{
|
|
4939
4941
|
"id": "error-envelope-shape",
|
|
@@ -4972,7 +4974,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4972
4974
|
],
|
|
4973
4975
|
"registeredAt": "2026-05-26",
|
|
4974
4976
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
|
|
4975
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4977
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
4976
4978
|
},
|
|
4977
4979
|
{
|
|
4978
4980
|
"id": "flush-interval-parity",
|
|
@@ -5017,7 +5019,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5017
5019
|
],
|
|
5018
5020
|
"registeredAt": "2026-05-26",
|
|
5019
5021
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
|
|
5020
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5022
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
5021
5023
|
},
|
|
5022
5024
|
{
|
|
5023
5025
|
"id": "idempotency-key-deterministic",
|
|
@@ -5122,7 +5124,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5122
5124
|
],
|
|
5123
5125
|
"registeredAt": "2026-05-26",
|
|
5124
5126
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
|
|
5125
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5127
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
5126
5128
|
},
|
|
5127
5129
|
{
|
|
5128
5130
|
"id": "invalid-input-rejected-natively",
|
|
@@ -5178,7 +5180,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5178
5180
|
],
|
|
5179
5181
|
"registeredAt": "2026-06-11",
|
|
5180
5182
|
"firstRegisteredIn": "swift trap-on-input class fix \u2014 first machine-tested Swift release",
|
|
5181
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5183
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
5182
5184
|
},
|
|
5183
5185
|
{
|
|
5184
5186
|
"id": "node-pii-scrubber",
|
|
@@ -5217,7 +5219,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5217
5219
|
],
|
|
5218
5220
|
"registeredAt": "2026-05-26",
|
|
5219
5221
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.1",
|
|
5220
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5222
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
5221
5223
|
},
|
|
5222
5224
|
{
|
|
5223
5225
|
"id": "node-shutdown-awaits-flush",
|
|
@@ -5250,7 +5252,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5250
5252
|
],
|
|
5251
5253
|
"registeredAt": "2026-05-26",
|
|
5252
5254
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.4",
|
|
5253
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5255
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
5254
5256
|
},
|
|
5255
5257
|
{
|
|
5256
5258
|
"id": "sdk-error-codes-catalogue",
|
|
@@ -5295,7 +5297,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5295
5297
|
],
|
|
5296
5298
|
"registeredAt": "2026-05-26",
|
|
5297
5299
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
|
|
5298
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5300
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
5299
5301
|
},
|
|
5300
5302
|
{
|
|
5301
5303
|
"id": "sync-purchases-funnel-parity",
|
|
@@ -5328,7 +5330,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5328
5330
|
],
|
|
5329
5331
|
"registeredAt": "2026-05-26",
|
|
5330
5332
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
|
|
5331
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5333
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
5332
5334
|
},
|
|
5333
5335
|
{
|
|
5334
5336
|
"id": "verifier-timestamp-mandatory",
|
|
@@ -5382,7 +5384,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5382
5384
|
],
|
|
5383
5385
|
"registeredAt": "2026-05-26",
|
|
5384
5386
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.2",
|
|
5385
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5387
|
+
"bundledIn": "@cross-deck/node@1.12.0"
|
|
5386
5388
|
}
|
|
5387
5389
|
]);
|
|
5388
5390
|
|