@cross-deck/node 1.6.0 → 1.7.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/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AliasIdentityInput, a as AliasResult, b as AuditDecision, c as AuditEntry, B as Breadcrumb, d as BreadcrumbCategory, e as BreadcrumbLevel, C as CROSSDECK_API_VERSION, f as CapturedError, g as Contract, h as ContractAppliesTo, i as ContractFailureInput, j as ContractPillar, k as ContractStatus, l as ContractTestRef, m as CrossdeckAuthenticationError, n as CrossdeckConfigurationError, o as CrossdeckContracts, p as CrossdeckError, q as CrossdeckErrorPayload, r as CrossdeckErrorType, s as CrossdeckInternalError, t as CrossdeckNetworkError, u as CrossdeckPermissionError, v as CrossdeckRateLimitError, w as CrossdeckServer, x as CrossdeckServerOptions, y as CrossdeckValidationError, D as DEFAULT_BASE_URL, z as DEFAULT_TIMEOUT_MS, E as Diagnostics, F as EntitlementCacheOptions, G as EntitlementMutationResult, H as EntitlementStore, I as EntitlementsListResponse, J as EntitlementsListener, K as Environment, L as ErrorCaptureConfig, M as ErrorLevel, N as EventProperties, O as ForgetResult, P as GrantDuration, Q as GrantEntitlementInput, R as GroupMembership, S as HeartbeatResponse, T as HttpRequestInfo, U as HttpResponseInfo, V as HttpRetriesConfig, W as IdentifyOptions, X as IdentityHints, Y as IngestOptions, Z as IngestResponse, _ as PublicEntitlement, $ as PurchaseResult, a0 as RequestOptions, a1 as RevokeEntitlementInput, a2 as RuntimeHost, a3 as RuntimeInfo, a4 as ServerEvent, a5 as StackFrame, a6 as StoredEntitlements, a7 as SyncPurchaseInput, a8 as makeCrossdeckError } from './crossdeck-server-C1Ue0rv4.mjs';
1
+ export { A as AliasIdentityInput, a as AliasResult, b as AuditDecision, c as AuditEntry, B as Breadcrumb, d as BreadcrumbCategory, e as BreadcrumbLevel, C as CROSSDECK_API_VERSION, f as CapturedError, g as Contract, h as ContractAppliesTo, i as ContractFailureInput, j as ContractPillar, k as ContractStatus, l as ContractTestRef, m as CrossdeckAuthenticationError, n as CrossdeckConfigurationError, o as CrossdeckContracts, p as CrossdeckError, q as CrossdeckErrorPayload, r as CrossdeckErrorType, s as CrossdeckInternalError, t as CrossdeckNetworkError, u as CrossdeckPermissionError, v as CrossdeckRateLimitError, w as CrossdeckServer, x as CrossdeckServerOptions, y as CrossdeckValidationError, D as DEFAULT_BASE_URL, z as DEFAULT_TIMEOUT_MS, E as Diagnostics, F as EntitlementCacheOptions, G as EntitlementMutationResult, H as EntitlementStore, I as EntitlementsListResponse, J as EntitlementsListener, K as Environment, L as ErrorCaptureConfig, M as ErrorLevel, N as EventProperties, O as ForgetResult, P as GrantDuration, Q as GrantEntitlementInput, R as GroupMembership, S as HeartbeatResponse, T as HttpRequestInfo, U as HttpResponseInfo, V as HttpRetriesConfig, W as IdentifyOptions, X as IdentityHints, Y as IngestOptions, Z as IngestResponse, _ as PublicEntitlement, $ as PurchaseResult, a0 as RequestOptions, a1 as RevokeEntitlementInput, a2 as RuntimeHost, a3 as RuntimeInfo, a4 as ServerEvent, a5 as StackFrame, a6 as StoredEntitlements, a7 as SyncPurchaseInput, a8 as makeCrossdeckError } from './crossdeck-server-DYawt4eT.mjs';
2
2
  import 'node:events';
3
3
 
