@mpgd/platform 0.19.0 → 0.21.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/commerce-conformance.d.ts +160 -53
- package/dist/commerce-conformance.js +550 -485
- package/dist/commerce-contract.d.ts +222 -0
- package/dist/commerce-contract.js +1 -0
- package/dist/commerce-vectors.d.ts +8 -0
- package/dist/commerce-vectors.js +38 -33
- package/dist/commerce-wallet.d.ts +324 -0
- package/dist/commerce-wallet.js +2087 -0
- package/dist/commerce.d.ts +30 -146
- package/dist/commerce.js +260 -235
- package/dist/index.d.ts +8 -0
- package/package.json +6 -2
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { type ActiveSubscription, type CommerceBillingAuthority, type CommerceCapabilities, type CommerceErrorCode, type CommerceErrorInfo, type CommerceEvent, type
|
|
2
|
-
import type { CommerceConformanceCatalogEntry, CommerceConformanceErrorFixture, CommerceConformanceFinishResultFixture, CommerceConformanceLotFixture, CommerceConformanceProfile, CommerceConformancePurchaseFixture, CommerceConformanceRequestResultFixture,
|
|
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 CreditEntitlement, type CreditLot, type CreditLotSpendScope, type CreditOffer, type CreditSpendInput, type CreditSpendResult, type CreditWalletClient, type CreditWalletSubscribeInput, 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, CommerceConformanceVector, CommerceConformanceVerifyDecision } from './commerce-vectors.js';
|
|
3
|
+
import { type CreditMembershipStartInput, type CreditMembershipStartResult, type CreditScheduledDebit, type CreditSubscriptionEvent } from './commerce-wallet.js';
|
|
3
4
|
import type { PlatformEvidenceEnvelope } from './index.js';
|
|
5
|
+
export { addIsoDuration, compareCreditLotSpendOrder, deriveCreditLotSpendScope, normalizeIsoTimestamp, } from './commerce-wallet.js';
|
|
4
6
|
/**
|
|
5
7
|
* Commerce v2 conformance runner (RFC "Conformance vectors") with the in-memory
|
|
6
8
|
* reference pieces every vector needs: a fake clock, a fake store with its
|
|
@@ -12,7 +14,11 @@ import type { PlatformEvidenceEnvelope } from './index.js';
|
|
|
12
14
|
*
|
|
13
15
|
* Store (B-phase) providers run the same vectors by supplying
|
|
14
16
|
* `createProvider` and translating the store actor steps into their own fake
|
|
15
|
-
* SDK; the reconciler, journal, backend, and matcher stay shared.
|
|
17
|
+
* SDK; the reconciler, journal, backend, and matcher stay shared. A vector
|
|
18
|
+
* flagged `profile.adaptive` then matches expectations derived from the
|
|
19
|
+
* provider's declared capabilities rather than its fixture profile; see
|
|
20
|
+
* `deriveAdaptiveCommerceConformanceExpectation` and
|
|
21
|
+
* `commerceConformanceAdaptiveSkipReason`.
|
|
16
22
|
*/
|
|
17
23
|
export declare const commerceConformanceDefaultPlayerId = "player-1";
|
|
18
24
|
export declare const commerceConformanceDefaultTitleId = "title-a";
|
|
@@ -27,10 +33,6 @@ export interface CommerceConformanceClock {
|
|
|
27
33
|
set(at: string): void;
|
|
28
34
|
}
|
|
29
35
|
export declare function createCommerceConformanceClock(initial?: string): CommerceConformanceClock;
|
|
30
|
-
/** Canonical `YYYY-MM-DDTHH:mm:ssZ`; milliseconds are kept only when non-zero. */
|
|
31
|
-
export declare function normalizeIsoTimestamp(input: string): string;
|
|
32
|
-
/** Adds an ISO-8601 duration (`P30D`, `P1M`, `PT12H`, ...) to a timestamp, in UTC calendar units. */
|
|
33
|
-
export declare function addIsoDuration(at: string, duration: string): string;
|
|
34
36
|
/** Capabilities of a vector profile: omitted blocks default to all supported and `available`. */
|
|
35
37
|
export declare function createCommerceConformanceCapabilities(profile: CommerceConformanceProfile): CommerceCapabilities;
|
|
36
38
|
export interface CommerceConformanceFixtureContext {
|
|
@@ -72,6 +74,14 @@ export interface CommerceConformanceStoreActor {
|
|
|
72
74
|
readonly transactionId: string;
|
|
73
75
|
readonly ledgerEntryId: string;
|
|
74
76
|
}): void | Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* The `token` this store reports for a fixture purchase when it is not the
|
|
79
|
+
* fixture's own: a host-order store reports the transaction id (RFC "Client
|
|
80
|
+
* contract"). An adaptive run replays the fixture purchases with this token
|
|
81
|
+
* into the reference store, so the derived verify requests carry the token
|
|
82
|
+
* the provider under test reports. Absent, the fixture token is used.
|
|
83
|
+
*/
|
|
84
|
+
tokenOf?(purchase: CommerceConformancePurchaseFixture): string;
|
|
75
85
|
}
|
|
76
86
|
export interface CommerceConformanceStore extends CommerceConformanceStoreActor {
|
|
77
87
|
/** The `CommerceProvider` over this fake store. */
|
|
@@ -163,12 +173,24 @@ export interface CreateCommerceConformanceBackendInput {
|
|
|
163
173
|
readonly ledgerEntryId: string;
|
|
164
174
|
readonly logicalId: CommerceLogicalProductId;
|
|
165
175
|
}) => void | Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* When it returns true, a verify request that replays a key this backend
|
|
178
|
+
* already granted is a finalization re-read (the reference reconciler under a
|
|
179
|
+
* server-owned finish): it is answered `{ granted, ledgerEntryId,
|
|
180
|
+
* finalization: 'completed', alreadyProcessed: true }` from the backend's own
|
|
181
|
+
* grant state and runs the finalizer, consuming no queued decision. Read on
|
|
182
|
+
* every call, so the runner can decide it once the provider's capabilities
|
|
183
|
+
* are known.
|
|
184
|
+
*/
|
|
185
|
+
readonly finalizeOnReread?: () => boolean;
|
|
166
186
|
}
|
|
167
187
|
/**
|
|
168
188
|
* Fake backend: every verify call consumes the next `queueVerify` decision in
|
|
169
189
|
* order and is logged; an unqueued verify is an unscripted operation. A
|
|
170
190
|
* `granted` decision makes sure the ledger holds the entry, so a replayed
|
|
171
|
-
* grant and a host-pushed grant never create a second entry.
|
|
191
|
+
* grant and a host-pushed grant never create a second entry. With
|
|
192
|
+
* `finalizeOnReread`, a replayed granted key is answered as finalized from
|
|
193
|
+
* the backend's own state.
|
|
172
194
|
*/
|
|
173
195
|
export declare function createCommerceConformanceBackend(input?: CreateCommerceConformanceBackendInput): CommerceConformanceBackend;
|
|
174
196
|
export interface CommerceJournalRecord {
|
|
@@ -246,33 +268,11 @@ export interface CreateCommerceConformanceRuntimeInput {
|
|
|
246
268
|
* `granted`, and only a ledger revocation moves it to `revoked`.
|
|
247
269
|
*/
|
|
248
270
|
export declare function createCommerceConformanceRuntime(input: CreateCommerceConformanceRuntimeInput): CommerceConformanceRuntime;
|
|
249
|
-
export
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
export
|
|
253
|
-
|
|
254
|
-
readonly eventId: string;
|
|
255
|
-
readonly type: CommerceConformanceSubscriptionEventType;
|
|
256
|
-
readonly subscriptionId: string;
|
|
257
|
-
readonly logicalId: CommerceLogicalProductId;
|
|
258
|
-
readonly periodIndex: number;
|
|
259
|
-
readonly expiresAt: string;
|
|
260
|
-
readonly reason?: CommerceErrorCode;
|
|
261
|
-
}
|
|
262
|
-
export interface CommerceConformanceMembershipStart {
|
|
263
|
-
readonly subscriptionId: string;
|
|
264
|
-
readonly titleId: string;
|
|
265
|
-
readonly logicalId: CommerceLogicalProductId;
|
|
266
|
-
readonly terms: CreditMembershipTerms;
|
|
267
|
-
}
|
|
268
|
-
export type CommerceConformanceMembershipResult = {
|
|
269
|
-
readonly status: 'started';
|
|
270
|
-
readonly subscription: ActiveSubscription;
|
|
271
|
-
readonly entitlementId: string;
|
|
272
|
-
} | {
|
|
273
|
-
readonly status: 'rejected';
|
|
274
|
-
readonly error: CommerceErrorInfo;
|
|
275
|
-
};
|
|
271
|
+
export type CommerceScheduledDebit = CreditScheduledDebit;
|
|
272
|
+
/** Stable id `<subscriptionId>:<periodIndex>:<type>`, so redelivery is idempotent. */
|
|
273
|
+
export type CommerceConformanceSubscriptionEvent = CreditSubscriptionEvent;
|
|
274
|
+
export type CommerceConformanceMembershipStart = CreditMembershipStartInput;
|
|
275
|
+
export type CommerceConformanceMembershipResult = CreditMembershipStartResult;
|
|
276
276
|
/** The wallet actor of a vector: wallet steps plus wallet observations. */
|
|
277
277
|
export interface CommerceConformanceWalletActor {
|
|
278
278
|
readonly lots: readonly CreditLot[];
|
|
@@ -297,8 +297,14 @@ export interface CommerceConformanceWalletActor {
|
|
|
297
297
|
}
|
|
298
298
|
export interface CommerceConformanceWallet extends CreditWalletClient, CommerceConformanceWalletActor {
|
|
299
299
|
readonly entitlements: readonly CreditEntitlement[];
|
|
300
|
+
/** Offers the wallet prices spends against; the provider registers the vector catalog when empty. */
|
|
301
|
+
readonly offers: readonly CreditOffer[];
|
|
302
|
+
addOffers(offers: readonly CreditOffer[]): void;
|
|
300
303
|
startMembership(input: CommerceConformanceMembershipStart): Promise<CommerceConformanceMembershipResult>;
|
|
301
|
-
|
|
304
|
+
getActiveSubscriptions(input?: {
|
|
305
|
+
readonly titleId?: string;
|
|
306
|
+
}): Promise<readonly ActiveSubscription[]>;
|
|
307
|
+
subscribe(listener: (event: CommerceEvent) => void, input?: CreditWalletSubscribeInput): () => void;
|
|
302
308
|
}
|
|
303
309
|
export interface CreateCommerceConformanceWalletInput {
|
|
304
310
|
readonly store: StoreId;
|
|
@@ -308,18 +314,12 @@ export interface CreateCommerceConformanceWalletInput {
|
|
|
308
314
|
readonly offers?: readonly CreditOffer[];
|
|
309
315
|
}
|
|
310
316
|
/**
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* `INSUFFICIENT_BALANCE`; memberships debit `membership:<id>:<period>` per
|
|
316
|
-
* paid period and lapse at period end when the eligible balance runs out.
|
|
317
|
+
* The reference wallet (`createInMemoryCreditWallet`) on the vector clock,
|
|
318
|
+
* with the actor surface: fixture lots credit through `creditFromGrant`, the
|
|
319
|
+
* backend finalizer redeems through `finalize`, and owner-side mistakes in a
|
|
320
|
+
* vector surface as script errors.
|
|
317
321
|
*/
|
|
318
322
|
export declare function createCommerceConformanceWallet(input: CreateCommerceConformanceWalletInput): CommerceConformanceWallet;
|
|
319
|
-
/** Store lots are title-scoped, off-store lots cross-title, promotion lots as issued (title by default). */
|
|
320
|
-
export declare function deriveCreditLotSpendScope(origin: CreditLot['origin'], explicit?: CreditLotSpendScope): CreditLotSpendScope;
|
|
321
|
-
/** RFC spend order: earliest `expiresAt` (none last), `title` before `cross-title`, oldest `createdAt`, then `lotId`. */
|
|
322
|
-
export declare function compareCreditLotSpendOrder(left: CreditLot, right: CreditLot): number;
|
|
323
323
|
export interface CreateCommerceConformanceWalletProviderInput {
|
|
324
324
|
readonly wallet: CommerceConformanceWallet;
|
|
325
325
|
readonly profile: CommerceConformanceProfile;
|
|
@@ -330,13 +330,12 @@ export interface CreateCommerceConformanceWalletProviderInput {
|
|
|
330
330
|
readonly clock: CommerceConformanceClock;
|
|
331
331
|
}
|
|
332
332
|
/**
|
|
333
|
-
* `
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* `host`.
|
|
333
|
+
* `createCreditCommerceProvider` over the reference wallet with the vector
|
|
334
|
+
* profile as its capability block. A wallet that lists no offers has the
|
|
335
|
+
* vector catalog registered as its offers first (a fixture price of one unit
|
|
336
|
+
* of the first wallet currency for entries without membership terms), so the
|
|
337
|
+
* wallet prices every spend and starts memberships itself. Memberships exist
|
|
338
|
+
* only where the billing authority is `host`.
|
|
340
339
|
*/
|
|
341
340
|
export declare function createCommerceConformanceWalletProvider(input: CreateCommerceConformanceWalletProviderInput): CommerceProvider;
|
|
342
341
|
export interface CommerceConformanceHarness {
|
|
@@ -364,10 +363,33 @@ export interface CommerceConformanceFailure {
|
|
|
364
363
|
readonly expected: unknown;
|
|
365
364
|
readonly actual: unknown;
|
|
366
365
|
}
|
|
366
|
+
export type CommerceConformanceVectorStatus = 'passed' | 'failed' | 'skipped';
|
|
367
|
+
/**
|
|
368
|
+
* `CommerceConformanceExpectation` as the matcher consumes it: a derived
|
|
369
|
+
* `finishCalls` list carries the store actor's own records, whose `result` may
|
|
370
|
+
* also be `unfinished`.
|
|
371
|
+
*/
|
|
372
|
+
export interface CommerceConformanceResolvedExpectation extends Omit<CommerceConformanceExpectation, 'finishCalls'> {
|
|
373
|
+
readonly finishCalls?: readonly CommerceConformanceFinishCall[];
|
|
374
|
+
}
|
|
375
|
+
/** How an adaptive vector's expectations were derived for the provider under test. */
|
|
376
|
+
export interface CommerceConformanceAdaptation {
|
|
377
|
+
/** The provider's store id; it replaces the fixture store id in derived keys and verify requests. */
|
|
378
|
+
readonly store: StoreId;
|
|
379
|
+
readonly finish: CommerceFinishOwner;
|
|
380
|
+
readonly evidence: CommerceEvidenceAuthority;
|
|
381
|
+
/** The expectation the vector was matched against. */
|
|
382
|
+
readonly expect: CommerceConformanceResolvedExpectation;
|
|
383
|
+
}
|
|
367
384
|
export interface CommerceConformanceVectorResult {
|
|
368
385
|
readonly vector: string;
|
|
386
|
+
/** `skipped` vectors did not run; `passed` is false and `reason` says why. */
|
|
387
|
+
readonly status: CommerceConformanceVectorStatus;
|
|
369
388
|
readonly passed: boolean;
|
|
370
389
|
readonly failures: readonly CommerceConformanceFailure[];
|
|
390
|
+
readonly reason?: string;
|
|
391
|
+
/** Present when the vector ran with adaptive expectations. */
|
|
392
|
+
readonly adaptive?: CommerceConformanceAdaptation;
|
|
371
393
|
}
|
|
372
394
|
export declare function createCommerceConformanceHarness(context: CommerceConformanceContext): CommerceConformanceHarness;
|
|
373
395
|
/**
|
|
@@ -375,9 +397,94 @@ export declare function createCommerceConformanceHarness(context: CommerceConfor
|
|
|
375
397
|
* each, then matches `expect`. Matching compares only the listed fields; arrays
|
|
376
398
|
* compare in order and by length; a listed `journal` is the whole journal.
|
|
377
399
|
* Any unscripted operation fails the vector at the step that needed it.
|
|
400
|
+
*
|
|
401
|
+
* With `options.createProvider`, a vector flagged `profile.adaptive` is first
|
|
402
|
+
* checked against the provider's declared capabilities
|
|
403
|
+
* (`commerceConformanceAdaptiveSkipReason`) and then matched against
|
|
404
|
+
* expectations derived from a reference run under those capabilities
|
|
405
|
+
* (`deriveAdaptiveCommerceConformanceExpectation`). Duplicate detection needs
|
|
406
|
+
* the whole run set, so it applies through `runCommerceConformanceVectors`.
|
|
378
407
|
*/
|
|
379
408
|
export declare function runCommerceConformanceVector(vector: CommerceConformanceVector, options?: CommerceConformanceRunOptions): Promise<CommerceConformanceVectorResult>;
|
|
380
409
|
export declare function runCommerceConformanceVectors(vectors: readonly CommerceConformanceVector[], options?: CommerceConformanceRunOptions): Promise<readonly CommerceConformanceVectorResult[]>;
|
|
410
|
+
export declare function isAdaptiveCommerceConformanceVector(vector: CommerceConformanceVector): boolean;
|
|
411
|
+
/**
|
|
412
|
+
* Why an adaptive vector does not apply to a provider with `capabilities`, or
|
|
413
|
+
* `undefined` when it runs. `set` is the whole run (for duplicate detection).
|
|
414
|
+
* The rules:
|
|
415
|
+
*
|
|
416
|
+
* - a `game.requestPurchase` of a product kind the provider does not sell
|
|
417
|
+
* (`purchase.<kind>` false, by the vector catalog), or with a quantity above
|
|
418
|
+
* one without `purchase.quantity`, cannot open the store UI;
|
|
419
|
+
* - a store-side `pending` purchase needs `lifecycle.pending`;
|
|
420
|
+
* - a store `error` event needs `lifecycle.events`;
|
|
421
|
+
* - without `lifecycle.events`, a store `complete` or `update` reaches the
|
|
422
|
+
* reconciler only by polling, so the next `runtime` step after it must be
|
|
423
|
+
* `reconcile` or `start`;
|
|
424
|
+
* - a `queueFinish` script needs a `client` or `host` finish owner, since the
|
|
425
|
+
* provider never calls `finishTransaction` under `server` or `none`;
|
|
426
|
+
* - within the `purchase-<owner>-finish` family only one member runs: the
|
|
427
|
+
* one named after the provider's owner, else the first by name.
|
|
428
|
+
*/
|
|
429
|
+
export declare function commerceConformanceAdaptiveSkipReason(vector: CommerceConformanceVector, capabilities: CommerceCapabilities, set?: readonly CommerceConformanceVector[]): string | undefined;
|
|
430
|
+
/**
|
|
431
|
+
* The vector with every scripted store purchase (`complete`, `update`, and a
|
|
432
|
+
* `queueRequest` answering `purchased`) carrying the token the store actor
|
|
433
|
+
* reports for it (`CommerceConformanceStoreActor.tokenOf`).
|
|
434
|
+
*/
|
|
435
|
+
export declare function withStoreTokens(vector: CommerceConformanceVector, tokenOf: (purchase: CommerceConformancePurchaseFixture) => string): CommerceConformanceVector;
|
|
436
|
+
/**
|
|
437
|
+
* Derives the adaptive expectation for `vector` from the observable state of a
|
|
438
|
+
* finished reference run under the provider's capabilities, applying the
|
|
439
|
+
* rules the RFC table states:
|
|
440
|
+
*
|
|
441
|
+
* - `journal`: the reference journal, so the tail after `granted` is
|
|
442
|
+
* `finishing, finished` for `client`, `host`, and `server` owners and
|
|
443
|
+
* `finished` for `none`; derived keys carry the provider's store id;
|
|
444
|
+
* - `finishCalls`: the reference store's calls, `by` the owner for `client`
|
|
445
|
+
* and `host`, none for `server` and `none`;
|
|
446
|
+
* - `verifyRequests`: the reference backend's log, with the finalization
|
|
447
|
+
* re-read a `server` owner adds unless the grant already reported
|
|
448
|
+
* `finalization: 'completed'`;
|
|
449
|
+
* - `available`: what the reference store still lists once idle, so empty
|
|
450
|
+
* after a completed finish for `client`, `host`, and `server` owners.
|
|
451
|
+
*
|
|
452
|
+
* Fields the fixture does not assert stay unasserted. The derivation is
|
|
453
|
+
* refused (a skip reason) when the reference run does not end every journal
|
|
454
|
+
* key in the fixture's terminal state, since the scenario then depends on a
|
|
455
|
+
* capability the provider lacks.
|
|
456
|
+
*/
|
|
457
|
+
export declare function deriveAdaptiveCommerceConformanceExpectation(vector: CommerceConformanceVector, reference: {
|
|
458
|
+
readonly store: StoreId;
|
|
459
|
+
readonly journal: Readonly<Record<string, readonly CommercePurchaseState[]>>;
|
|
460
|
+
readonly verifyRequests: readonly CommerceVerifyRequest[];
|
|
461
|
+
readonly finishCalls: readonly CommerceConformanceFinishCall[];
|
|
462
|
+
/** What the reference store still lists (pending included) once the run is idle. */
|
|
463
|
+
readonly available: readonly StorePurchase[];
|
|
464
|
+
}): Promise<{
|
|
465
|
+
readonly expect: CommerceConformanceResolvedExpectation;
|
|
466
|
+
readonly reason?: undefined;
|
|
467
|
+
} | {
|
|
468
|
+
readonly expect?: undefined;
|
|
469
|
+
readonly reason: string;
|
|
470
|
+
}>;
|
|
471
|
+
/**
|
|
472
|
+
* A fixture journal key as the provider's store id derives it: only the
|
|
473
|
+
* encoded store segment of a `store:<store>:<transactionId>` key
|
|
474
|
+
* (`createDerivedPurchaseIdempotencyKey`) is replaced, so a transaction id
|
|
475
|
+
* that happens to contain the fixture store id is left alone. Game-chosen
|
|
476
|
+
* keys and store-specific key forms carry no such prefix and pass through.
|
|
477
|
+
*/
|
|
478
|
+
export declare function adaptDerivedPurchaseKeyStore(key: string, fixtureStore: string, providerStore: string): string;
|
|
479
|
+
/**
|
|
480
|
+
* A fixture journal key as the provider's store id derives it. When the key is
|
|
481
|
+
* the derived key of a purchase the vector scripts under the fixture store, it
|
|
482
|
+
* is re-derived with `createDerivedPurchaseIdempotencyKey` for the provider
|
|
483
|
+
* store, which also covers the store-specific key forms (`google-play` token
|
|
484
|
+
* keys, `apps-in-toss:purchase:<id>`); otherwise only the generic
|
|
485
|
+
* `store:<store>:` prefix is swapped (`adaptDerivedPurchaseKeyStore`).
|
|
486
|
+
*/
|
|
487
|
+
export declare function adaptDerivedPurchaseKey(vector: CommerceConformanceVector, key: string, providerStore: string): Promise<string>;
|
|
381
488
|
/**
|
|
382
489
|
* Partial match: an expected object asserts only its listed keys, an expected
|
|
383
490
|
* array asserts its length and each element in order, and primitives compare
|