@mpgd/platform 0.20.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.
@@ -1,6 +1,6 @@
1
1
  import { type tags } from 'typia';
2
2
  import { PlatformOperationError, type CommerceAdapter, type CommerceCapabilities } from './index.js';
3
- import type { ActiveSubscription, CommerceErrorCode, CommerceErrorInfo, CommerceEvent, CommerceLogicalProductId, CommerceProvider, FetchProductsInput, FinishTransactionInput, FinishTransactionResult, GetAvailablePurchasesInput, IsoTimestamp, NonEmptyString, NonNegativeInteger, PurchaseKind, RequestPurchaseInput, RequestPurchaseResult, StoreId, StoreProduct, StorePurchase } from './commerce-contract.js';
3
+ import type { ActiveSubscription, CommerceErrorCode, CommerceErrorInfo, CommerceEvent, CommerceLogicalProductId, CommerceProvider, FetchProductsInput, FinishTransactionInput, FinishTransactionResult, GetAvailablePurchasesInput, IsoTimestamp, NonEmptyString, NonNegativeInteger, PositiveQuantity, PurchaseKind, RequestPurchaseInput, RequestPurchaseResult, StoreId, StoreProduct, StorePurchase } from './commerce-contract.js';
4
4
  export { createUnsupportedCommerceCapabilities, deriveLegacyCommerceCapabilities, type LegacyCommerceCapabilities, } from './commerce-capabilities.js';
5
5
  export type { CommerceAccountBindingPolicy, CommerceAuthorityCapabilities, CommerceCapabilities, CommerceCatalogCapabilities, CommerceEvidenceAuthority, CommerceFinishOwner, CommerceLifecycleCapabilities, CommercePurchaseCapabilities, } from './index.js';
6
6
  /**
@@ -46,6 +46,12 @@ export interface CreditSpendInput {
46
46
  readonly currency: NonEmptyString;
47
47
  readonly amount: number & tags.ExclusiveMinimum<0>;
48
48
  readonly idempotencyKey: NonEmptyString;
49
+ /**
50
+ * The offer being bought; the wallet prices the spend against it and issues
51
+ * an entitlement. Without it the spend is a plain debit of `amount` with no
52
+ * entitlement, an owner-side form the conformance vectors use; a wallet
53
+ * service should require it from game clients.
54
+ */
49
55
  readonly logicalId?: CommerceLogicalProductId;
50
56
  }