4
4
  /**
@@ -17,7 +17,7 @@ import 'node:events';
17
17
  *
18
18
  * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
19
19
  */
20
- declare const SDK_VERSION = "1.6.0";
20
+ declare const SDK_VERSION = "1.7.0";
21
21
  declare const SDK_NAME = "@cross-deck/node";
22
22
 
23
23
  /**
@@ -51,7 +51,7 @@ interface ErrorCodeEntry {
51
51
  /** The string thrown as `CrossdeckError.code`. */
52
52
  code: string;
53
53
  /** Broad category — `CrossdeckError.type`. */
54
- type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
54
+ type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "version_error" | "internal_error" | "network_error" | "configuration_error";
55
55
  /** One-sentence description. Surfaced verbatim in dashboards. */
56
56
  description: string;
57
57
  /** What the developer should do. Imperative phrasing. */
@@ -271,6 +271,12 @@ declare const _CROSSDECK_ERROR_CODES: readonly [{
271
271
  readonly description: "A field is present but the value failed validation.";
272
272
  readonly resolution: "Read error.message for the field + reason. SDK-managed call sites should never emit this — file a bug if you do.";
273
273
  readonly retryable: false;
274
+ }, {
275
+ readonly code: "sdk_version_unsupported";
276
+ readonly type: "version_error";
277
+ readonly description: "HTTP 426 — your installed SDK sends an event format the server no longer accepts. The data is good; only the wire dialect is too old. The SDK PARKS automatically: events are held in memory and deliver once you upgrade and restart.";
278
+ readonly resolution: "Update @cross-deck/node to at least the version in error.minVersion and restart — the held queue backfills. See https://cross-deck.com/docs/sdk-event-durability/.";
279
+ readonly retryable: false;
274
280
  }];
275
281
  /**
276
282
  * Literal union of every code documented in `CROSSDECK_ERROR_CODES`.
@@ -495,7 +501,7 @@ declare function scrubPiiFromProperties(properties: Record<string, unknown>): Re
495
501
  * - `sdk.no_durable_store`
496
502
  * - `sdk.super_property_registered`
497
503
  */
