@cross-deck/node 1.5.0 → 1.5.2

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-oAaKBnUU.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 CrossdeckAuthenticationError, h as CrossdeckConfigurationError, i as CrossdeckError, j as CrossdeckErrorPayload, k as CrossdeckErrorType, l as CrossdeckInternalError, m as CrossdeckNetworkError, n as CrossdeckPermissionError, o as CrossdeckRateLimitError, p as CrossdeckServer, q as CrossdeckServerOptions, r as CrossdeckValidationError, D as DEFAULT_BASE_URL, s as DEFAULT_TIMEOUT_MS, t as Diagnostics, E as EntitlementCacheOptions, u as EntitlementMutationResult, v as EntitlementStore, w as EntitlementsListResponse, x as EntitlementsListener, y as Environment, z as ErrorCaptureConfig, F as ErrorLevel, G as EventProperties, H as ForgetResult, I as GrantDuration, J as GrantEntitlementInput, K as GroupMembership, L as HeartbeatResponse, M as HttpRequestInfo, N as HttpResponseInfo, O as HttpRetriesConfig, P as IdentifyOptions, Q as IdentityHints, R as IngestOptions, S as IngestResponse, T as PublicEntitlement, U as PurchaseResult, V as RequestOptions, W as RevokeEntitlementInput, X as RuntimeHost, Y as RuntimeInfo, Z as ServerEvent, _ as StackFrame, $ as StoredEntitlements, a0 as SyncPurchaseInput, a1 as makeCrossdeckError } from './crossdeck-server-DhnHvUhh.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.4.2";
20
+ declare const SDK_VERSION = "1.3.1";
21
21
  declare const SDK_NAME = "@cross-deck/node";
22
22
 