51
57
  export interface CreditDebit {
@@ -56,6 +62,8 @@ export type CreditSpendResult = {
56
62
  readonly status: 'debited';
57
63
  readonly entitlementId: NonEmptyString;
58
64
  readonly debits: readonly CreditDebit[];
65
+ /** When the wallet settled the spend; a replay repeats the original time. */
66
+ readonly createdAt?: IsoTimestamp;
59
67
  /** A repeated idempotency key returns the original debits. */
60
68
  readonly alreadyProcessed?: boolean;
61
69
  } | {
@@ -69,6 +77,8 @@ export interface CreditEntitlement {
69
77
  readonly idempotencyKey: NonEmptyString;
70
78
  readonly createdAt: IsoTimestamp;
71
79
  readonly debits: readonly CreditDebit[];
80
+ /** Units bought at spend time; the provider reports it rather than deriving one from today's price. */
81
+ readonly quantity?: PositiveQuantity;
72
82
  }
73
83
  export interface CreditBalance {
74
84
  readonly titleId: NonEmptyString;
@@ -99,6 +109,13 @@ export interface CreditLot {
99
109
  /** Derived from the origin, never supplied by the caller. */
100
110
  readonly spendScope: CreditLotSpendScope;
101
111
  }
112
+ /**
113
+ * The client a consuming project implements against its wallet service.
114
+ * `createCreditCommerceProvider` (`@mpgd/platform/commerce-wallet`) turns it
115
+ * into a `CommerceProvider`. A `spend` of a membership offer starts the
116
+ * membership under the spend's idempotency key as its subscription id; the
117
+ * wallet service alone renews, lapses, and expires it.
118
+ */
102
119
  export interface CreditWalletClient {
103
120
  readonly store: StoreId;
104
121
  listOffers(input: {
@@ -108,6 +125,7 @@ export interface CreditWalletClient {
108
125
  listUnredeemed(input: {
109
126
  readonly titleId: string;
110
127
  }): Promise<readonly CreditEntitlement[]>;
128
+ /** Redeems an entitlement for the ledger entry that granted it; the wallet service verifies the pair. */
111
129
  redeem(input: {
112
130
  readonly entitlementId: string;
113
131
  readonly ledgerEntryId: string;
@@ -117,7 +135,15 @@ export interface CreditWalletClient {
117
135
  getBalance(input: {
118
136
  readonly titleId: string;
119
137
  }): Promise<CreditBalance>;
120
- subscribe?(listener: (event: CommerceEvent) => void): () => void;
138
+ /** Memberships the wallet service reports as active for the title; absent when the wallet sells none. */
139
+ getActiveSubscriptions?(input: {
140
+ readonly titleId: string;
141
+ }): Promise<readonly ActiveSubscription[]>;
142
+ /** With `input.titleId`, only events that concern that title (platform memberships concern every title). */
143
+ subscribe?(listener: (event: CommerceEvent) => void, input?: CreditWalletSubscribeInput): () => void;
144
+ }
145
+ export interface CreditWalletSubscribeInput {
146
+ readonly titleId?: string;
121
147
  }
122
148
  export interface CommerceBridgeGetCapabilitiesPayload {
123
149
  readonly protocol: typeof commerceProtocol;
package/dist/commerce.js CHANGED
@@ -3650,7 +3650,7 @@ export const assertCreditSpendInput = (() => {
3650
3650
  })();
3651
3651
  export const assertCreditSpendResult = (() => {
3652
3652
  const _iv0 = new Set(["ACCOUNT_BINDING_REQUIRED", "ACTION_REQUIRED", "ALREADY_OWNED", "CONFIGURATION_REQUIRED", "INSUFFICIENT_BALANCE", "INVALID_RESPONSE", "IN_PROGRESS", "ITEM_UNAVAILABLE", "NETWORK", "NOT_PREPARED", "OUTCOME_UNKNOWN", "STORE_UNAVAILABLE", "UNKNOWN", "USER_CANCELLED"]);
3653
- const _ae0 = "({ readonly status: \"debited\"; readonly entitlementId: NonEmptyString; readonly debits: readonly CreditDebit[]; readonly alreadyProcessed?: boolean; } | { readonly status: \"rejected\"; readonly error: CommerceErrorInfo; })";
3653
+ const _ae0 = "({ readonly status: \"debited\"; readonly entitlementId: NonEmptyString; readonly debits: readonly CreditDebit[]; readonly createdAt?: IsoTimestamp; readonly alreadyProcessed?: boolean; } | { readonly status: \"rejected\"; readonly error: CommerceErrorInfo; })";
3654
3654
  const _av0 = new Set(["ACCOUNT_BINDING_REQUIRED", "ACTION_REQUIRED", "ALREADY_OWNED", "CONFIGURATION_REQUIRED", "INSUFFICIENT_BALANCE", "INVALID_RESPONSE", "IN_PROGRESS", "ITEM_UNAVAILABLE", "NETWORK", "NOT_PREPARED", "OUTCOME_UNKNOWN", "STORE_UNAVAILABLE", "UNKNOWN", "USER_CANCELLED"]);
3655
3655
  const _ae1 = "(\"ACCOUNT_BINDING_REQUIRED\" | \"ACTION_REQUIRED\" | \"ALREADY_OWNED\" | \"CONFIGURATION_REQUIRED\" | \"INSUFFICIENT_BALANCE\" | \"INVALID_RESPONSE\" | \"IN_PROGRESS\" | \"ITEM_UNAVAILABLE\" | \"NETWORK\" | \"NOT_PREPARED\" | \"OUTCOME_UNKNOWN\" | \"STORE_UNAVAILABLE\" | \"UNKNOWN\" | \"USER_CANCELLED\")";
3656
3656
  const _ao0 = (input, _path, _exceptionable = true) => ("debited" === input.status || _assertGuard_1._assertGuard(_exceptionable, {
@@ -3688,13 +3688,23 @@ export const assertCreditSpendResult = (() => {
3688
3688
  path: _path + ".debits",
3689
3689
  expected: "ReadonlyArray<CreditDebit>",
3690
3690
  value: input.debits
3691
+ }, _errorFactory)) && (undefined === input.createdAt || "string" === typeof input.createdAt && (_isFormatDateTime_1._isFormatDateTime(input.createdAt) || _assertGuard_1._assertGuard(_exceptionable, {
3692
+ method: "typia.createAssertEquals",
3693
+ path: _path + ".createdAt",
3694
+ expected: "string & CommerceDateTimeFormat",
3695
+ value: input.createdAt
3696
+ }, _errorFactory)) || _assertGuard_1._assertGuard(_exceptionable, {
3697
+ method: "typia.createAssertEquals",
3698
+ path: _path + ".createdAt",
3699
+ expected: "((string & CommerceDateTimeFormat) | undefined)",
3700
+ value: input.createdAt
3691
3701
  }, _errorFactory)) && (undefined === input.alreadyProcessed || "boolean" === typeof input.alreadyProcessed || _assertGuard_1._assertGuard(_exceptionable, {
3692
3702
  method: "typia.createAssertEquals",
3693
3703
  path: _path + ".alreadyProcessed",
3694
3704
  expected: "(boolean | undefined)",
3695
3705
  value: input.alreadyProcessed
3696
3706
  }, _errorFactory)) && (3 === Object.keys(input).length || (false === _exceptionable || Object.keys(input).every(key => {
3697
- if (["status", "entitlementId", "debits", "alreadyProcessed"].some(prop => key === prop))
3707
+ if (["status", "entitlementId", "debits", "createdAt", "alreadyProcessed"].some(prop => key === prop))
3698
3708
  return true;
3699
3709
  const value = input[key];
3700
3710
  if (undefined === value)
@@ -3813,8 +3823,8 @@ export const assertCreditSpendResult = (() => {
3813
3823
  value: input
3814
3824
  }, _errorFactory);
3815
3825
  })();
3816
- const _io0 = (input, _exceptionable = true) => "debited" === input.status && ("string" === typeof input.entitlementId && 1 <= input.entitlementId.length) && (Array.isArray(input.debits) && input.debits.every((elem, _index1) => "object" === typeof elem && null !== elem && _io1(elem, true && _exceptionable))) && (undefined === input.alreadyProcessed || "boolean" === typeof input.alreadyProcessed) && (3 === Object.keys(input).length || Object.keys(input).every(key => {
3817
- if (["status", "entitlementId", "debits", "alreadyProcessed"].some(prop => key === prop))
3826
+ const _io0 = (input, _exceptionable = true) => "debited" === input.status && ("string" === typeof input.entitlementId && 1 <= input.entitlementId.length) && (Array.isArray(input.debits) && input.debits.every((elem, _index1) => "object" === typeof elem && null !== elem && _io1(elem, true && _exceptionable))) && (undefined === input.createdAt || "string" === typeof input.createdAt && _isFormatDateTime_1._isFormatDateTime(input.createdAt)) && (undefined === input.alreadyProcessed || "boolean" === typeof input.alreadyProcessed) && (3 === Object.keys(input).length || Object.keys(input).every(key => {
3827
+ if (["status", "entitlementId", "debits", "createdAt", "alreadyProcessed"].some(prop => key === prop))
3818
3828
  return true;
3819
3829
  const value = input[key];
3820
3830
  if (undefined === value)
@@ -3944,8 +3954,23 @@ export const assertCreditEntitlement = (() => {
3944
3954
  path: _path + ".debits",
3945
3955
  expected: "ReadonlyArray<CreditDebit>",
3946
3956
  value: input.debits
3957
+ }, _errorFactory)) && (undefined === input.quantity || "number" === typeof input.quantity && (_isTypeUint32_1._isTypeUint32(input.quantity) || _assertGuard_1._assertGuard(_exceptionable, {
3958
+ method: "typia.createAssertEquals",
3959
+ path: _path + ".quantity",
3960
+ expected: "number & CommerceUint32",
3961
+ value: input.quantity
3962
+ }, _errorFactory)) && (1 <= input.quantity || _assertGuard_1._assertGuard(_exceptionable, {
3963
+ method: "typia.createAssertEquals",
3964
+ path: _path + ".quantity",
3965
+ expected: "number & CommerceMinimum<1>",
3966
+ value: input.quantity
3967
+ }, _errorFactory)) || _assertGuard_1._assertGuard(_exceptionable, {
3968
+ method: "typia.createAssertEquals",
3969
+ path: _path + ".quantity",
3970
+ expected: "((number & CommerceUint32 & CommerceMinimum<1>) | undefined)",
3971
+ value: input.quantity
3947
3972
  }, _errorFactory)) && (6 === Object.keys(input).length || (false === _exceptionable || Object.keys(input).every(key => {
3948
- if (["entitlementId", "titleId", "logicalId", "idempotencyKey", "createdAt", "debits"].some(prop => key === prop))
3973
+ if (["entitlementId", "titleId", "logicalId", "idempotencyKey", "createdAt", "debits", "quantity"].some(prop => key === prop))
3949
3974
  return true;
3950
3975
  const value = input[key];
3951
3976
  if (undefined === value)
@@ -3990,8 +4015,8 @@ export const assertCreditEntitlement = (() => {
3990
4015
  value: value
3991
4016
  }, _errorFactory);
3992
4017
  })));
3993
- const _io0 = (input, _exceptionable = true) => "string" === typeof input.entitlementId && 1 <= input.entitlementId.length && ("string" === typeof input.titleId && 1 <= input.titleId.length) && ("string" === typeof input.logicalId && 1 <= input.logicalId.length) && ("string" === typeof input.idempotencyKey && 1 <= input.idempotencyKey.length) && ("string" === typeof input.createdAt && _isFormatDateTime_1._isFormatDateTime(input.createdAt)) && (Array.isArray(input.debits) && input.debits.every((elem, _index1) => "object" === typeof elem && null !== elem && _io1(elem, true && _exceptionable))) && (6 === Object.keys(input).length || Object.keys(input).every(key => {
3994
- if (["entitlementId", "titleId", "logicalId", "idempotencyKey", "createdAt", "debits"].some(prop => key === prop))
4018
+ const _io0 = (input, _exceptionable = true) => "string" === typeof input.entitlementId && 1 <= input.entitlementId.length && ("string" === typeof input.titleId && 1 <= input.titleId.length) && ("string" === typeof input.logicalId && 1 <= input.logicalId.length) && ("string" === typeof input.idempotencyKey && 1 <= input.idempotencyKey.length) && ("string" === typeof input.createdAt && _isFormatDateTime_1._isFormatDateTime(input.createdAt)) && (Array.isArray(input.debits) && input.debits.every((elem, _index1) => "object" === typeof elem && null !== elem && _io1(elem, true && _exceptionable))) && (undefined === input.quantity || "number" === typeof input.quantity && (_isTypeUint32_1._isTypeUint32(input.quantity) && 1 <= input.quantity)) && (6 === Object.keys(input).length || Object.keys(input).every(key => {
4019
+ if (["entitlementId", "titleId", "logicalId", "idempotencyKey", "createdAt", "debits", "quantity"].some(prop => key === prop))
3995
4020
  return true;
3996
4021
  const value = input[key];
3997
4022
  if (undefined === value)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/platform",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Shared platform gateway, monetization, ads, leaderboard, identity, lifecycle, and storage contracts for mpgd games.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -56,6 +56,10 @@
56
56
  "./commerce-conformance": {
57
57
  "types": "./dist/commerce-conformance.d.ts",
58
58
  "default": "./dist/commerce-conformance.js"
59
+ },
60
+ "./commerce-wallet": {
61
+ "types": "./dist/commerce-wallet.d.ts",
62
+ "default": "./dist/commerce-wallet.js"
59
63
  }
60
64
  },
61
65
  "files": [
@@ -80,6 +84,6 @@
80
84
  "lint": "ttsc --noEmit",
81
85
  "format": "ttsc format",
82
86
  "fix": "ttsc fix",
83
- "test": "vitest run && node test/capability-conformance-dist-import.mjs && node test/storage-conformance-dist-import.mjs && node test/ads-dist-import.mjs && node test/commerce-dist-import.mjs && node test/commerce-conformance-dist-import.mjs && cd ../.. && node tools/run-ttsx.mjs packages/platform/test/commerce-derived-key-parity.ts"
87
+ "test": "vitest run && node test/capability-conformance-dist-import.mjs && node test/storage-conformance-dist-import.mjs && node test/ads-dist-import.mjs && node test/commerce-dist-import.mjs && node test/commerce-conformance-dist-import.mjs && node test/wallet-commerce-dist-import.mjs && cd ../.. && node tools/run-ttsx.mjs packages/platform/test/commerce-derived-key-parity.ts"
84
88
  }
85
89
  }