@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/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.10.1";
|
|
391
391
|
var SDK_NAME = "@cross-deck/node";
|
|
392
392
|
|
|
393
393
|
// src/_diagnostic-telemetry.ts
|
|
@@ -3149,6 +3149,184 @@ var CrossdeckServer = class extends import_node_events.EventEmitter {
|
|
|
3149
3149
|
this.populateEntitlementCache({ customerId }, response);
|
|
3150
3150
|
return response;
|
|
3151
3151
|
}
|
|
3152
|
+
// ── Blocking — "entitlements, inverted" (Crossdeck Trust, v2 preview) ───────
|
|
3153
|
+
//
|
|
3154
|
+
// getEntitlements() answers "can they access Pro?"; resolve() answers "should
|
|
3155
|
+
// they be here at all?". Same call, opposite question. Both read Crossdeck live.
|
|
3156
|
+
// The safety inversion: entitlements fail CLOSED (protect revenue); blocking
|
|
3157
|
+
// fails OPEN (protect real users) — these methods NEVER throw for the verdict
|
|
3158
|
+
// and NEVER return blocked:true on uncertainty.
|
|
3159
|
+
/**
|
|
3160
|
+
* Resolve a user's BLOCK verdict (plus identity / entitlement context) — the same
|
|
3161
|
+
* `/v1/resolve` you'd call for entitlements, read for `blocked`. Wire it into signup
|
|
3162
|
+
* and every visit; on `blocked`, sign the user out (and, for public content, take
|
|
3163
|
+
* their pages offline — see the Blocking developer guide, Pattern B).
|
|
3164
|
+
*
|
|
3165
|
+
* **Fail-open is the contract.** This never throws for the block decision and never
|
|
3166
|
+
* returns `blocked: true` on uncertainty. Any error — Crossdeck unreachable, timeout,
|
|
3167
|
+
* unresolved identity — yields `{ blocked: false, degraded: true }`, so a glitch can
|
|
3168
|
+
* never lock out a real user.
|
|
3169
|
+
*
|
|
3170
|
+
* Forward the END USER's `ip` (you call this server-to-server, so otherwise Crossdeck
|
|
3171
|
+
* sees your server's IP and an ip-rule can't match the visitor), and the verified
|
|
3172
|
+
* identity (`userId` + `idToken`) so a `domain`/`email` rule can match the email.
|
|
3173
|
+
*
|
|
3174
|
+
* @experimental Preview — Crossdeck Trust ships with Crossdeck v2. Stable shape, not yet GA.
|
|
3175
|
+
*/
|
|
3176
|
+
async resolve(input, options) {
|
|
3177
|
+
const failOpen = () => ({
|
|
3178
|
+
blocked: false,
|
|
3179
|
+
blockReason: null,
|
|
3180
|
+
blockedKey: null,
|
|
3181
|
+
degraded: true,
|
|
3182
|
+
status: "anonymous",
|
|
3183
|
+
entitlements: [],
|
|
3184
|
+
crossdeckCustomerId: null,
|
|
3185
|
+
anonymousId: input?.anonymousId ?? null,
|
|
3186
|
+
identityError: null
|
|
3187
|
+
});
|
|
3188
|
+
if (!input || !input.anonymousId && !input.userId) {
|
|
3189
|
+
this.debug.emit(
|
|
3190
|
+
"sdk.resolve_missing_identity",
|
|
3191
|
+
"resolve() needs an anonymousId or a userId; returning fail-open blocked:false."
|
|
3192
|
+
);
|
|
3193
|
+
return failOpen();
|
|
3194
|
+
}
|
|
3195
|
+
try {
|
|
3196
|
+
const raw = await this.http.request("POST", "/resolve", {
|
|
3197
|
+
body: {
|
|
3198
|
+
...input.userId ? { userId: input.userId } : {},
|
|
3199
|
+
...input.idToken ? { idToken: input.idToken } : {},
|
|
3200
|
+
...input.anonymousId ? { anonymousId: input.anonymousId } : {},
|
|
3201
|
+
...input.ip ? { ip: input.ip } : {}
|
|
3202
|
+
},
|
|
3203
|
+
signal: options?.signal,
|
|
3204
|
+
timeoutMs: options?.timeoutMs
|
|
3205
|
+
});
|
|
3206
|
+
return {
|
|
3207
|
+
blocked: raw.blocked === true,
|
|
3208
|
+
blockReason: raw.blockReason ?? null,
|
|
3209
|
+
blockedKey: raw.blockedKey ?? null,
|
|
3210
|
+
status: typeof raw.status === "string" ? raw.status : "active",
|
|
3211
|
+
entitlements: Array.isArray(raw.entitlements) ? raw.entitlements.filter((e) => typeof e === "string") : [],
|
|
3212
|
+
crossdeckCustomerId: raw.crossdeckCustomerId ?? raw.user?.id ?? null,
|
|
3213
|
+
anonymousId: raw.anonymousId ?? input.anonymousId ?? null,
|
|
3214
|
+
identityError: raw.identityError ?? null
|
|
3215
|
+
};
|
|
3216
|
+
} catch (err) {
|
|
3217
|
+
this.debug.emit(
|
|
3218
|
+
"sdk.resolve_failed",
|
|
3219
|
+
`resolve() failed \u2014 fail-open blocked:false. ${err instanceof Error ? err.message : String(err)}`,
|
|
3220
|
+
{ error: err instanceof Error ? err.message : String(err) }
|
|
3221
|
+
);
|
|
3222
|
+
return failOpen();
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
/**
|
|
3226
|
+
* Convenience over {@link resolve}: just the boolean. Fail-open (false on any error).
|
|
3227
|
+
*
|
|
3228
|
+
* @experimental Preview — see {@link resolve}.
|
|
3229
|
+
*/
|
|
3230
|
+
async isBlocked(input, options) {
|
|
3231
|
+
return (await this.resolve(input, options)).blocked;
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* The OWNER's block verdict for the public-page path (Pattern B / §5b of the Blocking
|
|
3235
|
+
* developer guide) — "is the owner of this page blocked?", no session token required.
|
|
3236
|
+
* Pass the owner's recorded identifiers (`userId`/`email`/`domain`/`ip`; the `ip` is
|
|
3237
|
+
* their CREATION ip, not a live visitor's). Poll on a short TTL to invalidate your
|
|
3238
|
+
* public-page cache. Fail-open — any error → `{ blocked: false, degraded: true }`.
|
|
3239
|
+
*
|
|
3240
|
+
* @experimental Preview — see {@link resolve}.
|
|
3241
|
+
*/
|
|
3242
|
+
async getOwnerStatus(input, options) {
|
|
3243
|
+
const query = {
|
|
3244
|
+
userId: input?.userId || void 0,
|
|
3245
|
+
email: input?.email || void 0,
|
|
3246
|
+
domain: input?.domain || void 0,
|
|
3247
|
+
ip: input?.ip || void 0
|
|
3248
|
+
};
|
|
3249
|
+
if (!query.userId && !query.email && !query.domain && !query.ip) {
|
|
3250
|
+
this.debug.emit(
|
|
3251
|
+
"sdk.owner_status_missing_identity",
|
|
3252
|
+
"getOwnerStatus() needs at least one of userId/email/domain/ip; returning fail-open blocked:false."
|
|
3253
|
+
);
|
|
3254
|
+
return { blocked: false, blockReason: null, blockedKey: null, degraded: true };
|
|
3255
|
+
}
|
|
3256
|
+
try {
|
|
3257
|
+
const raw = await this.http.request("GET", "/trust/status", {
|
|
3258
|
+
query,
|
|
3259
|
+
signal: options?.signal,
|
|
3260
|
+
timeoutMs: options?.timeoutMs
|
|
3261
|
+
});
|
|
3262
|
+
return {
|
|
3263
|
+
blocked: raw.blocked === true,
|
|
3264
|
+
blockReason: raw.blockReason ?? null,
|
|
3265
|
+
blockedKey: raw.blockedKey ?? null
|
|
3266
|
+
};
|
|
3267
|
+
} catch (err) {
|
|
3268
|
+
this.debug.emit(
|
|
3269
|
+
"sdk.owner_status_failed",
|
|
3270
|
+
`getOwnerStatus() failed \u2014 fail-open blocked:false. ${err instanceof Error ? err.message : String(err)}`,
|
|
3271
|
+
{ error: err instanceof Error ? err.message : String(err) }
|
|
3272
|
+
);
|
|
3273
|
+
return { blocked: false, blockReason: null, blockedKey: null, degraded: true };
|
|
3274
|
+
}
|
|
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
|
+
}
|
|
3152
3330
|
/**
|
|
3153
3331
|
* Synchronous entitlement check. Returns `true` iff the customer
|
|
3154
3332
|
* has the entitlement AND the cache entry is fresh (within
|
|
@@ -4596,8 +4774,8 @@ function normaliseSecrets(input) {
|
|
|
4596
4774
|
}
|
|
4597
4775
|
|
|
4598
4776
|
// src/_contracts-bundled.ts
|
|
4599
|
-
var BUNDLED_IN = "@cross-deck/node@1.
|
|
4600
|
-
var SDK_VERSION2 = "1.
|
|
4777
|
+
var BUNDLED_IN = "@cross-deck/node@1.10.1";
|
|
4778
|
+
var SDK_VERSION2 = "1.10.1";
|
|
4601
4779
|
var BUNDLED_CONTRACTS = Object.freeze([
|
|
4602
4780
|
{
|
|
4603
4781
|
"id": "contract-failed-payload-schema-lock",
|
|
@@ -4719,7 +4897,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4719
4897
|
"legal/security/index.html#diagnostic",
|
|
4720
4898
|
"legal/sdk-data/index.html#b-diagnostic"
|
|
4721
4899
|
],
|
|
4722
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4900
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
4723
4901
|
},
|
|
4724
4902
|
{
|
|
4725
4903
|
"id": "documentation-honesty",
|
|
@@ -4751,7 +4929,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4751
4929
|
],
|
|
4752
4930
|
"registeredAt": "2026-05-26",
|
|
4753
4931
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.1",
|
|
4754
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4932
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
4755
4933
|
},
|
|
4756
4934
|
{
|
|
4757
4935
|
"id": "error-envelope-shape",
|
|
@@ -4790,7 +4968,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4790
4968
|
],
|
|
4791
4969
|
"registeredAt": "2026-05-26",
|
|
4792
4970
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
|
|
4793
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4971
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
4794
4972
|
},
|
|
4795
4973
|
{
|
|
4796
4974
|
"id": "flush-interval-parity",
|
|
@@ -4835,7 +5013,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4835
5013
|
],
|
|
4836
5014
|
"registeredAt": "2026-05-26",
|
|
4837
5015
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
|
|
4838
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5016
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
4839
5017
|
},
|
|
4840
5018
|
{
|
|
4841
5019
|
"id": "idempotency-key-deterministic",
|
|
@@ -4940,7 +5118,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4940
5118
|
],
|
|
4941
5119
|
"registeredAt": "2026-05-26",
|
|
4942
5120
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
|
|
4943
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5121
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
4944
5122
|
},
|
|
4945
5123
|
{
|
|
4946
5124
|
"id": "invalid-input-rejected-natively",
|
|
@@ -4996,7 +5174,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4996
5174
|
],
|
|
4997
5175
|
"registeredAt": "2026-06-11",
|
|
4998
5176
|
"firstRegisteredIn": "swift trap-on-input class fix \u2014 first machine-tested Swift release",
|
|
4999
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5177
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
5000
5178
|
},
|
|
5001
5179
|
{
|
|
5002
5180
|
"id": "node-pii-scrubber",
|
|
@@ -5035,7 +5213,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5035
5213
|
],
|
|
5036
5214
|
"registeredAt": "2026-05-26",
|
|
5037
5215
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.1",
|
|
5038
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5216
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
5039
5217
|
},
|
|
5040
5218
|
{
|
|
5041
5219
|
"id": "node-shutdown-awaits-flush",
|
|
@@ -5068,7 +5246,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5068
5246
|
],
|
|
5069
5247
|
"registeredAt": "2026-05-26",
|
|
5070
5248
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.4",
|
|
5071
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5249
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
5072
5250
|
},
|
|
5073
5251
|
{
|
|
5074
5252
|
"id": "sdk-error-codes-catalogue",
|
|
@@ -5113,7 +5291,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5113
5291
|
],
|
|
5114
5292
|
"registeredAt": "2026-05-26",
|
|
5115
5293
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
|
|
5116
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5294
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
5117
5295
|
},
|
|
5118
5296
|
{
|
|
5119
5297
|
"id": "sync-purchases-funnel-parity",
|
|
@@ -5146,7 +5324,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5146
5324
|
],
|
|
5147
5325
|
"registeredAt": "2026-05-26",
|
|
5148
5326
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
|
|
5149
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5327
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
5150
5328
|
},
|
|
5151
5329
|
{
|
|
5152
5330
|
"id": "verifier-timestamp-mandatory",
|
|
@@ -5200,7 +5378,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5200
5378
|
],
|
|
5201
5379
|
"registeredAt": "2026-05-26",
|
|
5202
5380
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.2",
|
|
5203
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5381
|
+
"bundledIn": "@cross-deck/node@1.10.1"
|
|
5204
5382
|
}
|
|
5205
5383
|
]);
|
|
5206
5384
|
|