23
23
  /**
@@ -158,34 +158,16 @@ declare const _CROSSDECK_ERROR_CODES: readonly [{
158
158
  readonly resolution: "Increase flushOnExitTimeoutMs in CrossdeckServer options. Default is 2000ms; serverless runtimes typically allow 5-10s before SIGKILL. If events are dropping silently in production, raise this.";
159
159
  readonly retryable: false;
160
160
  }, {
161
- readonly code: "webhook_signature_mismatch";
162
- readonly type: "authentication_error";
163
- readonly description: "Webhook HMAC didn't verify against any configured secret (wrong-secret / stale rotation signal).";
164
- readonly resolution: "Confirm the secret matches dashboard → Webhooks. If you rotated, include both the old and new secret as an array until receivers cut over.";
165
- readonly retryable: false;
166
- }, {
167
- readonly code: "webhook_timestamp_outside_tolerance";
168
- readonly type: "authentication_error";
169
- readonly description: "Webhook timestamp drift exceeds the configured replay-tolerance window (default 5 minutes; replay-attack signal).";
170
- readonly resolution: "Verify NTP on the receiving host. A spike on this code warrants its own alert separate from signature_mismatch — replay attacks look like this.";
171
- readonly retryable: false;
172
- }, {
173
- readonly code: "webhook_timestamp_missing";
161
+ readonly code: "webhook_invalid_signature";
174
162
  readonly type: "authentication_error";
175
- readonly description: "Webhook signature header is absent or has no `t=` timestamp segment — the timestamp gate cannot be verified.";
176
- readonly resolution: "Confirm the request actually came from Crossdeck (signature headers are always present on real deliveries). A missing header is either a misconfigured intermediary or a forged request.";
163
+ readonly description: "The webhook signature header did not verify against the supplied secret.";
164
+ readonly resolution: "Confirm the secret matches the one in your Crossdeck dashboard Webhooks page. If the request is genuinely from Crossdeck, the secret is wrong, stale, or recently rotated.";
177
165
  readonly retryable: false;
178
166
  }, {
179
- readonly code: "webhook_payload_not_json";
167
+ readonly code: "webhook_replay_window_exceeded";
180
168
  readonly type: "authentication_error";
181
- readonly description: "Webhook signature verified but the body isn't valid JSON payload tampered post-signing or source bug.";
182
- readonly resolution: "Inspect the raw payload. If it's not JSON, either the request was modified in transit or the sender has a bug file a support ticket with the raw body.";
183
- readonly retryable: false;
184
- }, {
185
- readonly code: "webhook_invalid_tolerance";
186
- readonly type: "configuration_error";
187
- readonly description: "verifyWebhookSignature() called with a non-finite / negative / above-24h-cap replayToleranceMs (would silently disable replay protection).";
188
- readonly resolution: "Pass a finite number between 0 and 86_400_000ms (24h). Default (5 minutes) is correct for almost every scenario. Pre-v1.4.0 accepted Infinity/NaN and silently dropped the check.";
169
+ readonly description: "The webhook timestamp is older than the replay-tolerance window (default 5 minutes).";
170
+ readonly resolution: "The webhook is either replayed or your receiving clock is wildly skewed. Verify NTP on the receiving host. Increase replayToleranceMs only if you accept the replay-attack risk.";
189
171
  readonly retryable: false;
190
172
  }, {
191
173
  readonly code: "webhook_missing_secret";
@@ -193,84 +175,6 @@ declare const _CROSSDECK_ERROR_CODES: readonly [{
193
175
  readonly description: "verifyWebhookSignature() was called without a signing secret.";
194
176
  readonly resolution: "Pass the secret from your Crossdeck dashboard → Webhooks page. Never hardcode in source — read from an env var.";
195
177
  readonly retryable: false;
196
- }, {
197
- readonly code: "webhook_invalid_signature";
198
- readonly type: "authentication_error";
199
- readonly description: "DEPRECATED in v1.4.0 — split into webhook_signature_mismatch / webhook_timestamp_missing / webhook_timestamp_outside_tolerance / webhook_payload_not_json for alerting clarity.";
200
- readonly resolution: "Migrate alert rules to the more specific v1.4.0 codes — they distinguish replay-attack signals from wrong-secret signals.";
201
- readonly retryable: false;
202
- }, {
203
- readonly code: "webhook_replay_window_exceeded";
204
- readonly type: "authentication_error";
205
- readonly description: "DEPRECATED in v1.4.0 — renamed to webhook_timestamp_outside_tolerance.";
206
- readonly resolution: "Update alerts to webhook_timestamp_outside_tolerance.";
207
- readonly retryable: false;
208
- }, {
209
- readonly code: "missing_api_key";
210
- readonly type: "authentication_error";
211
- readonly description: "No Authorization header (or Crossdeck-Api-Key header) on the request.";
212
- readonly resolution: "Confirm the CrossdeckServer was constructed with a cd_sk_… secretKey. Re-check env vars in production deployments.";
213
- readonly retryable: false;
214
- }, {
215
- readonly code: "invalid_api_key";
216
- readonly type: "authentication_error";
217
- readonly description: "The secret key is malformed, unknown, or doesn't resolve to a project.";
218
- readonly resolution: "Copy the key from Crossdeck dashboard → API keys. Server SDK requires cd_sk_test_ / cd_sk_live_ — client SDK keys (cd_pub_…) won't work on the Node SDK.";
219
- readonly retryable: false;
220
- }, {
221
- readonly code: "key_revoked";
222
- readonly type: "authentication_error";
223
- readonly description: "The secret key was revoked in the dashboard.";
224
- readonly resolution: "Mint a fresh key in dashboard → API keys → Create new. The revoked key cannot be reactivated.";
225
- readonly retryable: false;
226
- }, {
227
- readonly code: "env_mismatch";
228
- readonly type: "permission_error";
229
- readonly description: "The key's env prefix doesn't match the resolved app's configured env.";
230
- readonly resolution: "Use a cd_sk_live_ key with a production app, cd_sk_test_ with a sandbox app. Crossing breaks the env lock.";
231
- readonly retryable: false;
232
- }, {
233
- readonly code: "idempotency_key_in_use";
234
- readonly type: "invalid_request_error";
235
- readonly description: "An Idempotency-Key was reused for a request with a different body (Stripe-grade contract).";
236
- readonly resolution: "Server SDK derives keys deterministically from the body since v1.4.0; this should only fire if you passed options.idempotencyKey explicitly. Use a fresh key per logical operation.";
237
- readonly retryable: false;
238
- }, {
239
- readonly code: "rate_limited";
240
- readonly type: "rate_limit_error";
241
- readonly description: "Request rate exceeded the project's per-second cap.";
242
- readonly resolution: "Honour Retry-After (managed retries do this automatically). For custom paths, throttle to <100 req/s/key.";
243
- readonly retryable: true;
244
- }, {
245
- readonly code: "internal_error";
246
- readonly type: "internal_error";
247
- readonly description: "Server-side issue. Safe to retry with backoff.";
248
- readonly resolution: "Managed retries handle this automatically. If a code path surfaces it to your code, contact support with the requestId.";
249
- readonly retryable: true;
250
- }, {
251
- readonly code: "google_not_supported";
252
- readonly type: "invalid_request_error";
253
- readonly description: "POST /purchases/sync with rail=google is gated until the Play Developer API reconciliation worker ships.";
254
- readonly resolution: "Until v1.5+, Google Play purchases verify via Real-time Developer Notifications. The Android SDK auto-track path handles this transparently.";
255
- readonly retryable: false;
256
- }, {
257
- readonly code: "stripe_not_supported";
258
- readonly type: "invalid_request_error";
259
- readonly description: "POST /purchases/sync with rail=stripe is unsupported — Stripe webhooks deliver evidence server-side.";
260
- readonly resolution: "Wire Stripe via the standard Checkout / Customer Portal flow; Crossdeck reconciles via the platform webhook automatically.";
261
- readonly retryable: false;
262
- }, {
263
- readonly code: "missing_required_param";
264
- readonly type: "invalid_request_error";
265
- readonly description: "A required field is absent from the request body.";
266
- readonly resolution: "The error.message identifies the missing field. Refer to the SDK's TypeScript types for canonical shapes.";
267
- readonly retryable: false;
268
- }, {
269
- readonly code: "invalid_param_value";
270
- readonly type: "invalid_request_error";
271
- readonly description: "A field is present but the value failed validation.";
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
- readonly retryable: false;
274
178
  }];
275
179
  /**
276
180
  * Literal union of every code documented in `CROSSDECK_ERROR_CODES`.
@@ -297,22 +201,9 @@ declare function getErrorCode(code: string): ErrorCodeEntry | undefined;
297
201
  /**
298
202
  * Webhook signature verification — Stripe pattern.
299
203
  *
300
- * **[ROADMAP v1.4.0 honesty note]:** Crossdeck does NOT yet send
301
- * outbound webhooks. Outbound delivery (signer + worker + scheduler
302
- * + dead-letter dashboard) is on the post-v1.5 roadmap. This
303
- * verifier exists today so customer-side integration code can be
304
- * written and tested against fixtures (use `signWebhookPayload`
305
- * to produce signed bodies for your local tests), and so the
306
- * verification contract surface is locked in BEFORE delivery
307
- * ships — Phase 7.2 of the bank-grade reconciliation tightened
308
- * the timestamp-validation footguns here precisely because the
309
- * helper IS the contract surface for inbound validation,
310
- * regardless of when first-party delivery lights up.
311
- *
312
- * Lets customers verify the events Crossdeck sends to THEM (when
313
- * delivery ships). Table-stakes for any backend SDK (Stripe ships
314
- * `Stripe.webhooks.constructEvent()` from day one, Svix ships
315
- * `Webhook.verify()` from day one).
204
+ * Lets customers verify the events Crossdeck sends to THEM. Table-stakes
205
+ * for any backend SDK (Stripe ships `Stripe.webhooks.constructEvent()`
206
+ * from day one, Svix ships `Webhook.verify()` from day one).
316
207
  *
317
208
  * Wire format:
318
209
  * Header `Crossdeck-Signature: t=<unix-seconds>,v1=<hex>`
@@ -353,21 +244,9 @@ declare function getErrorCode(code: string): ErrorCodeEntry | undefined;
353
244
  interface VerifyWebhookOptions {
354
245
  /**
355
246
  * Maximum age of the webhook timestamp in milliseconds. Default
356
- * 5 minutes (`DEFAULT_REPLAY_TOLERANCE_MS`). Anything older than
357
- * this is rejected as a replay.
358
- *
359
- * **v1.4.0 Phase 7.2 bank-grade contract:** the timestamp window
360
- * is MANDATORY. Pre-v1.4.0 the helper accepted `tolerance: 0`
361
- * (silently disables the check) and `tolerance: Infinity` /
362
- * `null` / `NaN` (silently disables via `Math.abs(...) > Infinity
363
- * = false`). Customers relying on replay protection silently
364
- * lost it.
365
- *
366
- * The helper now rejects non-finite / negative / above-cap
367
- * tolerances at the boundary with a typed
368
- * `webhook_invalid_tolerance` error. Hard upper bound is 24h —
369
- * sufficient for any plausible clock-skew scenario, prevents
370
- * "Infinity by typo" from defeating replay protection.
247
+ * 5 minutes (5 * 60 * 1000). Anything older than this is rejected
248
+ * as a replay. Pass 0 to disable the replay window (NOT recommended
249
+ * — accept the trade-off only if you have a separate replay defence).
371
250
  */
