@mpgd/platform 0.18.0 → 0.20.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.
@@ -0,0 +1,518 @@
1
+ import { type ActiveSubscription, type CommerceBillingAuthority, type CommerceCapabilities, type CommerceErrorCode, type CommerceErrorInfo, type CommerceEvent, type CommerceEvidenceAuthority, type CommerceFinishOwner, type CommerceLogicalProductId, type CommerceProvider, type CommercePurchaseState, type CreditBalance, type CreditDebit, type CreditEntitlement, type CreditLot, type CreditLotSpendScope, type CreditMembershipTerms, type CreditOffer, type CreditSpendInput, type CreditSpendResult, type CreditWalletClient, type FetchProductsInput, type PurchaseKind, type RequestPurchaseInput, type RequestPurchaseResult, type StoreId, type StoreProduct, type StorePurchase } from './commerce.js';
2
+ import type { CommerceConformanceCatalogEntry, CommerceConformanceErrorFixture, CommerceConformanceExpectation, CommerceConformanceFinishResultFixture, CommerceConformanceLotFixture, CommerceConformanceProfile, CommerceConformancePurchaseFixture, CommerceConformanceRequestResultFixture, CommerceConformanceSubscriptionEventType, CommerceConformanceVector, CommerceConformanceVerifyDecision } from './commerce-vectors.js';
3
+ import type { PlatformEvidenceEnvelope } from './index.js';
4
+ /**
5
+ * Commerce v2 conformance runner (RFC "Conformance vectors") with the in-memory
6
+ * reference pieces every vector needs: a fake clock, a fake store with its
7
+ * provider, a fake backend and ledger, an in-memory journal, the reference
8
+ * reconciler runtime, and a reference credit wallet with its provider. Pure
9
+ * in-process code: no Phaser, DOM, network, or SDK imports. Nothing here
10
+ * grants outside the fake ledger, and the fake ledger only writes what a
11
+ * vector scripts through `queueVerify`, `push`, or `revoke`.
12
+ *
13
+ * Store (B-phase) providers run the same vectors by supplying
14
+ * `createProvider` and translating the store actor steps into their own fake
15
+ * SDK; the reconciler, journal, backend, and matcher stay shared. A vector
16
+ * flagged `profile.adaptive` then matches expectations derived from the
17
+ * provider's declared capabilities rather than its fixture profile; see
18
+ * `deriveAdaptiveCommerceConformanceExpectation` and
19
+ * `commerceConformanceAdaptiveSkipReason`.
20
+ */
21
+ export declare const commerceConformanceDefaultPlayerId = "player-1";
22
+ export declare const commerceConformanceDefaultTitleId = "title-a";
23
+ export declare const commerceConformanceDefaultClockStart = "2026-10-01T00:00:00Z";
24
+ /** An operation the vector did not script; it fails the vector with its message. */
25
+ export declare class CommerceConformanceScriptError extends Error {
26
+ readonly name = "CommerceConformanceScriptError";
27
+ }
28
+ export interface CommerceConformanceClock {
29
+ /** ISO-8601 timestamp of the fake "now". */
30
+ now(): string;
31
+ set(at: string): void;
32
+ }
33
+ export declare function createCommerceConformanceClock(initial?: string): CommerceConformanceClock;
34
+ /** Canonical `YYYY-MM-DDTHH:mm:ssZ`; milliseconds are kept only when non-zero. */
35
+ export declare function normalizeIsoTimestamp(input: string): string;
36
+ /** Adds an ISO-8601 duration (`P30D`, `P1M`, `PT12H`, ...) to a timestamp, in UTC calendar units. */
37
+ export declare function addIsoDuration(at: string, duration: string): string;
38
+ /** Capabilities of a vector profile: omitted blocks default to all supported and `available`. */
39
+ export declare function createCommerceConformanceCapabilities(profile: CommerceConformanceProfile): CommerceCapabilities;
40
+ export interface CommerceConformanceFixtureContext {
41
+ readonly profile: CommerceConformanceProfile;
42
+ readonly catalog: readonly CommerceConformanceCatalogEntry[];
43
+ readonly playerId: string;
44
+ }
45
+ /** Fake evidence for a fixture purchase: `fixture.<store>.v1` carrying the token. */
46
+ export declare function createCommerceConformanceEvidence(store: StoreId, token: string): PlatformEvidenceEnvelope;
47
+ /**
48
+ * Fills vector purchase shorthand into a typed `StorePurchase`: the profile's
49
+ * store, quantity 1, the catalog logical id of the SKU, fake evidence, and the
50
+ * account binding the profile's authority block implies.
51
+ */
52
+ export declare function fillCommerceConformancePurchase(fixture: CommerceConformancePurchaseFixture, context: CommerceConformanceFixtureContext): StorePurchase;
53
+ export declare function commerceErrorInfoOfFixture(fixture: CommerceConformanceErrorFixture): CommerceErrorInfo;
54
+ export interface CommerceConformanceFinishCall {
55
+ readonly by: 'client' | 'host';
56
+ readonly transactionId: string;
57
+ readonly ledgerEntryId: string;
58
+ readonly isConsumable: boolean;
59
+ readonly result: 'finished' | 'unfinished' | CommerceErrorCode;
60
+ }
61
+ /** The store actor of a vector: scripted answers, store-side events, and store observations. */
62
+ export interface CommerceConformanceStoreActor {
63
+ /** Calls that reached the store's purchase UI. */
64
+ readonly purchaseUiOpened: number;
65
+ readonly finishCalls: readonly CommerceConformanceFinishCall[];
66
+ complete(purchase: CommerceConformancePurchaseFixture): void;
67
+ update(purchase: CommerceConformancePurchaseFixture): void;
68
+ error(input: {
69
+ readonly logicalId?: CommerceLogicalProductId;
70
+ readonly error: CommerceConformanceErrorFixture;
71
+ }): void;
72
+ queueRequest(result: CommerceConformanceRequestResultFixture): void;
73
+ queueFinish(result: CommerceConformanceFinishResultFixture): void;
74
+ /** The backend's finalizer consumed the purchase (`lifecycle.finish: 'server'`). */
75
+ finalize?(input: {
76
+ readonly transactionId: string;
77
+ readonly ledgerEntryId: string;
78
+ }): void | Promise<void>;
79
+ /**
80
+ * The `token` this store reports for a fixture purchase when it is not the
81
+ * fixture's own: a host-order store reports the transaction id (RFC "Client
82
+ * contract"). An adaptive run replays the fixture purchases with this token
83
+ * into the reference store, so the derived verify requests carry the token
84
+ * the provider under test reports. Absent, the fixture token is used.
85
+ */
86
+ tokenOf?(purchase: CommerceConformancePurchaseFixture): string;
87
+ }
88
+ export interface CommerceConformanceStore extends CommerceConformanceStoreActor {
89
+ /** The `CommerceProvider` over this fake store. */
90
+ readonly provider: CommerceProvider;
91
+ /** Purchases the store still reports as unfinished. */
92
+ readonly purchases: readonly StorePurchase[];
93
+ readonly restoreCalls: number;
94
+ /** Removes a purchase from the unfinished list. */
95
+ markFinished(transactionId: string): void;
96
+ }
97
+ export type CreateCommerceConformanceStoreInput = CommerceConformanceFixtureContext;
98
+ /**
99
+ * Fake store: answers `launched` unless a `queueRequest` entry is queued,
100
+ * `{ finished: true }` unless a `queueFinish` entry is queued, lists reported
101
+ * purchases until they are finished, and buffers events emitted before the
102
+ * first listener with `createCommerceEventBuffer`.
103
+ */
104
+ export declare function createCommerceConformanceStore(input: CreateCommerceConformanceStoreInput): CommerceConformanceStore;
105
+ export interface CommerceVerifyRequest {
106
+ readonly idempotencyKey: string;
107
+ readonly playerId: string;
108
+ readonly store: StoreId;
109
+ readonly transactionId: string;
110
+ readonly token: string;
111
+ readonly logicalId: CommerceLogicalProductId;
112
+ readonly quantity: number;
113
+ readonly evidence: PlatformEvidenceEnvelope;
114
+ }
115
+ export interface CommerceVerifyResponse {
116
+ readonly status: 'granted' | 'pending' | 'rejected';
117
+ readonly ledgerEntryId?: string;
118
+ /** Reported for `lifecycle.finish: 'server'` stores. */
119
+ readonly finalization?: 'pending' | 'completed';
120
+ readonly alreadyProcessed?: boolean;
121
+ }
122
+ export type CommerceLedgerEntry = {
123
+ readonly entryId: string;
124
+ readonly type: 'grant';
125
+ readonly logicalId: CommerceLogicalProductId;
126
+ readonly store: StoreId;
127
+ readonly transactionId: string;
128
+ readonly idempotencyKey?: string;
129
+ /** Written by the host's fulfillment path rather than a verify call. */
130
+ readonly pushed?: boolean;
131
+ } | {
132
+ readonly entryId: string;
133
+ readonly type: 'revoke';
134
+ readonly logicalId: CommerceLogicalProductId;
135
+ readonly reverses: string;
136
+ readonly reason: string;
137
+ };
138
+ export interface CommerceLedgerStatus {
139
+ readonly ledgerEntryId: string;
140
+ readonly status: 'active' | 'reversed' | 'unknown';
141
+ readonly reversedBy?: string;
142
+ }
143
+ /** What the reference runtime needs from a backend. */
144
+ export interface CommerceConformanceBackendClient {
145
+ verifyPurchase(request: CommerceVerifyRequest): Promise<CommerceVerifyResponse>;
146
+ getLedgerStatus(ledgerEntryIds: readonly string[]): Promise<readonly CommerceLedgerStatus[]>;
147
+ }
148
+ export interface CommerceConformanceBackend extends CommerceConformanceBackendClient {
149
+ readonly verifyRequests: readonly CommerceVerifyRequest[];
150
+ readonly ledger: readonly CommerceLedgerEntry[];
151
+ queueVerify(decision: CommerceConformanceVerifyDecision): void;
152
+ /** Writes a server-push grant keyed by transaction id; a retry for the same transaction replays the original entry. */
153
+ push(input: {
154
+ readonly transactionId: string;
155
+ readonly entryId: string;
156
+ readonly logicalId: CommerceLogicalProductId;
157
+ readonly store?: StoreId;
158
+ }): void;
159
+ revoke(input: {
160
+ readonly ledgerEntryId: string;
161
+ readonly entryId: string;
162
+ readonly reason: string;
163
+ }): void;
164
+ /** Grants the ledger still holds active. */
165
+ entitlements(): readonly {
166
+ readonly id: string;
167
+ readonly logicalId: CommerceLogicalProductId;
168
+ }[];
169
+ }
170
+ export interface CreateCommerceConformanceBackendInput {
171
+ /** The server-side finalizer, invoked when a decision reports `finalization: 'completed'`. */
172
+ readonly finalize?: (input: {
173
+ readonly store: StoreId;
174
+ readonly transactionId: string;
175
+ readonly ledgerEntryId: string;
176
+ readonly logicalId: CommerceLogicalProductId;
177
+ }) => void | Promise<void>;
178
+ /**
179
+ * When it returns true, a verify request that replays a key this backend
180
+ * already granted is a finalization re-read (the reference reconciler under a
181
+ * server-owned finish): it is answered `{ granted, ledgerEntryId,
182
+ * finalization: 'completed', alreadyProcessed: true }` from the backend's own
183
+ * grant state and runs the finalizer, consuming no queued decision. Read on
184
+ * every call, so the runner can decide it once the provider's capabilities
185
+ * are known.
186
+ */
187
+ readonly finalizeOnReread?: () => boolean;
188
+ }
189
+ /**
190
+ * Fake backend: every verify call consumes the next `queueVerify` decision in
191
+ * order and is logged; an unqueued verify is an unscripted operation. A
192
+ * `granted` decision makes sure the ledger holds the entry, so a replayed
193
+ * grant and a host-pushed grant never create a second entry. With
194
+ * `finalizeOnReread`, a replayed granted key is answered as finalized from
195
+ * the backend's own state.
196
+ */
197
+ export declare function createCommerceConformanceBackend(input?: CreateCommerceConformanceBackendInput): CommerceConformanceBackend;
198
+ export interface CommerceJournalRecord {
199
+ /** The purchase idempotency key: the game's key or a derived key. */
200
+ readonly key: string;
201
+ readonly revision: number;
202
+ readonly playerId: string;
203
+ readonly store: StoreId;
204
+ readonly logicalId: CommerceLogicalProductId;
205
+ readonly kind: PurchaseKind;
206
+ readonly origin: 'game' | 'discovered';
207
+ readonly state: CommercePurchaseState;
208
+ readonly updatedAt: string;
209
+ readonly purchase?: StorePurchase;
210
+ /** Saved before the first send and resent unchanged on later passes. */
211
+ readonly request?: CommerceVerifyRequest;
212
+ readonly ledgerEntryId?: string;
213
+ readonly error?: CommerceErrorInfo;
214
+ readonly revokedBy?: string;
215
+ }
216
+ /**
217
+ * Purchase journal with the compare-and-replace semantics of
218
+ * `MonetizationOperationStore`. A game supplies a durable, atomic
219
+ * implementation; the in-memory one below exists for conformance runs.
220
+ */
221
+ export interface CommerceJournalStore {
222
+ /** Atomically insert or return the existing record for this key. */
223
+ reserve(record: CommerceJournalRecord): Promise<{
224
+ readonly created: boolean;
225
+ readonly record: CommerceJournalRecord;
226
+ }>;
227
+ read(key: string): Promise<CommerceJournalRecord | undefined>;
228
+ /** Compare-and-swap on `expectedRevision`; the stored record carries the next revision. */
229
+ replace(expectedRevision: number, record: CommerceJournalRecord): Promise<CommerceJournalRecord>;
230
+ /** Non-terminal records; terminal records stay addressable by `read`. */
231
+ listRecoverable(playerId: string): Promise<readonly CommerceJournalRecord[]>;
232
+ /** Records in `granted`, `finishing`, or `finished` kept for revocation reporting. */
233
+ listGranted(playerId: string): Promise<readonly CommerceJournalRecord[]>;
234
+ }
235
+ export interface CommerceConformanceJournal extends CommerceJournalStore {
236
+ readonly keys: readonly string[];
237
+ /** Every state revision of a key, in write order. */
238
+ history(key: string): readonly CommercePurchaseState[];
239
+ /** `history` for every key, in creation order. */
240
+ snapshot(): Readonly<Record<string, readonly CommercePurchaseState[]>>;
241
+ }
242
+ /** In-memory journal that keeps every revision so `expect.journal` can read the full sequence. */
243
+ export declare function createCommerceConformanceJournal(): CommerceConformanceJournal;
244
+ export interface CommerceConformanceRuntime {
245
+ readonly started: boolean;
246
+ /** Subscribe, read available purchases when declared, then reconcile until idle. */
247
+ start(): Promise<void>;
248
+ /** Drop in-memory state; the journal, store, ledger, and wallet are kept. */
249
+ stop(): Promise<void>;
250
+ /** One reconcile pass, then any passes that events arriving meanwhile trigger. */
251
+ reconcile(): Promise<void>;
252
+ /** Reconcile while a trigger is pending; a no-op when stopped. */
253
+ runUntilIdle(): Promise<void>;
254
+ requestPurchase(input: RequestPurchaseInput): Promise<RequestPurchaseResult>;
255
+ fetchProducts(input?: FetchProductsInput): Promise<readonly StoreProduct[]>;
256
+ }
257
+ export interface CreateCommerceConformanceRuntimeInput {
258
+ readonly provider: CommerceProvider;
259
+ readonly backend: CommerceConformanceBackendClient;
260
+ readonly journal: CommerceJournalStore;
261
+ readonly clock: CommerceConformanceClock;
262
+ readonly catalog: readonly CommerceConformanceCatalogEntry[];
263
+ readonly playerId: string;
264
+ }
265
+ /**
266
+ * Reference reconciler. Every transition is its own journal revision written
267
+ * through compare-and-replace; the backend is in-process, so there is no
268
+ * transport, but a thrown finish or verify keeps the state for the next pass
269
+ * as a transport failure would. Only a ledger entry moves a record to
270
+ * `granted`, and only a ledger revocation moves it to `revoked`.
271
+ */
272
+ export declare function createCommerceConformanceRuntime(input: CreateCommerceConformanceRuntimeInput): CommerceConformanceRuntime;
273
+ export interface CommerceScheduledDebit extends CreditDebit {
274
+ readonly idempotencyKey: string;
275
+ }
276
+ export interface CommerceConformanceSubscriptionEvent {
277
+ /** Stable id `<subscriptionId>:<periodIndex>:<type>`, so redelivery is idempotent. */
278
+ readonly eventId: string;
279
+ readonly type: CommerceConformanceSubscriptionEventType;
280
+ readonly subscriptionId: string;
281
+ readonly logicalId: CommerceLogicalProductId;
282
+ readonly periodIndex: number;
283
+ readonly expiresAt: string;
284
+ readonly reason?: CommerceErrorCode;
285
+ }
286
+ export interface CommerceConformanceMembershipStart {
287
+ readonly subscriptionId: string;
288
+ readonly titleId: string;
289
+ readonly logicalId: CommerceLogicalProductId;
290
+ readonly terms: CreditMembershipTerms;
291
+ }
292
+ export type CommerceConformanceMembershipResult = {
293
+ readonly status: 'started';
294
+ readonly subscription: ActiveSubscription;
295
+ readonly entitlementId: string;
296
+ } | {
297
+ readonly status: 'rejected';
298
+ readonly error: CommerceErrorInfo;
299
+ };
300
+ /** The wallet actor of a vector: wallet steps plus wallet observations. */
301
+ export interface CommerceConformanceWalletActor {
302
+ readonly lots: readonly CreditLot[];
303
+ /** Every debit of every processed spend, in order; replays add nothing. */
304
+ readonly debits: readonly CommerceScheduledDebit[];
305
+ readonly subscriptionEvents: readonly CommerceConformanceSubscriptionEvent[];
306
+ creditFromGrant(lot: CommerceConformanceLotFixture, options?: {
307
+ readonly spendScope?: CreditLotSpendScope;
308
+ }): CreditLot;
309
+ spend(input: CreditSpendInput): Promise<CreditSpendResult>;
310
+ /** Runs the renewal scheduler for memberships whose paid period has ended. */
311
+ renewDue(): Promise<void>;
312
+ getBalance(input: {
313
+ readonly titleId: string;
314
+ }): Promise<CreditBalance>;
315
+ getActiveSubscriptions(): Promise<readonly ActiveSubscription[]>;
316
+ /** The backend's finalizer redeemed the entitlement (`lifecycle.finish: 'server'`). */
317
+ finalize?(input: {
318
+ readonly transactionId: string;
319
+ readonly ledgerEntryId: string;
320
+ }): void | Promise<void>;
321
+ }
322
+ export interface CommerceConformanceWallet extends CreditWalletClient, CommerceConformanceWalletActor {
323
+ readonly entitlements: readonly CreditEntitlement[];
324
+ startMembership(input: CommerceConformanceMembershipStart): Promise<CommerceConformanceMembershipResult>;
325
+ subscribe(listener: (event: CommerceEvent) => void): () => void;
326
+ }
327
+ export interface CreateCommerceConformanceWalletInput {
328
+ readonly store: StoreId;
329
+ readonly clock: CommerceConformanceClock;
330
+ readonly currencies: readonly string[];
331
+ /** Offers served by `listOffers`; defaults to none. */
332
+ readonly offers?: readonly CreditOffer[];
333
+ }
334
+ /**
335
+ * Reference in-memory wallet implementing the RFC "Credit lots" and "Billing
336
+ * authority and memberships" rules: lot ids equal ledger entry ids, spend
337
+ * scope derives from the lot origin, spends consume eligible lots in the
338
+ * fixed order, are idempotent by key, and debit nothing on
339
+ * `INSUFFICIENT_BALANCE`; memberships debit `membership:<id>:<period>` per
340
+ * paid period and lapse at period end when the eligible balance runs out.
341
+ */
342
+ export declare function createCommerceConformanceWallet(input: CreateCommerceConformanceWalletInput): CommerceConformanceWallet;
343
+ /** Store lots are title-scoped, off-store lots cross-title, promotion lots as issued (title by default). */
344
+ export declare function deriveCreditLotSpendScope(origin: CreditLot['origin'], explicit?: CreditLotSpendScope): CreditLotSpendScope;
345
+ /** RFC spend order: earliest `expiresAt` (none last), `title` before `cross-title`, oldest `createdAt`, then `lotId`. */
346
+ export declare function compareCreditLotSpendOrder(left: CreditLot, right: CreditLot): number;
347
+ export interface CreateCommerceConformanceWalletProviderInput {
348
+ readonly wallet: CommerceConformanceWallet;
349
+ readonly profile: CommerceConformanceProfile;
350
+ readonly catalog: readonly CommerceConformanceCatalogEntry[];
351
+ readonly currencies: readonly string[];
352
+ readonly billingAuthority: CommerceBillingAuthority;
353
+ readonly titleId: string;
354
+ readonly clock: CommerceConformanceClock;
355
+ }
356
+ /**
357
+ * `CommerceProvider` over the reference wallet: `fetchProducts` lists the
358
+ * wallet's offers (catalog-derived offers only when the wallet lists none),
359
+ * `requestPurchase` spends the offer price times the requested quantity
360
+ * (outcome `purchased`, `settlement` set, evidence adapter-settled),
361
+ * `getAvailablePurchases` lists unredeemed entitlements, and finishing is the
362
+ * backend's `redeem`. Memberships exist only where the billing authority is
363
+ * `host`.
364
+ */
365
+ export declare function createCommerceConformanceWalletProvider(input: CreateCommerceConformanceWalletProviderInput): CommerceProvider;
366
+ export interface CommerceConformanceHarness {
367
+ readonly provider: CommerceProvider;
368
+ /** Handles `store` steps; required by store vectors. */
369
+ readonly store?: CommerceConformanceStoreActor;
370
+ /** Handles `wallet` steps; required by wallet vectors. */
371
+ readonly wallet?: CommerceConformanceWalletActor;
372
+ }
373
+ export interface CommerceConformanceContext extends CommerceConformanceFixtureContext {
374
+ readonly vector: CommerceConformanceVector;
375
+ readonly titleId: string;
376
+ readonly clock: CommerceConformanceClock;
377
+ readonly backend: CommerceConformanceBackend;
378
+ readonly journal: CommerceConformanceJournal;
379
+ }
380
+ export interface CommerceConformanceRunOptions {
381
+ /** Supplies a provider over the caller's fake SDK; defaults to the fake store or the reference wallet. */
382
+ readonly createProvider?: (context: CommerceConformanceContext) => CommerceConformanceHarness | Promise<CommerceConformanceHarness>;
383
+ readonly playerId?: string;
384
+ readonly titleId?: string;
385
+ }
386
+ export interface CommerceConformanceFailure {
387
+ readonly path: string;
388
+ readonly expected: unknown;
389
+ readonly actual: unknown;
390
+ }
391
+ export type CommerceConformanceVectorStatus = 'passed' | 'failed' | 'skipped';
392
+ /**
393
+ * `CommerceConformanceExpectation` as the matcher consumes it: a derived
394
+ * `finishCalls` list carries the store actor's own records, whose `result` may
395
+ * also be `unfinished`.
396
+ */
397
+ export interface CommerceConformanceResolvedExpectation extends Omit<CommerceConformanceExpectation, 'finishCalls'> {
398
+ readonly finishCalls?: readonly CommerceConformanceFinishCall[];
399
+ }
400
+ /** How an adaptive vector's expectations were derived for the provider under test. */
401
+ export interface CommerceConformanceAdaptation {
402
+ /** The provider's store id; it replaces the fixture store id in derived keys and verify requests. */
403
+ readonly store: StoreId;
404
+ readonly finish: CommerceFinishOwner;
405
+ readonly evidence: CommerceEvidenceAuthority;
406
+ /** The expectation the vector was matched against. */
407
+ readonly expect: CommerceConformanceResolvedExpectation;
408
+ }
409
+ export interface CommerceConformanceVectorResult {
410
+ readonly vector: string;
411
+ /** `skipped` vectors did not run; `passed` is false and `reason` says why. */
412
+ readonly status: CommerceConformanceVectorStatus;
413
+ readonly passed: boolean;
414
+ readonly failures: readonly CommerceConformanceFailure[];
415
+ readonly reason?: string;
416
+ /** Present when the vector ran with adaptive expectations. */
417
+ readonly adaptive?: CommerceConformanceAdaptation;
418
+ }
419
+ export declare function createCommerceConformanceHarness(context: CommerceConformanceContext): CommerceConformanceHarness;
420
+ /**
421
+ * Runs one vector: executes every step, lets the runtime run until idle after
422
+ * each, then matches `expect`. Matching compares only the listed fields; arrays
423
+ * compare in order and by length; a listed `journal` is the whole journal.
424
+ * Any unscripted operation fails the vector at the step that needed it.
425
+ *
426
+ * With `options.createProvider`, a vector flagged `profile.adaptive` is first
427
+ * checked against the provider's declared capabilities
428
+ * (`commerceConformanceAdaptiveSkipReason`) and then matched against
429
+ * expectations derived from a reference run under those capabilities
430
+ * (`deriveAdaptiveCommerceConformanceExpectation`). Duplicate detection needs
431
+ * the whole run set, so it applies through `runCommerceConformanceVectors`.
432
+ */
433
+ export declare function runCommerceConformanceVector(vector: CommerceConformanceVector, options?: CommerceConformanceRunOptions): Promise<CommerceConformanceVectorResult>;
434
+ export declare function runCommerceConformanceVectors(vectors: readonly CommerceConformanceVector[], options?: CommerceConformanceRunOptions): Promise<readonly CommerceConformanceVectorResult[]>;
435
+ export declare function isAdaptiveCommerceConformanceVector(vector: CommerceConformanceVector): boolean;
436
+ /**
437
+ * Why an adaptive vector does not apply to a provider with `capabilities`, or
438
+ * `undefined` when it runs. `set` is the whole run (for duplicate detection).
439
+ * The rules:
440
+ *
441
+ * - a `game.requestPurchase` of a product kind the provider does not sell
442
+ * (`purchase.<kind>` false, by the vector catalog), or with a quantity above
443
+ * one without `purchase.quantity`, cannot open the store UI;
444
+ * - a store-side `pending` purchase needs `lifecycle.pending`;
445
+ * - a store `error` event needs `lifecycle.events`;
446
+ * - without `lifecycle.events`, a store `complete` or `update` reaches the
447
+ * reconciler only by polling, so the next `runtime` step after it must be
448
+ * `reconcile` or `start`;
449
+ * - a `queueFinish` script needs a `client` or `host` finish owner, since the
450
+ * provider never calls `finishTransaction` under `server` or `none`;
451
+ * - within the `purchase-<owner>-finish` family only one member runs: the
452
+ * one named after the provider's owner, else the first by name.
453
+ */
454
+ export declare function commerceConformanceAdaptiveSkipReason(vector: CommerceConformanceVector, capabilities: CommerceCapabilities, set?: readonly CommerceConformanceVector[]): string | undefined;
455
+ /**
456
+ * The vector with every scripted store purchase (`complete`, `update`, and a
457
+ * `queueRequest` answering `purchased`) carrying the token the store actor
458
+ * reports for it (`CommerceConformanceStoreActor.tokenOf`).
459
+ */
460
+ export declare function withStoreTokens(vector: CommerceConformanceVector, tokenOf: (purchase: CommerceConformancePurchaseFixture) => string): CommerceConformanceVector;
461
+ /**
462
+ * Derives the adaptive expectation for `vector` from the observable state of a
463
+ * finished reference run under the provider's capabilities, applying the
464
+ * rules the RFC table states:
465
+ *
466
+ * - `journal`: the reference journal, so the tail after `granted` is
467
+ * `finishing, finished` for `client`, `host`, and `server` owners and
468
+ * `finished` for `none`; derived keys carry the provider's store id;
469
+ * - `finishCalls`: the reference store's calls, `by` the owner for `client`
470
+ * and `host`, none for `server` and `none`;
471
+ * - `verifyRequests`: the reference backend's log, with the finalization
472
+ * re-read a `server` owner adds unless the grant already reported
473
+ * `finalization: 'completed'`;
474
+ * - `available`: what the reference store still lists once idle, so empty
475
+ * after a completed finish for `client`, `host`, and `server` owners.
476
+ *
477
+ * Fields the fixture does not assert stay unasserted. The derivation is
478
+ * refused (a skip reason) when the reference run does not end every journal
479
+ * key in the fixture's terminal state, since the scenario then depends on a
480
+ * capability the provider lacks.
481
+ */
482
+ export declare function deriveAdaptiveCommerceConformanceExpectation(vector: CommerceConformanceVector, reference: {
483
+ readonly store: StoreId;
484
+ readonly journal: Readonly<Record<string, readonly CommercePurchaseState[]>>;
485
+ readonly verifyRequests: readonly CommerceVerifyRequest[];
486
+ readonly finishCalls: readonly CommerceConformanceFinishCall[];
487
+ /** What the reference store still lists (pending included) once the run is idle. */
488
+ readonly available: readonly StorePurchase[];
489
+ }): Promise<{
490
+ readonly expect: CommerceConformanceResolvedExpectation;
491
+ readonly reason?: undefined;
492
+ } | {
493
+ readonly expect?: undefined;
494
+ readonly reason: string;
495
+ }>;
496
+ /**
497
+ * A fixture journal key as the provider's store id derives it: only the
498
+ * encoded store segment of a `store:<store>:<transactionId>` key
499
+ * (`createDerivedPurchaseIdempotencyKey`) is replaced, so a transaction id
500
+ * that happens to contain the fixture store id is left alone. Game-chosen
501
+ * keys and store-specific key forms carry no such prefix and pass through.
502
+ */
503
+ export declare function adaptDerivedPurchaseKeyStore(key: string, fixtureStore: string, providerStore: string): string;
504
+ /**
505
+ * A fixture journal key as the provider's store id derives it. When the key is
506
+ * the derived key of a purchase the vector scripts under the fixture store, it
507
+ * is re-derived with `createDerivedPurchaseIdempotencyKey` for the provider
508
+ * store, which also covers the store-specific key forms (`google-play` token
509
+ * keys, `apps-in-toss:purchase:<id>`); otherwise only the generic
510
+ * `store:<store>:` prefix is swapped (`adaptDerivedPurchaseKeyStore`).
511
+ */
512
+ export declare function adaptDerivedPurchaseKey(vector: CommerceConformanceVector, key: string, providerStore: string): Promise<string>;
513
+ /**
514
+ * Partial match: an expected object asserts only its listed keys, an expected
515
+ * array asserts its length and each element in order, and primitives compare
516
+ * strictly. Failures name the deepest differing path.
517
+ */
518
+ export declare function matchPartial(expected: unknown, actual: unknown, path: string, failures: CommerceConformanceFailure[]): void;