@cross-deck/web 1.4.2 → 1.5.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
@@ -128,6 +128,167 @@ interface Breadcrumb {
128
128
  data?: Record<string, unknown>;
129
129
  }
130
130
 
131
+ /**
132
+ * Public, typed accessor for the bank-grade behavioural contracts
133
+ * this SDK ships. The full architecture — schema, distribution,
134
+ * audit loop, pillar taxonomy — lives in `contracts/README.md`
135
+ * at the monorepo root.
136
+ *
137
+ * Why a typed surface (vs. plain JSON access): contract IDs and
138
+ * pillar names are part of Crossdeck's public commitment to
139
+ * customers. Reading them through `CrossdeckContracts` means the
140
+ * compiler catches drift the moment a contract is renamed or
141
+ * retired. Tools that consume contracts at runtime (dashboards,
142
+ * AI assistants, customer integration tests) get the exact same
143
+ * shape every SDK ships, with no parsing layer to drift.
144
+ *
145
+ * --- BINARY STABILITY ---
146
+ * `Contract` is treated as an evolving — but back-compat — wire
147
+ * shape. Fields may be added in any minor release. Existing
148
+ * fields will not be removed or repurposed except in a major
149
+ * version bump, even if all known contracts stop using them.
150
+ * Customers can rely on `id`, `pillar`, `status`, `appliesTo`,
151
+ * `codeRef`, `testRef`, `registeredAt`, `firstRegisteredIn`,
152
+ * and `bundledIn` being present on every contract in every
153
+ * future minor/patch release of this SDK.
154
+ */
155
+ /**
156
+ * Which bank-grade pillar a contract belongs to. The taxonomy is
157
+ * deliberately small — every contract maps to exactly one. New
158
+ * pillars require a Crossdeck major-version bump.
159
+ */
160
+ type ContractPillar = "revenue" | "entitlements" | "analytics" | "webhooks" | "errors" | "lifecycle" | "identity";
161
+ /**
162
+ * Lifecycle stage of a contract.
163
+ * - `enforced`: live in this SDK and exercised by `testRef`.
164
+ * - `proposed`: registered for an upcoming release; `testRef`
165
+ * may point to a not-yet-existing file.
166
+ * - `retired`: kept for history only; the behaviour no longer
167
+ * ships. Filtered out of `CrossdeckContracts.all()` by default.
168
+ */
169
+ type ContractStatus = "enforced" | "proposed" | "retired";
170
+ /** Which SDKs (and/or `backend`) a contract is binding on. */
171
+ type ContractAppliesTo = "web" | "node" | "react-native" | "swift" | "android" | "backend";
172
+ /**
173
+ * Pointer to the test that exercises a contract clause. The
174
+ * `name` is matched verbatim against the file's text by
175
+ * `scripts/contract-audit.mjs`, so a rename without updating
176
+ * the contract aborts CI.
177
+ */
178
+ interface ContractTestRef {
179
+ readonly file: string;
180
+ readonly name: string;
181
+ }
182
+ /** One bank-grade behavioural guarantee — see `contracts/README.md`. */
183
+ interface Contract {
184
+ readonly id: string;
185
+ readonly pillar: ContractPillar;
186
+ readonly status: ContractStatus;
187
+ readonly claim: string;
188
+ readonly appliesTo: readonly ContractAppliesTo[];
189
+ readonly codeRef: readonly string[];
190
+ readonly testRef: readonly ContractTestRef[];
191
+ /** ISO-8601 date the contract was first registered. */
192
+ readonly registeredAt: string;
193
+ /** The release note / phase the contract first appeared in. Immutable. */
194
+ readonly firstRegisteredIn: string;
195
+ /** The SDK release this snapshot was bundled with, stamped at build time. */
196
+ readonly bundledIn: string;
197
+ }
198
+ /**
199
+ * Typed entry point to the bank-grade contracts bundled with this
200
+ * SDK release. Stable, side-effect-free, tree-shakeable.
201
+ *
202
+ * @example Audit at app boot
203
+ * ```ts
204
+ * import { CrossdeckContracts } from "@cross-deck/web";
205
+ *
206
+ * for (const c of CrossdeckContracts.all()) {
207
+ * console.log(`[crossdeck] ${c.id} (${c.pillar})`);
208
+ * }
209
+ * ```
210
+ *
211
+ * @example Assert a specific clause is in force
212
+ * ```ts
213
+ * const isolation = CrossdeckContracts.byId("per-user-cache-isolation");
214
+ * if (!isolation || isolation.status !== "enforced") {
215
+ * throw new Error("entitlement isolation contract is not enforced — refusing to start");
216
+ * }
217
+ * ```
218
+ */
219
+ declare const CrossdeckContracts: {
220
+ /** Every contract that applies to this SDK and is currently enforced. */
221
+ readonly all: () => readonly Contract[];
222
+ /**
223
+ * Every contract bundled with this SDK release, including
224
+ * `proposed` and `retired` entries. Use `all()` for the
225
+ * enforced-only view.
226
+ */
227
+ readonly allIncludingHistorical: () => readonly Contract[];
228
+ /** Look up a contract by its stable `id`. */
229
+ readonly byId: (id: string) => Contract | undefined;
230
+ /** Every enforced contract within a pillar. */
231
+ readonly byPillar: (pillar: ContractPillar) => readonly Contract[];
232
+ /** Filter by lifecycle status. */
233
+ readonly withStatus: (status: ContractStatus) => readonly Contract[];
234
+ /** Semver of the SDK release these contracts were bundled with. */
235
+ readonly sdkVersion: "1.5.0";
236
+ /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
237
+ readonly bundledIn: "@cross-deck/web@1.5.0";
238
+ /**
239
+ * Resolve a failing test back to the contract it exercises.
240
+ * Used by test-framework hooks (Vitest `afterEach`, XCTest
241
+ * observation, JUnit `TestWatcher`) to find the contract id of
242
+ * a failed contract test so `reportContractFailure(...)` can
243
+ * stamp the right `contract_id` on the emitted event.
244
+ *
245
+ * Match is on `testRef.name` (case-sensitive, exact). Returns
246
+ * the first contract whose `testRef` list contains a matching
247
+ * entry, regardless of pillar or status.
248
+ */
249
+ readonly findByTestName: (name: string) => Contract | undefined;
250
+ };
251
+ /**
252
+ * Input to {@link Crossdeck.reportContractFailure}. Lets a test
253
+ * harness / dogfood app / customer integration report a contract
254
+ * violation back to Crossdeck through the standard `track()` API
255
+ * with a typed property bag — no new ingest endpoint, no bespoke
256
+ * code, just a `crossdeck.contract_failed` custom event.
257
+ *
258
+ * `sdk_version` and `sdk_platform` are auto-stamped by the SDK so
259
+ * every emitted event carries them correctly without the caller
260
+ * needing to read them out of `CrossdeckContracts.sdkVersion`.
261
+ */
262
+ interface ContractFailureInput {
263
+ /** Stable contract id (`per-user-cache-isolation` etc.). */
264
+ contractId: string;
265
+ /** Human-readable failure reason — the assertion message. */
266
+ failureReason: string;
267
+ /**
268
+ * Where the failure was observed:
269
+ * - `ci` — the SDK's own test suite on CI
270
+ * - `dogfood` — Crossdeck's internal dogfood project
271
+ * - `customer-app` — a customer's app verifying contracts
272
+ */
273
+ runContext: "ci" | "dogfood" | "customer-app";
274
+ /**
275
+ * Stable identifier for this verification run. CI: `GITHUB_RUN_ID`
276
+ * or equivalent. Dogfood: per-launch UUID. Customer app: any
277
+ * stable handle the customer chooses to group fires by run.
278
+ */
279
+ runId: string;
280
+ /**
281
+ * Optional pointer back to the failing test, for triage. The SDK
282
+ * sends both `test_file` and `test_name` on the wire when set.
283
+ */
284
+ testRef?: {
285
+ file: string;
286
+ name: string;
287
+ };
288
+ /** Optional extra properties merged into the event payload. */
289
+ extra?: Record<string, unknown>;
290
+ }
291
+
131
292
  /**
132
293
  * Stack-trace parser — normalises Chrome / Firefox / Safari / Edge
133
294
  * stack strings into a common frame shape.
@@ -478,6 +639,18 @@ declare class CrossdeckClient {
478
639
  * trip happens in the background. To flush before the page unloads,
479
640
  * call flush().
480
641
  */