372
251
  replayToleranceMs?: number;
373
252
  /**
@@ -378,21 +257,13 @@ interface VerifyWebhookOptions {
378
257
  }
379
258
  /**
380
259
  * Verify a Crossdeck-signed webhook. Returns the parsed JSON payload
381
- * on success. Throws `CrossdeckError` with one of these
382
- * distinguishable codes (v1.4.0 Phase 7.2 — pre-v1.4.0 conflated
383
- * everything under `webhook_invalid_signature`; alerting can now
384
- * separate replay-attack signals from wrong-secret signals):
385
- * - `webhook_missing_secret` — no secret configured.
386
- * - `webhook_invalid_tolerance` — caller passed Infinity / NaN /
387
- * negative / above-24h-cap `replayToleranceMs`.
388
- * - `webhook_timestamp_missing` — header absent or has no `t=`.
389
- * - `webhook_timestamp_outside_tolerance` — drift > tolerance
390
- * (replay-attack signal — split this from signature mismatch
391
- * in your alerting rules).
392
- * - `webhook_signature_mismatch` — HMAC didn't match any
393
- * configured secret (wrong-secret / rotation-drift signal).
394
- * - `webhook_payload_not_json` — signature verified but the body
395
- * isn't parseable JSON (tampered post-signing or source bug).
260
+ * on success. Throws `CrossdeckError` on:
261
+ * - missing / malformed signature header (`webhook_invalid_signature`)
262
+ * - missing secret (`webhook_missing_secret`)
263
+ * - timestamp outside replay window (`webhook_replay_window_exceeded`)
264
+ * - HMAC mismatch (`webhook_invalid_signature`)
265
+ * - non-JSON payload (`webhook_invalid_signature` — same code because
266
+ * a tampered payload that breaks JSON parses as invalid)
396
267
  *
397
268
  * `secret` accepts a single string or an array of strings (for
398
269
  * rotation). Any one match is sufficient.
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-oAaKBnUU.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 CrossdeckAuthenticationError, h as CrossdeckConfigurationError, i as CrossdeckError, j as CrossdeckErrorPayload, k as CrossdeckErrorType, l as CrossdeckInternalError, m as CrossdeckNetworkError, n as CrossdeckPermissionError, o as CrossdeckRateLimitError, p as CrossdeckServer, q as CrossdeckServerOptions, r as CrossdeckValidationError, D as DEFAULT_BASE_URL, s as DEFAULT_TIMEOUT_MS, t as Diagnostics, E as EntitlementCacheOptions, u as EntitlementMutationResult, v as EntitlementStore, w as EntitlementsListResponse, x as EntitlementsListener, y as Environment, z as ErrorCaptureConfig, F as ErrorLevel, G as EventProperties, H as ForgetResult, I as GrantDuration, J as GrantEntitlementInput, K as GroupMembership, L as HeartbeatResponse, M as HttpRequestInfo, N as HttpResponseInfo, O as HttpRetriesConfig, P as IdentifyOptions, Q as IdentityHints, R as IngestOptions, S as IngestResponse, T as PublicEntitlement, U as PurchaseResult, V as RequestOptions, W as RevokeEntitlementInput, X as RuntimeHost, Y as RuntimeInfo, Z as ServerEvent, _ as StackFrame, $ as StoredEntitlements, a0 as SyncPurchaseInput, a1 as makeCrossdeckError } from './crossdeck-server-DhnHvUhh.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.4.2";
20
+ declare const SDK_VERSION = "1.3.1";
21
21
  declare const SDK_NAME = "@cross-deck/node";
22
22
 
23
23
  /**
@@ -158,34 +158,16 @@ declare const _CROSSDECK_ERROR_CODES: readonly [{
158
158
  readonly resolution: "Increase flushOnExitTimeoutMs in CrossdeckServer options. Default is 2000ms; serverless runtimes typically allow 5-10s before SIGKILL. If events are dropping silently in production, raise this.";
159
159
  readonly retryable: false;
160
160
  }, {
161
- readonly code: "webhook_signature_mismatch";
162
- readonly type: "authentication_error";
163
- readonly description: "Webhook HMAC didn't verify against any configured secret (wrong-secret / stale rotation signal).";
164
- readonly resolution: "Confirm the secret matches dashboard → Webhooks. If you rotated, include both the old and new secret as an array until receivers cut over.";
165
- readonly retryable: false;
166
- }, {
167
- readonly code: "webhook_timestamp_outside_tolerance";
168
- readonly type: "authentication_error";
169
- readonly description: "Webhook timestamp drift exceeds the configured replay-tolerance window (default 5 minutes; replay-attack signal).";
170
- readonly resolution: "Verify NTP on the receiving host. A spike on this code warrants its own alert separate from signature_mismatch — replay attacks look like this.";
171
- readonly retryable: false;
172
- }, {
173
- readonly code: "webhook_timestamp_missing";
161
+ readonly code: "webhook_invalid_signature";
174
162
  readonly type: "authentication_error";
175
- readonly description: "Webhook signature header is absent or has no `t=` timestamp segment — the timestamp gate cannot be verified.";
176
- readonly resolution: "Confirm the request actually came from Crossdeck (signature headers are always present on real deliveries). A missing header is either a misconfigured intermediary or a forged request.";
163
+ readonly description: "The webhook signature header did not verify against the supplied secret.";
164
+ readonly resolution: "Confirm the secret matches the one in your Crossdeck dashboard Webhooks page. If the request is genuinely from Crossdeck, the secret is wrong, stale, or recently rotated.";
177
165
  readonly retryable: false;
178
166
  }, {
179
- readonly code: "webhook_payload_not_json";
167
+ readonly code: "webhook_replay_window_exceeded";
180
168
  readonly type: "authentication_error";
181
- readonly description: "Webhook signature verified but the body isn't valid JSON payload tampered post-signing or source bug.";
182
- readonly resolution: "Inspect the raw payload. If it's not JSON, either the request was modified in transit or the sender has a bug file a support ticket with the raw body.";
183
- readonly retryable: false;
184
- }, {
185
- readonly code: "webhook_invalid_tolerance";
186
- readonly type: "configuration_error";
187
- readonly description: "verifyWebhookSignature() called with a non-finite / negative / above-24h-cap replayToleranceMs (would silently disable replay protection).";
188
- readonly resolution: "Pass a finite number between 0 and 86_400_000ms (24h). Default (5 minutes) is correct for almost every scenario. Pre-v1.4.0 accepted Infinity/NaN and silently dropped the check.";
169
+ readonly description: "The webhook timestamp is older than the replay-tolerance window (default 5 minutes).";
170
+ readonly resolution: "The webhook is either replayed or your receiving clock is wildly skewed. Verify NTP on the receiving host. Increase replayToleranceMs only if you accept the replay-attack risk.";
189
171
  readonly retryable: false;
190
172
  }, {
191
173
  readonly code: "webhook_missing_secret";
@@ -193,84 +175,6 @@ declare const _CROSSDECK_ERROR_CODES: readonly [{
193
175
  readonly description: "verifyWebhookSignature() was called without a signing secret.";
194
176
  readonly resolution: "Pass the secret from your Crossdeck dashboard → Webhooks page. Never hardcode in source — read from an env var.";
195
177
  readonly retryable: false;
196
- }, {
197
- readonly code: "webhook_invalid_signature";
198
- readonly type: "authentication_error";
199
- readonly description: "DEPRECATED in v1.4.0 — split into webhook_signature_mismatch / webhook_timestamp_missing / webhook_timestamp_outside_tolerance / webhook_payload_not_json for alerting clarity.";
200
- readonly resolution: "Migrate alert rules to the more specific v1.4.0 codes — they distinguish replay-attack signals from wrong-secret signals.";
201
- readonly retryable: false;
202
- }, {
203
- readonly code: "webhook_replay_window_exceeded";
204
- readonly type: "authentication_error";
205
- readonly description: "DEPRECATED in v1.4.0 — renamed to webhook_timestamp_outside_tolerance.";
206
- readonly resolution: "Update alerts to webhook_timestamp_outside_tolerance.";
207
- readonly retryable: false;
208
- }, {
209
- readonly code: "missing_api_key";
210
- readonly type: "authentication_error";
211
- readonly description: "No Authorization header (or Crossdeck-Api-Key header) on the request.";
212
- readonly resolution: "Confirm the CrossdeckServer was constructed with a cd_sk_… secretKey. Re-check env vars in production deployments.";
213
- readonly retryable: false;
214
- }, {
215
- readonly code: "invalid_api_key";
216
- readonly type: "authentication_error";
217
- readonly description: "The secret key is malformed, unknown, or doesn't resolve to a project.";
218
- readonly resolution: "Copy the key from Crossdeck dashboard → API keys. Server SDK requires cd_sk_test_ / cd_sk_live_ — client SDK keys (cd_pub_…) won't work on the Node SDK.";
219
- readonly retryable: false;
220
- }, {
221
- readonly code: "key_revoked";
222
- readonly type: "authentication_error";
223
- readonly description: "The secret key was revoked in the dashboard.";
224
- readonly resolution: "Mint a fresh key in dashboard → API keys → Create new. The revoked key cannot be reactivated.";
225
- readonly retryable: false;
226
- }, {
227
- readonly code: "env_mismatch";
228
- readonly type: "permission_error";
229
- readonly description: "The key's env prefix doesn't match the resolved app's configured env.";
230
- readonly resolution: "Use a cd_sk_live_ key with a production app, cd_sk_test_ with a sandbox app. Crossing breaks the env lock.";
231
- readonly retryable: false;
232
- }, {
233
- readonly code: "idempotency_key_in_use";
234
- readonly type: "invalid_request_error";
235
- readonly description: "An Idempotency-Key was reused for a request with a different body (Stripe-grade contract).";
236
- readonly resolution: "Server SDK derives keys deterministically from the body since v1.4.0; this should only fire if you passed options.idempotencyKey explicitly. Use a fresh key per logical operation.";
237
- readonly retryable: false;
238
- }, {
239
- readonly code: "rate_limited";
240
- readonly type: "rate_limit_error";
241
- readonly description: "Request rate exceeded the project's per-second cap.";
242
- readonly resolution: "Honour Retry-After (managed retries do this automatically). For custom paths, throttle to <100 req/s/key.";
243
- readonly retryable: true;
244
- }, {
245
- readonly code: "internal_error";
246
- readonly type: "internal_error";
247
- readonly description: "Server-side issue. Safe to retry with backoff.";
248
- readonly resolution: "Managed retries handle this automatically. If a code path surfaces it to your code, contact support with the requestId.";
249
- readonly retryable: true;
250
- }, {
251
- readonly code: "google_not_supported";
252
- readonly type: "invalid_request_error";
253
- readonly description: "POST /purchases/sync with rail=google is gated until the Play Developer API reconciliation worker ships.";
254
- readonly resolution: "Until v1.5+, Google Play purchases verify via Real-time Developer Notifications. The Android SDK auto-track path handles this transparently.";
255
- readonly retryable: false;
256
- }, {
257
- readonly code: "stripe_not_supported";
258
- readonly type: "invalid_request_error";
259
- readonly description: "POST /purchases/sync with rail=stripe is unsupported — Stripe webhooks deliver evidence server-side.";
260
- readonly resolution: "Wire Stripe via the standard Checkout / Customer Portal flow; Crossdeck reconciles via the platform webhook automatically.";
261
- readonly retryable: false;
262
- }, {
263
- readonly code: "missing_required_param";
264
- readonly type: "invalid_request_error";
265
- readonly description: "A required field is absent from the request body.";
266
- readonly resolution: "The error.message identifies the missing field. Refer to the SDK's TypeScript types for canonical shapes.";
267
- readonly retryable: false;
268
- }, {
269
- readonly code: "invalid_param_value";
270
- readonly type: "invalid_request_error";
271
- readonly description: "A field is present but the value failed validation.";
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
- readonly retryable: false;
274
178
  }];
275
179
  /**
276
180
  * Literal union of every code documented in `CROSSDECK_ERROR_CODES`.
@@ -297,22 +201,9 @@ declare function getErrorCode(code: string): ErrorCodeEntry | undefined;
297
201
  /**
298
202
  * Webhook signature verification — Stripe pattern.
299
203
  *
300
- * **[ROADMAP v1.4.0 honesty note]:** Crossdeck does NOT yet send
301
- * outbound webhooks. Outbound delivery (signer + worker + scheduler
302
- * + dead-letter dashboard) is on the post-v1.5 roadmap. This
303
- * verifier exists today so customer-side integration code can be
304
- * written and tested against fixtures (use `signWebhookPayload`
305
- * to produce signed bodies for your local tests), and so the
306
- * verification contract surface is locked in BEFORE delivery
307
- * ships — Phase 7.2 of the bank-grade reconciliation tightened
308
- * the timestamp-validation footguns here precisely because the
309
- * helper IS the contract surface for inbound validation,
310
- * regardless of when first-party delivery lights up.
311
- *
312
- * Lets customers verify the events Crossdeck sends to THEM (when
313
- * delivery ships). Table-stakes for any backend SDK (Stripe ships
314
- * `Stripe.webhooks.constructEvent()` from day one, Svix ships
315
- * `Webhook.verify()` from day one).
204
+ * Lets customers verify the events Crossdeck sends to THEM. Table-stakes
205
+ * for any backend SDK (Stripe ships `Stripe.webhooks.constructEvent()`
206
+ * from day one, Svix ships `Webhook.verify()` from day one).
316
207
  *
317
208
  * Wire format:
318
209
  * Header `Crossdeck-Signature: t=<unix-seconds>,v1=<hex>`
@@ -353,21 +244,9 @@ declare function getErrorCode(code: string): ErrorCodeEntry | undefined;
353
244
  interface VerifyWebhookOptions {
354
245
  /**
355
246
  * Maximum age of the webhook timestamp in milliseconds. Default
356
- * 5 minutes (`DEFAULT_REPLAY_TOLERANCE_MS`). Anything older than
357
- * this is rejected as a replay.
358
- *
359
- * **v1.4.0 Phase 7.2 bank-grade contract:** the timestamp window
360
- * is MANDATORY. Pre-v1.4.0 the helper accepted `tolerance: 0`
361
- * (silently disables the check) and `tolerance: Infinity` /
362
- * `null` / `NaN` (silently disables via `Math.abs(...) > Infinity
363
- * = false`). Customers relying on replay protection silently
364
- * lost it.
365
- *
366
- * The helper now rejects non-finite / negative / above-cap
367
- * tolerances at the boundary with a typed
368
- * `webhook_invalid_tolerance` error. Hard upper bound is 24h —
369
- * sufficient for any plausible clock-skew scenario, prevents
370
- * "Infinity by typo" from defeating replay protection.
247
+ * 5 minutes (5 * 60 * 1000). Anything older than this is rejected
248
+ * as a replay. Pass 0 to disable the replay window (NOT recommended
249
+ * — accept the trade-off only if you have a separate replay defence).
371
250
  */
