@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/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.9.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.8.2";
4600
- var SDK_VERSION2 = "1.8.2";
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.8.2"
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.8.2"
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.8.2"
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.8.2"
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.8.2"
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.8.2"
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.8.2"
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.8.2"
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.8.2"
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.8.2"
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.8.2"
5381
+ "bundledIn": "@cross-deck/node@1.10.1"
5204
5382
  }
5205
5383
  ]);
5206
5384