@mpgd/platform 0.19.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.
@@ -1,5 +1,5 @@
1
- import { type ActiveSubscription, type CommerceBillingAuthority, type CommerceCapabilities, type CommerceErrorCode, type CommerceErrorInfo, type CommerceEvent, 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, CommerceConformanceFinishResultFixture, CommerceConformanceLotFixture, CommerceConformanceProfile, CommerceConformancePurchaseFixture, CommerceConformanceRequestResultFixture, CommerceConformanceSubscriptionEventType, CommerceConformanceVector, CommerceConformanceVerifyDecision } from './commerce-vectors.js';
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
3
  import type { PlatformEvidenceEnvelope } from './index.js';
4
4
  /**
5
5
  * Commerce v2 conformance runner (RFC "Conformance vectors") with the in-memory
@@ -12,7 +12,11 @@ import type { PlatformEvidenceEnvelope } from './index.js';
12
12
  *
13
13
  * Store (B-phase) providers run the same vectors by supplying
14
14
  * `createProvider` and translating the store actor steps into their own fake
15
- * SDK; the reconciler, journal, backend, and matcher stay shared.
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`.
16
20
  */
17
21
  export declare const commerceConformanceDefaultPlayerId = "player-1";
18
22
  export declare const commerceConformanceDefaultTitleId = "title-a";
@@ -72,6 +76,14 @@ export interface CommerceConformanceStoreActor {
72
76
  readonly transactionId: string;
73
77
  readonly ledgerEntryId: string;
74
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;
75
87
  }
76
88
  export interface CommerceConformanceStore extends CommerceConformanceStoreActor {
77
89
  /** The `CommerceProvider` over this fake store. */
@@ -163,12 +175,24 @@ export interface CreateCommerceConformanceBackendInput {
163
175
  readonly ledgerEntryId: string;
164
176
  readonly logicalId: CommerceLogicalProductId;
165
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;
166
188
  }
167
189
  /**
168
190
  * Fake backend: every verify call consumes the next `queueVerify` decision in
169
191
  * order and is logged; an unqueued verify is an unscripted operation. A
170
192
  * `granted` decision makes sure the ledger holds the entry, so a replayed
171
- * grant and a host-pushed grant never create a second entry.
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.
172
196
  */
173
197
  export declare function createCommerceConformanceBackend(input?: CreateCommerceConformanceBackendInput): CommerceConformanceBackend;
174
198
  export interface CommerceJournalRecord {
@@ -364,10 +388,33 @@ export interface CommerceConformanceFailure {
364
388
  readonly expected: unknown;
365
389
  readonly actual: unknown;
366
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
+ }
367
409
  export interface CommerceConformanceVectorResult {
368
410
  readonly vector: string;
411
+ /** `skipped` vectors did not run; `passed` is false and `reason` says why. */
412
+ readonly status: CommerceConformanceVectorStatus;
369
413
  readonly passed: boolean;
370
414
  readonly failures: readonly CommerceConformanceFailure[];
415
+ readonly reason?: string;
416
+ /** Present when the vector ran with adaptive expectations. */
417
+ readonly adaptive?: CommerceConformanceAdaptation;
371
418
  }
372
419
  export declare function createCommerceConformanceHarness(context: CommerceConformanceContext): CommerceConformanceHarness;
373
420
  /**
@@ -375,9 +422,94 @@ export declare function createCommerceConformanceHarness(context: CommerceConfor
375
422
  * each, then matches `expect`. Matching compares only the listed fields; arrays
376
423
  * compare in order and by length; a listed `journal` is the whole journal.
377
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`.
378
432
  */
379
433
  export declare function runCommerceConformanceVector(vector: CommerceConformanceVector, options?: CommerceConformanceRunOptions): Promise<CommerceConformanceVectorResult>;
380
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>;
381
513
  /**
382
514
  * Partial match: an expected object asserts only its listed keys, an expected
383
515
  * array asserts its length and each element in order, and primitives compare