498
- type DebugSignal = "sdk.configured" | "sdk.first_event_sent" | "sdk.invalid_key" | "sdk.no_identity" | "sdk.entitlement_cache_used" | "sdk.entitlement_cache_warm" | "sdk.entitlement_cache_stale" | "sdk.entitlement_store_recovered" | "sdk.no_durable_store" | "sdk.purchase_evidence_sent" | "sdk.environment_mismatch" | "sdk.sensitive_property_warning" | "sdk.property_coerced" | "sdk.flush_retry_scheduled" | "sdk.flush_permanent_failure" | "sdk.flush_on_exit_started" | "sdk.flush_on_exit_completed" | "sdk.webhook_verified" | "sdk.runtime_detected" | "sdk.super_property_registered" | "sdk.boot_heartbeat_failed";
504
+ type DebugSignal = "sdk.configured" | "sdk.first_event_sent" | "sdk.invalid_key" | "sdk.no_identity" | "sdk.entitlement_cache_used" | "sdk.entitlement_cache_warm" | "sdk.entitlement_cache_stale" | "sdk.entitlement_store_recovered" | "sdk.no_durable_store" | "sdk.purchase_evidence_sent" | "sdk.environment_mismatch" | "sdk.sensitive_property_warning" | "sdk.property_coerced" | "sdk.flush_retry_scheduled" | "sdk.flush_permanent_failure" | "sdk.parked" | "sdk.flush_on_exit_started" | "sdk.flush_on_exit_completed" | "sdk.webhook_verified" | "sdk.runtime_detected" | "sdk.super_property_registered" | "sdk.boot_heartbeat_failed";
499
505
  interface DebugContext {
500
506
  [key: string]: unknown;
501
507
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AliasIdentityInput, a as AliasResult, b as AuditDecision, c as AuditEntry, B as Breadcrumb, d as BreadcrumbCategory, e as BreadcrumbLevel, C as CROSSDECK_API_VERSION, f as CapturedError, g as Contract, h as ContractAppliesTo, i as ContractFailureInput, j as ContractPillar, k as ContractStatus, l as ContractTestRef, m as CrossdeckAuthenticationError, n as CrossdeckConfigurationError, o as CrossdeckContracts, p as CrossdeckError, q as CrossdeckErrorPayload, r as CrossdeckErrorType, s as CrossdeckInternalError, t as CrossdeckNetworkError, u as CrossdeckPermissionError, v as CrossdeckRateLimitError, w as CrossdeckServer, x as CrossdeckServerOptions, y as CrossdeckValidationError, D as DEFAULT_BASE_URL, z as DEFAULT_TIMEOUT_MS, E as Diagnostics, F as EntitlementCacheOptions, G as EntitlementMutationResult, H as EntitlementStore, I as EntitlementsListResponse, J as EntitlementsListener, K as Environment, L as ErrorCaptureConfig, M as ErrorLevel, N as EventProperties, O as ForgetResult, P as GrantDuration, Q as GrantEntitlementInput, R as GroupMembership, S as HeartbeatResponse, T as HttpRequestInfo, U as HttpResponseInfo, V as HttpRetriesConfig, W as IdentifyOptions, X as IdentityHints, Y as IngestOptions, Z as IngestResponse, _ as PublicEntitlement, $ as PurchaseResult, a0 as RequestOptions, a1 as RevokeEntitlementInput, a2 as RuntimeHost, a3 as RuntimeInfo, a4 as ServerEvent, a5 as StackFrame, a6 as StoredEntitlements, a7 as SyncPurchaseInput, a8 as makeCrossdeckError } from './crossdeck-server-C1Ue0rv4.js';
1
+ export { A as AliasIdentityInput, a as AliasResult, b as AuditDecision, c as AuditEntry, B as Breadcrumb, d as BreadcrumbCategory, e as BreadcrumbLevel, C as CROSSDECK_API_VERSION, f as CapturedError, g as Contract, h as ContractAppliesTo, i as ContractFailureInput, j as ContractPillar, k as ContractStatus, l as ContractTestRef, m as CrossdeckAuthenticationError, n as CrossdeckConfigurationError, o as CrossdeckContracts, p as CrossdeckError, q as CrossdeckErrorPayload, r as CrossdeckErrorType, s as CrossdeckInternalError, t as CrossdeckNetworkError, u as CrossdeckPermissionError, v as CrossdeckRateLimitError, w as CrossdeckServer, x as CrossdeckServerOptions, y as CrossdeckValidationError, D as DEFAULT_BASE_URL, z as DEFAULT_TIMEOUT_MS, E as Diagnostics, F as EntitlementCacheOptions, G as EntitlementMutationResult, H as EntitlementStore, I as EntitlementsListResponse, J as EntitlementsListener, K as Environment, L as ErrorCaptureConfig, M as ErrorLevel, N as EventProperties, O as ForgetResult, P as GrantDuration, Q as GrantEntitlementInput, R as GroupMembership, S as HeartbeatResponse, T as HttpRequestInfo, U as HttpResponseInfo, V as HttpRetriesConfig, W as IdentifyOptions, X as IdentityHints, Y as IngestOptions, Z as IngestResponse, _ as PublicEntitlement, $ as PurchaseResult, a0 as RequestOptions, a1 as RevokeEntitlementInput, a2 as RuntimeHost, a3 as RuntimeInfo, a4 as ServerEvent, a5 as StackFrame, a6 as StoredEntitlements, a7 as SyncPurchaseInput, a8 as makeCrossdeckError } from './crossdeck-server-DYawt4eT.js';
2
2
  import 'node:events';
3
3
 
4
4
  /**
@@ -17,7 +17,7 @@ import 'node:events';
17
17
  *
18
18
  * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
19
19
  */
20
- declare const SDK_VERSION = "1.6.0";
20
+ declare const SDK_VERSION = "1.7.0";
21
21
  declare const SDK_NAME = "@cross-deck/node";
22
22
 