642
+ /**
643
+ * Emit `crossdeck.contract_failed` with the canonical property
644
+ * shape (`contract_id`, `sdk_version`, `sdk_platform`,
645
+ * `failure_reason`, `run_context`, `run_id`). Goes through the
646
+ * standard track() pipeline — same consent gate, same queue,
647
+ * same ingest, no new endpoint.
648
+ *
649
+ * Wire the call from a test hook, dogfood failure path, or
650
+ * customer contract-verification harness; see
651
+ * `contracts/README.md` for the per-test-framework hook recipes.
652
+ */
653
+ reportContractFailure(input: ContractFailureInput): void;
481
654
  track(name: string, properties?: EventProperties): void;
482
655
  /**
483
656
  * Force-flush queued events. Useful to call from page-unload handlers.
@@ -762,4 +935,4 @@ interface DeviceInfo {
762
935
  appVersion?: string;
763
936
  }
764
937
 
765
- export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, Crossdeck, CrossdeckClient, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
938
+ export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, type Contract, type ContractAppliesTo, type ContractFailureInput, type ContractPillar, type ContractStatus, type ContractTestRef, Crossdeck, CrossdeckClient, CrossdeckContracts, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
package/dist/index.d.ts CHANGED
@@ -128,6 +128,167 @@ interface Breadcrumb {
128
128
  data?: Record<string, unknown>;
129
129
  }
130
130
 
131
+ /**
132
+ * Public, typed accessor for the bank-grade behavioural contracts
133
+ * this SDK ships. The full architecture — schema, distribution,
134
+ * audit loop, pillar taxonomy — lives in `contracts/README.md`
135
+ * at the monorepo root.
136
+ *
137
+ * Why a typed surface (vs. plain JSON access): contract IDs and
138
+ * pillar names are part of Crossdeck's public commitment to
139
+ * customers. Reading them through `CrossdeckContracts` means the
140
+ * compiler catches drift the moment a contract is renamed or
141
+ * retired. Tools that consume contracts at runtime (dashboards,
142
+ * AI assistants, customer integration tests) get the exact same
143
+ * shape every SDK ships, with no parsing layer to drift.
144
+ *
145
+ * --- BINARY STABILITY ---
146
+ * `Contract` is treated as an evolving — but back-compat — wire
147
+ * shape. Fields may be added in any minor release. Existing
148
+ * fields will not be removed or repurposed except in a major
149
+ * version bump, even if all known contracts stop using them.
150
+ * Customers can rely on `id`, `pillar`, `status`, `appliesTo`,
151
+ * `codeRef`, `testRef`, `registeredAt`, `firstRegisteredIn`,
152
+ * and `bundledIn` being present on every contract in every
153
+ * future minor/patch release of this SDK.
154
+ */
155
+ /**
156
+ * Which bank-grade pillar a contract belongs to. The taxonomy is
157
+ * deliberately small — every contract maps to exactly one. New
158
+ * pillars require a Crossdeck major-version bump.
159
+ */
160
+ type ContractPillar = "revenue" | "entitlements" | "analytics" | "webhooks" | "errors" | "lifecycle" | "identity";
161
+ /**
162
+ * Lifecycle stage of a contract.
163
+ * - `enforced`: live in this SDK and exercised by `testRef`.
164
+ * - `proposed`: registered for an upcoming release; `testRef`
165
+ * may point to a not-yet-existing file.
166
+ * - `retired`: kept for history only; the behaviour no longer
167
+ * ships. Filtered out of `CrossdeckContracts.all()` by default.
168
+ */
169
+ type ContractStatus = "enforced" | "proposed" | "retired";
170
+ /** Which SDKs (and/or `backend`) a contract is binding on. */
171
+ type ContractAppliesTo = "web" | "node" | "react-native" | "swift" | "android" | "backend";
172
+ /**
173
+ * Pointer to the test that exercises a contract clause. The
174
+ * `name` is matched verbatim against the file's text by
175
+ * `scripts/contract-audit.mjs`, so a rename without updating
176
+ * the contract aborts CI.
177
+ */
178
+ interface ContractTestRef {
179
+ readonly file: string;
180
+ readonly name: string;
181
+ }
182
+ /** One bank-grade behavioural guarantee — see `contracts/README.md`. */
183
+ interface Contract {
184
+ readonly id: string;
185
+ readonly pillar: ContractPillar;
186
+ readonly status: ContractStatus;
187
+ readonly claim: string;
188
+ readonly appliesTo: readonly ContractAppliesTo[];
189
+ readonly codeRef: readonly string[];
190
+ readonly testRef: readonly ContractTestRef[];
191
+ /** ISO-8601 date the contract was first registered. */
192
+ readonly registeredAt: string;
193
+ /** The release note / phase the contract first appeared in. Immutable. */
194
+ readonly firstRegisteredIn: string;
195
+ /** The SDK release this snapshot was bundled with, stamped at build time. */
196
+ readonly bundledIn: string;
197
+ }
198
+ /**
199
+ * Typed entry point to the bank-grade contracts bundled with this
200
+ * SDK release. Stable, side-effect-free, tree-shakeable.
201
+ *
202
+ * @example Audit at app boot
203
+ * ```ts
204
+ * import { CrossdeckContracts } from "@cross-deck/web";
205
+ *
206
+ * for (const c of CrossdeckContracts.all()) {
207
+ * console.log(`[crossdeck] ${c.id} (${c.pillar})`);
208
+ * }
209
+ * ```
210
+ *
211
+ * @example Assert a specific clause is in force
212
+ * ```ts
213
+ * const isolation = CrossdeckContracts.byId("per-user-cache-isolation");
214
+ * if (!isolation || isolation.status !== "enforced") {
215
+ * throw new Error("entitlement isolation contract is not enforced — refusing to start");
216
+ * }
217
+ * ```
218
+ */
219
+ declare const CrossdeckContracts: {
220
+ /** Every contract that applies to this SDK and is currently enforced. */
221
+ readonly all: () => readonly Contract[];
222
+ /**
223
+ * Every contract bundled with this SDK release, including
224
+ * `proposed` and `retired` entries. Use `all()` for the
225
+ * enforced-only view.
226
+ */
227
+ readonly allIncludingHistorical: () => readonly Contract[];
228
+ /** Look up a contract by its stable `id`. */
229
+ readonly byId: (id: string) => Contract | undefined;
230
+ /** Every enforced contract within a pillar. */
231
+ readonly byPillar: (pillar: ContractPillar) => readonly Contract[];
232
+ /** Filter by lifecycle status. */
233
+ readonly withStatus: (status: ContractStatus) => readonly Contract[];
234
+ /** Semver of the SDK release these contracts were bundled with. */
235
+ readonly sdkVersion: "1.5.0";
236
+ /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
237
+ readonly bundledIn: "@cross-deck/web@1.5.0";
238
+ /**
239
+ * Resolve a failing test back to the contract it exercises.
240
+ * Used by test-framework hooks (Vitest `afterEach`, XCTest
241
+ * observation, JUnit `TestWatcher`) to find the contract id of
242
+ * a failed contract test so `reportContractFailure(...)` can
243
+ * stamp the right `contract_id` on the emitted event.
244
+ *
245
+ * Match is on `testRef.name` (case-sensitive, exact). Returns
246
+ * the first contract whose `testRef` list contains a matching
247
+ * entry, regardless of pillar or status.
248
+ */
249
+ readonly findByTestName: (name: string) => Contract | undefined;
250
+ };
251
+ /**
252
+ * Input to {@link Crossdeck.reportContractFailure}. Lets a test
253
+ * harness / dogfood app / customer integration report a contract
254
+ * violation back to Crossdeck through the standard `track()` API
255
+ * with a typed property bag — no new ingest endpoint, no bespoke
256
+ * code, just a `crossdeck.contract_failed` custom event.
257
+ *
258
+ * `sdk_version` and `sdk_platform` are auto-stamped by the SDK so
259
+ * every emitted event carries them correctly without the caller
260
+ * needing to read them out of `CrossdeckContracts.sdkVersion`.
261
+ */
262
+ interface ContractFailureInput {
263
+ /** Stable contract id (`per-user-cache-isolation` etc.). */
264
+ contractId: string;
265
+ /** Human-readable failure reason — the assertion message. */
266
+ failureReason: string;
267
+ /**
268
+ * Where the failure was observed:
269
+ * - `ci` — the SDK's own test suite on CI
270
+ * - `dogfood` — Crossdeck's internal dogfood project
271
+ * - `customer-app` — a customer's app verifying contracts
272
+ */
273
+ runContext: "ci" | "dogfood" | "customer-app";
274
+ /**
275
+ * Stable identifier for this verification run. CI: `GITHUB_RUN_ID`
276
+ * or equivalent. Dogfood: per-launch UUID. Customer app: any
277
+ * stable handle the customer chooses to group fires by run.
278
+ */
279
+ runId: string;
280
+ /**
281
+ * Optional pointer back to the failing test, for triage. The SDK
282
+ * sends both `test_file` and `test_name` on the wire when set.
283
+ */
284
+ testRef?: {
285
+ file: string;
286
+ name: string;
287
+ };
288
+ /** Optional extra properties merged into the event payload. */
289
+ extra?: Record<string, unknown>;
290
+ }
291
+
131
292
  /**
132
293
  * Stack-trace parser — normalises Chrome / Firefox / Safari / Edge
133
294
  * stack strings into a common frame shape.
@@ -478,6 +639,18 @@ declare class CrossdeckClient {
478
639
  * trip happens in the background. To flush before the page unloads,
479
640
  * call flush().
480
641
  */
642
+ /**
643
+ * Emit `crossdeck.contract_failed` with the canonical property
644
+ * shape (`contract_id`, `sdk_version`, `sdk_platform`,
645
+ * `failure_reason`, `run_context`, `run_id`). Goes through the
646
+ * standard track() pipeline — same consent gate, same queue,
647
+ * same ingest, no new endpoint.
648
+ *
649
+ * Wire the call from a test hook, dogfood failure path, or
650
+ * customer contract-verification harness; see
651
+ * `contracts/README.md` for the per-test-framework hook recipes.
652
+ */
653
+ reportContractFailure(input: ContractFailureInput): void;
481
654
  track(name: string, properties?: EventProperties): void;
482
655
  /**
483
656
  * Force-flush queued events. Useful to call from page-unload handlers.
@@ -762,4 +935,4 @@ interface DeviceInfo {
762
935
  appVersion?: string;
763
936
  }
764
937
 
765
- export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, Crossdeck, CrossdeckClient, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
938
+ export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, type Contract, type ContractAppliesTo, type ContractFailureInput, type ContractPillar, type ContractStatus, type ContractTestRef, Crossdeck, CrossdeckClient, CrossdeckContracts, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };