@cross-deck/node 1.5.3 → 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/CHANGELOG.md +66 -0
- package/dist/auto-events/index.d.mts +1 -1
- package/dist/auto-events/index.d.ts +1 -1
- package/dist/{crossdeck-server-BFTTwCEP.d.mts → crossdeck-server-DYawt4eT.d.mts} +32 -3
- package/dist/{crossdeck-server-BFTTwCEP.d.ts → crossdeck-server-DYawt4eT.d.ts} +32 -3
- package/dist/index.cjs +146 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.mjs +146 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/contracts.json +0 -552
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-
|
|
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.
|
|
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-
|
|
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.
|
|
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.
|
|
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) {
|
|
@@ -904,6 +929,11 @@ var EventQueue = class {
|
|
|
904
929
|
try {
|
|
905
930
|
const env = this.cfg.envelope();
|
|
906
931
|
const body = {
|
|
932
|
+
// Event Envelope v1 §1 — schema/wire version the server uses to
|
|
933
|
+
// decide whether it can parse this payload. Integer 1; only bumped
|
|
934
|
+
// on a breaking wire change. Distinct from sdk.version (which
|
|
935
|
+
// answers "which build?") — two questions, two fields.
|
|
936
|
+
envelopeVersion: 1,
|
|
907
937
|
events: batch,
|
|
908
938
|
sdk: env.sdk
|
|
909
939
|
};
|
|
@@ -927,6 +957,28 @@ var EventQueue = class {
|
|
|
927
957
|
} catch (err) {
|
|
928
958
|
const message = err instanceof Error ? err.message : String(err);
|
|
929
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
|
+
}
|
|
930
982
|
if (isPermanent4xx(err)) {
|
|
931
983
|
const droppedCount = batch.length;
|
|
932
984
|
this.pendingBatch = null;
|
|
@@ -1025,8 +1077,22 @@ function isPermanent4xx(err) {
|
|
|
1025
1077
|
if (typeof status !== "number" || !Number.isFinite(status)) return false;
|
|
1026
1078
|
if (status < 400 || status >= 500) return false;
|
|
1027
1079
|
if (status === 408 || status === 429) return false;
|
|
1080
|
+
if (status === 426) return false;
|
|
1028
1081
|
return true;
|
|
1029
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
|
+
}
|
|
1030
1096
|
function defaultScheduler(fn, ms) {
|
|
1031
1097
|
const id = setTimeout(fn, ms);
|
|
1032
1098
|
if (typeof id.unref === "function") {
|
|
@@ -1888,6 +1954,25 @@ function runtimeInfoToProperties(info) {
|
|
|
1888
1954
|
if (info.appVersion) out.appVersion = info.appVersion;
|
|
1889
1955
|
return out;
|
|
1890
1956
|
}
|
|
1957
|
+
function buildEventContext(info, sdkName, sdkVersion) {
|
|
1958
|
+
const ctx = {
|
|
1959
|
+
// Common fields (spec §4, all platforms)
|
|
1960
|
+
os: info.platform,
|
|
1961
|
+
osVersion: info.platformRelease,
|
|
1962
|
+
appVersion: info.appVersion ?? null,
|
|
1963
|
+
sdkName,
|
|
1964
|
+
sdkVersion,
|
|
1965
|
+
// locale / timezone: Node has no process-level locale by default;
|
|
1966
|
+
// surface them from the environment if available, otherwise null.
|
|
1967
|
+
locale: typeof process !== "undefined" && process.env["LANG"] || null,
|
|
1968
|
+
timezone: typeof Intl !== "undefined" && Intl.DateTimeFormat().resolvedOptions().timeZone || null,
|
|
1969
|
+
// Node-specific runtime context (spec §4 Node section)
|
|
1970
|
+
nodeVersion: info.nodeVersion,
|
|
1971
|
+
host: info.host,
|
|
1972
|
+
region: info.region ?? null
|
|
1973
|
+
};
|
|
1974
|
+
return ctx;
|
|
1975
|
+
}
|
|
1891
1976
|
|
|
1892
1977
|
// src/flush-on-exit.ts
|
|
1893
1978
|
var SIGNALS = ["SIGTERM", "SIGINT"];
|
|
@@ -2555,6 +2640,8 @@ var CrossdeckServer = class extends EventEmitter {
|
|
|
2555
2640
|
processAnonymousId;
|
|
2556
2641
|
runtime;
|
|
2557
2642
|
runtimeProperties;
|
|
2643
|
+
/** Envelope v1 §4 context object — built once at SDK init, reused on every event. */
|
|
2644
|
+
eventContext;
|
|
2558
2645
|
breadcrumbs;
|
|
2559
2646
|
eventQueue;
|
|
2560
2647
|
errorTracker;
|
|
@@ -2572,6 +2659,23 @@ var CrossdeckServer = class extends EventEmitter {
|
|
|
2572
2659
|
*/
|
|
2573
2660
|
entitlementStore;
|
|
2574
2661
|
debug;
|
|
2662
|
+
/**
|
|
2663
|
+
* Event Envelope v1 §3 — per-session monotonic sequence counter.
|
|
2664
|
+
*
|
|
2665
|
+
* Node has no mobile "session" lifecycle (no app launch / background /
|
|
2666
|
+
* foreground). We model a session as the SDK instance lifetime: the
|
|
2667
|
+
* counter starts at 0 on construction and increments once per
|
|
2668
|
+
* `track()` call. `session.started` is the boot-telemetry event
|
|
2669
|
+
* (the first `track()` called from `emitBootTelemetryEvent()`), so
|
|
2670
|
+
* the seq will be 0 for that event, matching the spec's "reset to 0
|
|
2671
|
+
* at session.started" clause — by construction, it's the zeroth event
|
|
2672
|
+
* of this instance's lifecycle.
|
|
2673
|
+
*
|
|
2674
|
+
* The counter persists for the entire process lifetime of the SDK
|
|
2675
|
+
* instance (spec §3 clause 1: background/foreground does not reset).
|
|
2676
|
+
* A new `CrossdeckServer` construction is the only reset (new session).
|
|
2677
|
+
*/
|
|
2678
|
+
sessionSeq = 0;
|
|
2575
2679
|
/**
|
|
2576
2680
|
* Alias map — `developerUserId` / `anonymousId` → canonical
|
|
2577
2681
|
* `crossdeckCustomerId`. Populated by `getEntitlements()` so a
|
|
@@ -2631,6 +2735,7 @@ var CrossdeckServer = class extends EventEmitter {
|
|
|
2631
2735
|
appVersion: options.appVersion
|
|
2632
2736
|
});
|
|
2633
2737
|
this.runtimeProperties = runtimeInfoToProperties(this.runtime);
|
|
2738
|
+
this.eventContext = buildEventContext(this.runtime, SDK_NAME, this.sdkVersion);
|
|
2634
2739
|
this.breadcrumbs = new BreadcrumbBuffer(options.breadcrumbsMaxSize ?? 50);
|
|
2635
2740
|
this.superProps = new SuperPropertyStore();
|
|
2636
2741
|
this.entitlementCache = new EntitlementCache({
|
|
@@ -2691,6 +2796,13 @@ var CrossdeckServer = class extends EventEmitter {
|
|
|
2691
2796
|
error: info.lastError
|
|
2692
2797
|
});
|
|
2693
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
|
+
},
|
|
2694
2806
|
onFirstFlushSuccess: () => {
|
|
2695
2807
|
this.debug.emit("sdk.first_event_sent", "First batch landed.");
|
|
2696
2808
|
}
|
|
@@ -3082,7 +3194,6 @@ var CrossdeckServer = class extends EventEmitter {
|
|
|
3082
3194
|
}
|
|
3083
3195
|
}
|
|
3084
3196
|
const properties = {
|
|
3085
|
-
...this.runtimeProperties,
|
|
3086
3197
|
...this.superProps.getSuperProperties(),
|
|
3087
3198
|
...sanitized
|
|
3088
3199
|
};
|
|
@@ -3091,10 +3202,14 @@ var CrossdeckServer = class extends EventEmitter {
|
|
|
3091
3202
|
properties.$groups = groupIds;
|
|
3092
3203
|
}
|
|
3093
3204
|
const identity = this.resolveIdentity(event);
|
|
3205
|
+
const seq = this.sessionSeq++;
|
|
3206
|
+
const timestamp = event.timestamp ?? Date.now();
|
|
3094
3207
|
const queued = {
|
|
3095
3208
|
eventId: event.eventId ?? mintId("evt", 8),
|
|
3096
3209
|
name: event.name,
|
|
3097
|
-
timestamp
|
|
3210
|
+
timestamp,
|
|
3211
|
+
seq,
|
|
3212
|
+
context: this.eventContext,
|
|
3098
3213
|
properties,
|
|
3099
3214
|
...identity
|
|
3100
3215
|
};
|
|
@@ -3133,6 +3248,8 @@ var CrossdeckServer = class extends EventEmitter {
|
|
|
3133
3248
|
}
|
|
3134
3249
|
const normalized = events.map((event) => this.normalizeIngestEvent(event));
|
|
3135
3250
|
const body = {
|
|
3251
|
+
// Event Envelope v1 §1 — wire version (parity with the queue path).
|
|
3252
|
+
envelopeVersion: 1,
|
|
3136
3253
|
events: normalized,
|
|
3137
3254
|
sdk: { name: SDK_NAME, version: this.sdkVersion },
|
|
3138
3255
|
// Match the queue's batch envelope (see event-queue.ts) — backend
|
|
@@ -4260,6 +4377,13 @@ var _CROSSDECK_ERROR_CODES = Object.freeze([
|
|
|
4260
4377
|
description: "A field is present but the value failed validation.",
|
|
4261
4378
|
resolution: "Read error.message for the field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
|
|
4262
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
|
|
4263
4387
|
}
|
|
4264
4388
|
]);
|
|
4265
4389
|
function isCrossdeckErrorCode(code) {
|
|
@@ -4391,8 +4515,8 @@ function normaliseSecrets(input) {
|
|
|
4391
4515
|
}
|
|
4392
4516
|
|
|
4393
4517
|
// src/_contracts-bundled.ts
|
|
4394
|
-
var BUNDLED_IN = "@cross-deck/node@1.
|
|
4395
|
-
var SDK_VERSION2 = "1.
|
|
4518
|
+
var BUNDLED_IN = "@cross-deck/node@1.6.0";
|
|
4519
|
+
var SDK_VERSION2 = "1.6.0";
|
|
4396
4520
|
var BUNDLED_CONTRACTS = Object.freeze([
|
|
4397
4521
|
{
|
|
4398
4522
|
"id": "contract-failed-payload-schema-lock",
|
|
@@ -4514,7 +4638,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4514
4638
|
"legal/security/index.html#diagnostic",
|
|
4515
4639
|
"legal/sdk-data/index.html#b-diagnostic"
|
|
4516
4640
|
],
|
|
4517
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4641
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4518
4642
|
},
|
|
4519
4643
|
{
|
|
4520
4644
|
"id": "documentation-honesty",
|
|
@@ -4546,7 +4670,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4546
4670
|
],
|
|
4547
4671
|
"registeredAt": "2026-05-26",
|
|
4548
4672
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.1",
|
|
4549
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4673
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4550
4674
|
},
|
|
4551
4675
|
{
|
|
4552
4676
|
"id": "error-envelope-shape",
|
|
@@ -4585,7 +4709,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4585
4709
|
],
|
|
4586
4710
|
"registeredAt": "2026-05-26",
|
|
4587
4711
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
|
|
4588
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4712
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4589
4713
|
},
|
|
4590
4714
|
{
|
|
4591
4715
|
"id": "flush-interval-parity",
|
|
@@ -4630,7 +4754,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4630
4754
|
],
|
|
4631
4755
|
"registeredAt": "2026-05-26",
|
|
4632
4756
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
|
|
4633
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4757
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4634
4758
|
},
|
|
4635
4759
|
{
|
|
4636
4760
|
"id": "idempotency-key-deterministic",
|
|
@@ -4735,7 +4859,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4735
4859
|
],
|
|
4736
4860
|
"registeredAt": "2026-05-26",
|
|
4737
4861
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
|
|
4738
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4862
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4739
4863
|
},
|
|
4740
4864
|
{
|
|
4741
4865
|
"id": "node-pii-scrubber",
|
|
@@ -4774,7 +4898,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4774
4898
|
],
|
|
4775
4899
|
"registeredAt": "2026-05-26",
|
|
4776
4900
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.1",
|
|
4777
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4901
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4778
4902
|
},
|
|
4779
4903
|
{
|
|
4780
4904
|
"id": "node-shutdown-awaits-flush",
|
|
@@ -4807,7 +4931,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4807
4931
|
],
|
|
4808
4932
|
"registeredAt": "2026-05-26",
|
|
4809
4933
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.4",
|
|
4810
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4934
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4811
4935
|
},
|
|
4812
4936
|
{
|
|
4813
4937
|
"id": "sdk-error-codes-catalogue",
|
|
@@ -4821,9 +4945,14 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4821
4945
|
"codeRef": [
|
|
4822
4946
|
"sdks/web/src/error-codes.ts",
|
|
4823
4947
|
"sdks/node/src/error-codes.ts",
|
|
4948
|
+
"sdks/web/src/_contract-verifiers.ts",
|
|
4824
4949
|
"backend/src/api/v1-errors.ts"
|
|
4825
4950
|
],
|
|
4826
4951
|
"testRef": [
|
|
4952
|
+
{
|
|
4953
|
+
"file": "sdks/web/tests/contract-verifiers.test.ts",
|
|
4954
|
+
"name": "sdk-error-codes-catalogue covers every backend wire code with remediation"
|
|
4955
|
+
},
|
|
4827
4956
|
{
|
|
4828
4957
|
"file": "sdks/web/tests/error-codes-backfill.test.ts",
|
|
4829
4958
|
"name": "includes backend code"
|
|
@@ -4847,7 +4976,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4847
4976
|
],
|
|
4848
4977
|
"registeredAt": "2026-05-26",
|
|
4849
4978
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
|
|
4850
|
-
"bundledIn": "@cross-deck/node@1.
|
|
4979
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4851
4980
|
},
|
|
4852
4981
|
{
|
|
4853
4982
|
"id": "sync-purchases-funnel-parity",
|
|
@@ -4880,7 +5009,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4880
5009
|
],
|
|
4881
5010
|
"registeredAt": "2026-05-26",
|
|
4882
5011
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
|
|
4883
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5012
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4884
5013
|
},
|
|
4885
5014
|
{
|
|
4886
5015
|
"id": "verifier-timestamp-mandatory",
|
|
@@ -4934,7 +5063,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
4934
5063
|
],
|
|
4935
5064
|
"registeredAt": "2026-05-26",
|
|
4936
5065
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 7.2",
|
|
4937
|
-
"bundledIn": "@cross-deck/node@1.
|
|
5066
|
+
"bundledIn": "@cross-deck/node@1.6.0"
|
|
4938
5067
|
}
|
|
4939
5068
|
]);
|
|
4940
5069
|
|