23
23
  /**
@@ -51,7 +51,7 @@ interface ErrorCodeEntry {
51
51
  /** The string thrown as `CrossdeckError.code`. */
52
52
  code: string;
53
53
  /** Broad category — `CrossdeckError.type`. */
54
- type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
54
+ type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "version_error" | "internal_error" | "network_error" | "configuration_error";
55
55
  /** One-sentence description. Surfaced verbatim in dashboards. */
56
56
  description: string;
57
57
  /** What the developer should do. Imperative phrasing. */
@@ -271,6 +271,12 @@ declare const _CROSSDECK_ERROR_CODES: readonly [{
271
271
  readonly description: "A field is present but the value failed validation.";
272
272
  readonly resolution: "Read error.message for the field + reason. SDK-managed call sites should never emit this — file a bug if you do.";
273
273
  readonly retryable: false;
274
+ }, {
275
+ readonly code: "sdk_version_unsupported";
276
+ readonly type: "version_error";
277
+ readonly description: "HTTP 426 — your installed SDK sends an event format the server no longer accepts. The data is good; only the wire dialect is too old. The SDK PARKS automatically: events are held in memory and deliver once you upgrade and restart.";
278
+ readonly resolution: "Update @cross-deck/node to at least the version in error.minVersion and restart — the held queue backfills. See https://cross-deck.com/docs/sdk-event-durability/.";
279
+ readonly retryable: false;
274
280
  }];
275
281
  /**
276
282
  * Literal union of every code documented in `CROSSDECK_ERROR_CODES`.
@@ -495,7 +501,7 @@ declare function scrubPiiFromProperties(properties: Record<string, unknown>): Re
495
501
  * - `sdk.no_durable_store`
496
502
  * - `sdk.super_property_registered`
497
503
  */
498
- type DebugSignal = "sdk.configured" | "sdk.first_event_sent" | "sdk.invalid_key" | "sdk.no_identity" | "sdk.entitlement_cache_used" | "sdk.entitlement_cache_warm" | "sdk.entitlement_cache_stale" | "sdk.entitlement_store_recovered" | "sdk.no_durable_store" | "sdk.purchase_evidence_sent" | "sdk.environment_mismatch" | "sdk.sensitive_property_warning" | "sdk.property_coerced" | "sdk.flush_retry_scheduled" | "sdk.flush_permanent_failure" | "sdk.flush_on_exit_started" | "sdk.flush_on_exit_completed" | "sdk.webhook_verified" | "sdk.runtime_detected" | "sdk.super_property_registered" | "sdk.boot_heartbeat_failed";
504
+ type DebugSignal = "sdk.configured" | "sdk.first_event_sent" | "sdk.invalid_key" | "sdk.no_identity" | "sdk.entitlement_cache_used" | "sdk.entitlement_cache_warm" | "sdk.entitlement_cache_stale" | "sdk.entitlement_store_recovered" | "sdk.no_durable_store" | "sdk.purchase_evidence_sent" | "sdk.environment_mismatch" | "sdk.sensitive_property_warning" | "sdk.property_coerced" | "sdk.flush_retry_scheduled" | "sdk.flush_permanent_failure" | "sdk.parked" | "sdk.flush_on_exit_started" | "sdk.flush_on_exit_completed" | "sdk.webhook_verified" | "sdk.runtime_detected" | "sdk.super_property_registered" | "sdk.boot_heartbeat_failed";
499
505
  interface DebugContext {
500
506
  [key: string]: unknown;
501
507
  }