372
251
  replayToleranceMs?: number;
373
252
  /**
@@ -378,21 +257,13 @@ interface VerifyWebhookOptions {
378
257
  }
379
258
  /**
380
259
  * Verify a Crossdeck-signed webhook. Returns the parsed JSON payload
381
- * on success. Throws `CrossdeckError` with one of these
382
- * distinguishable codes (v1.4.0 Phase 7.2 — pre-v1.4.0 conflated
383
- * everything under `webhook_invalid_signature`; alerting can now
384
- * separate replay-attack signals from wrong-secret signals):
385
- * - `webhook_missing_secret` — no secret configured.
386
- * - `webhook_invalid_tolerance` — caller passed Infinity / NaN /
387
- * negative / above-24h-cap `replayToleranceMs`.
388
- * - `webhook_timestamp_missing` — header absent or has no `t=`.
389
- * - `webhook_timestamp_outside_tolerance` — drift > tolerance
390
- * (replay-attack signal — split this from signature mismatch
391
- * in your alerting rules).
392
- * - `webhook_signature_mismatch` — HMAC didn't match any
393
- * configured secret (wrong-secret / rotation-drift signal).
394
- * - `webhook_payload_not_json` — signature verified but the body
395
- * isn't parseable JSON (tampered post-signing or source bug).
260
+ * on success. Throws `CrossdeckError` on:
261
+ * - missing / malformed signature header (`webhook_invalid_signature`)
262
+ * - missing secret (`webhook_missing_secret`)
263
+ * - timestamp outside replay window (`webhook_replay_window_exceeded`)
264
+ * - HMAC mismatch (`webhook_invalid_signature`)
265
+ * - non-JSON payload (`webhook_invalid_signature` — same code because
266
+ * a tampered payload that breaks JSON parses as invalid)
396
267
  *
397
268
  * `secret` accepts a single string or an array of strings (for
398
269
  * rotation). Any one match is sufficient.