package/dist/index.mjs CHANGED
@@ -8,6 +8,8 @@ var CrossdeckError = class _CrossdeckError extends Error {
8
8
  requestId;
9
9
  status;
10
10
  retryAfterMs;
11
+ minVersion;
12
+ surface;
11
13
  constructor(payload) {
12
14
  super(payload.message);
13
15
  this.name = "CrossdeckError";
@@ -16,6 +18,8 @@ var CrossdeckError = class _CrossdeckError extends Error {
16
18
  this.requestId = payload.requestId;
17
19
  this.status = payload.status;
18
20
  this.retryAfterMs = payload.retryAfterMs;
21
+ this.minVersion = payload.minVersion;
22
+ this.surface = payload.surface;
19
23
  Object.setPrototypeOf(this, _CrossdeckError.prototype);
20
24
  }
21
25
  /**
@@ -37,6 +41,8 @@ var CrossdeckError = class _CrossdeckError extends Error {
37
41
  requestId: this.requestId,
38
42
  status: this.status,
39
43
  retryAfterMs: this.retryAfterMs,
44
+ minVersion: this.minVersion,
45
+ surface: this.surface,
40
46
  stack: this.stack
41
47
  };
42
48
  }
@@ -127,7 +133,10 @@ async function crossdeckErrorFromResponse(res) {
127
133
  message: envelope.message ?? `HTTP ${res.status}`,
128
134
  requestId: envelope.request_id ?? requestId,
129
135
  status: res.status,
130
- retryAfterMs
136
+ retryAfterMs,
137
+ // PARK metadata, present only on a 426 / sdk_version_unsupported body.
138
+ minVersion: typeof envelope.minVersion === "string" ? envelope.minVersion : void 0,
139
+ surface: typeof envelope.surface === "string" ? envelope.surface : void 0
131
140
  });
132
141
  }
133
142
  return makeCrossdeckError({
@@ -320,7 +329,7 @@ function byteLength(s) {
320
329
  import * as https from "https";
321
330
 
322
331
  // src/_version.ts
323
- var SDK_VERSION = "1.6.0";
332
+ var SDK_VERSION = "1.7.0";
324
333
  var SDK_NAME = "@cross-deck/node";
325
334
 
326
335
  // src/_diagnostic-telemetry.ts
@@ -340,7 +349,12 @@ var DIAGNOSTIC_TELEMETRY_ALLOWED_KEYS = /* @__PURE__ */ new Set([
340
349
  "run_id",
341
350
  "test_file",
342
351
  "test_name",
343
- "device_class"
352
+ "device_class",
353
+ // verification_phase — categorical `boot` / `hot_path` bucket set by the
354
+ // runtime contract verifier layer. Declared in the shared diagnostics
355
+ // contract (contracts/diagnostics/contract-failed-payload-schema-lock.json)
356
+ // and carried by web/swift; node had drifted by omitting it.
357
+ "verification_phase"
344
358
  ]);
345
359
  function filterDiagnosticPayload(payload) {
346
360
  const filtered = {};
@@ -833,6 +847,16 @@ var EventQueue = class {
833
847
  cancelTimer = null;
834
848
  firstFlushFired = false;
835
849
  nextRetryAt = null;
850
+ /**
851
+ * PARK state (HTTP 426 / `sdk_version_unsupported`). Once parked, the
852
+ * queue stops flushing — retrying a known-too-old payload only wastes
853
+ * bandwidth and drips pointless rejects into the server logs until the
854
+ * process restarts on an upgraded SDK. In-memory: a fresh process starts
855
+ * unparked, retries once, and either delivers (upgraded) or re-parks.
856
+ */
857
+ parked = false;
858
+ /** One developer-facing console warning per process — never per-event spam. */
859
+ parkWarned = false;
836
860
  retry;
837
861
  /**
838
862
  * Stable Idempotency-Key for the current in-flight batch. Minted
@@ -885,6 +909,7 @@ var EventQueue = class {
885
909
  * this one settles. Strict ordering preserved.
886
910
  */
887
911
  async flush() {
912
+ if (this.parked) return null;
888
913
  let batch;
889
914
  let batchId;
890
915
  if (this.pendingBatch !== null && this.pendingBatchId !== null) {
@@ -932,6 +957,28 @@ var EventQueue = class {
932
957
  } catch (err) {
933
958
  const message = err instanceof Error ? err.message : String(err);
934
959
  this.lastError = message;
960
+ if (isVersionRejected(err)) {
961
+ this.parked = true;
962
+ this.buffer = [...batch, ...this.buffer];
963
+ if (this.buffer.length > HARD_BUFFER_CAP) {
964
+ const overflow = this.buffer.length - HARD_BUFFER_CAP;
965
+ this.buffer.splice(0, overflow);
966
+ this.dropped += overflow;
967
+ }
968
+ this.pendingBatch = null;
969
+ this.pendingBatchId = null;
970
+ this.inFlight -= batch.length;
971
+ this.cfg.onBufferChange?.(this.buffer.length);
972
+ const minVersion = versionRejectionFloor(err);
973
+ if (!this.parkWarned) {
974
+ this.parkWarned = true;
975
+ console.warn(
976
+ `[Crossdeck] SDK outdated \u2014 the server is no longer accepting this version's event format. Events are PARKED in memory (held, not lost across this process) and will deliver once you update @cross-deck/node${minVersion ? ` to >= ${minVersion}` : ""} and restart. A restart before upgrade clears them \u2014 configure a disk queue for cross-restart durability.`
977
+ );
978
+ }
979
+ this.cfg.onParked?.({ minVersion, surface: versionRejectionSurface(err) });
980
+ return null;
981
+ }
935
982
  if (isPermanent4xx(err)) {
936
983
  const droppedCount = batch.length;
937
984
  this.pendingBatch = null;
@@ -1030,8 +1077,22 @@ function isPermanent4xx(err) {
1030
1077
  if (typeof status !== "number" || !Number.isFinite(status)) return false;
1031
1078
  if (status < 400 || status >= 500) return false;
1032
1079
  if (status === 408 || status === 429) return false;
1080
+ if (status === 426) return false;
1033
1081
  return true;
1034
1082
  }
1083
+ function isVersionRejected(err) {
1084
+ if (!err || typeof err !== "object") return false;
1085
+ if (err.status === 426) return true;
1086
+ return err.code === "sdk_version_unsupported";
1087
+ }
1088
+ function versionRejectionFloor(err) {
1089
+ const v = err?.minVersion;
1090
+ return typeof v === "string" && v.length > 0 ? v : void 0;
1091
+ }
1092
+ function versionRejectionSurface(err) {
1093
+ const v = err?.surface;
1094
+ return typeof v === "string" && v.length > 0 ? v : void 0;
1095
+ }
1035
1096
  function defaultScheduler(fn, ms) {
1036
1097
  const id = setTimeout(fn, ms);
1037
1098
  if (typeof id.unref === "function") {
@@ -2735,6 +2796,13 @@ var CrossdeckServer = class extends EventEmitter {
2735
2796
  error: info.lastError
2736
2797
  });
2737
2798
  },
2799
+ onParked: (info) => {
2800
+ this.debug.emit(
2801
+ "sdk.parked",
2802
+ `[crossdeck] SDK parked \u2014 server no longer accepts this version's event format. Events held (paused, not lost); update @cross-deck/node${info.minVersion ? ` to >= ${info.minVersion}` : ""} and restart to resume.`,
2803
+ { ...info }
2804
+ );
2805
+ },
2738
2806
  onFirstFlushSuccess: () => {
2739
2807
  this.debug.emit("sdk.first_event_sent", "First batch landed.");
2740
2808
  }
@@ -4309,6 +4377,13 @@ var _CROSSDECK_ERROR_CODES = Object.freeze([
4309
4377
  description: "A field is present but the value failed validation.",
4310
4378
  resolution: "Read error.message for the field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
4311
4379
  retryable: false
4380
+ },
4381
+ {
4382
+ code: "sdk_version_unsupported",
4383
+ type: "version_error",
4384
+ description: "HTTP 426 \u2014 your installed SDK sends an event format the server no longer accepts. The data is good; only the wire dialect is too old. The SDK PARKS automatically: events are held in memory and deliver once you upgrade and restart.",
4385
+ resolution: "Update @cross-deck/node to at least the version in error.minVersion and restart \u2014 the held queue backfills. See https://cross-deck.com/docs/sdk-event-durability/.",
4386
+ retryable: false
4312
4387
  }
4313
4388
  ]);
4314
4389
  function isCrossdeckErrorCode(code) {