@mpgd/capacitor-storekit 0.4.4 → 0.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/README.md CHANGED
@@ -61,10 +61,12 @@ once (a current non-consumable entitlement the native side marks
61
61
  still be verified; the server ledger dedupes. A finished consumable is not
62
62
  listed. A `revokedAt` maps to `state: 'revoked'`, and one malformed
63
63
  transaction is reported as a `purchaseError` without blocking its siblings);
64
- `finishTransaction({ transactionId, ledgerEntryId })` resolves the session
65
- app account token and finishes only a listed, non-revoked transaction bound
66
- to that token, or a durable of another token a restore shared to this
67
- player (see Recovery);
64
+ `finishTransaction({ transactionId, ledgerEntryId, isConsumable, retry? })`
65
+ resolves the session app account token and finishes only a listed,
66
+ non-revoked transaction bound to that token, or a durable of another token a
67
+ restore shared to this player (see Recovery), and on a retry (`retry: true`)
68
+ answers `finished: true` without a native call for a consumable the list no
69
+ longer carries (a lost acknowledgement, see Recovery);
68
70
  `restorePurchases` over `sync`; `transactionUpdated` as `purchaseUpdated`;
69
71
  and an availability probe as `getAvailability`, probing the configured
70
72
  SKUs batch by batch until one is sold. Every purchase carries the
@@ -105,7 +107,7 @@ consumables of another token stay excluded. The native
105
107
  or a `purchaseError` subscriber exists.
106
108
 
107
109
  `createCapacitorStoreKitCommerceProvider({ products, getAppAccountToken,
108
- bundleId?, sdk?, isIos?, mapError?, onDiagnostic? })` builds the provider
110
+ purchaseGate?, bundleId?, sdk?, isIos?, mapError?, onDiagnostic? })` builds the provider
109
111
  with `authority: { evidence: 'client-token', accountBinding: 'required' }`
110
112
  and evidence `{ schema: 'app-store.signed-transaction.v1', payload: {
111
113
  signedTransaction, transactionId, originalTransactionId?, bundleId? } }`,
@@ -115,7 +117,18 @@ its `signedTransactionVerifier` (authenticity and transaction id binding)
115
117
  and then, by default, decides the grant on the transaction's current status
116
118
  from the App Store Server API, so a JWS retained from before a refund never
117
119
  grants; see the backend guide for the opt-in offline-only mode. Subscription
118
- mappings are rejected at construction. `createCapacitorStoreKitServiceProvider`
120
+ mappings are rejected at construction. `getAppAccountToken` also filters
121
+ recovery and finishing to the current player, so it must keep answering
122
+ while sales are paused: pause new sales with the optional `purchaseGate`
123
+ instead, which is asked only before a new purchase opens the StoreKit sheet
124
+ (never by recovery, restore, or finishing) and refuses with `failed`,
125
+ `ITEM_UNAVAILABLE`, and `PURCHASES_PAUSED` (see the adapter-capacitor
126
+ README). With an adapter-capacitor from before
127
+ the gate, which would ignore it, the plugin detects the missing gate support
128
+ and answers every new purchase itself with a `failed` result, `ITEM_UNAVAILABLE` and
129
+ `PURCHASE_GATE_UNSUPPORTED` instead of selling; recovery is unaffected.
130
+ A token that cannot be resolved is a `failed` result with
131
+ `STOREKIT_ACCOUNT_LOOKUP_FAILED` (a thrown `CommerceError` keeps its own code). `createCapacitorStoreKitServiceProvider`
119
132
  wraps the provider as a `CapacitorServiceProvider` (id `apple-storekit` by
120
133
  default, no `features`, no `methods`) so the gateway serves the six
121
134
  `commerce.v2.*` bridge methods from it, exposes it as `gateway.commerce`, and
@@ -131,13 +144,17 @@ Finishing never grants. The SDK finishes any transaction it is given a
131
144
  (`createCommerceReconciler` in `@mpgd/game-services`) is the gate, and it
132
145
  calls `finishTransaction` only for a journal record in `granted` or
133
146
  `finishing` that holds the ledger entry id the backend grant wrote
134
- (`assertCommerceFinishAllowed`). The journal revision the grant wrote is the
135
- pairing, and the real control stays server-side, in the verifier that checks
136
- the JWS before the ledger grant, and in the native plugin never granting
137
- anything on finish. `src/commerce-reconciler-storekit-smoke.test.ts` shows the
138
- reconciler over the fake plugin: a grant finishes with the ledger entry, a
139
- rejection and an outage never finish, a failed finish is retried, and another
140
- account's transaction is never submitted.
147
+ (`assertCommerceFinishAllowed`), or for a `finished` consumable StoreKit
148
+ lists again, with that same ledger entry (see Recovery). The journal revision
149
+ the grant wrote is the pairing, and the real control stays server-side, in
150
+ the verifier that checks the JWS before the ledger grant, and in the native
151
+ plugin never granting anything on finish.
152
+ `src/commerce-reconciler-storekit-smoke.test.ts` shows the reconciler over
153
+ the fake plugin: a grant finishes with the ledger entry, a rejection and an
154
+ outage never finish, a failed finish is retried, another account's
155
+ transaction is never submitted, a finish whose answer was lost ends
156
+ `finished` on the next run, a transaction listed again is finished again
157
+ without a second verify, and an Ask to Buy purchase finishes once approved.
141
158
 
142
159
  The conformance test runs every store vector whose profile the App Store
143
160
  supports against a fake plugin, directly and through the gateway bridge path,
@@ -211,6 +228,44 @@ separately on the server. `restorePurchases` invokes the user-initiated
211
228
  entitlements. Subscriptions are not supported by this package; they require a
212
229
  separate subscription status and renewal flow.
213
230
 
231
+ A finish whose answer is lost after StoreKit applied it (the app is closed
232
+ between the native finish and the journal write) leaves the record
233
+ `finishing`. The reconciler's next finish of a record that was already
234
+ `finishing` carries `retry: true`, and on a retry the SDK reads the
235
+ consumable missing from `getTransactions` as finished: StoreKit keeps a
236
+ transaction in
237
+ [`Transaction.unfinished`](https://developer.apple.com/documentation/storekit/transaction/unfinished)
238
+ until `finish()`, and a finished consumable is in neither that list nor
239
+ `currentEntitlements`, so the SDK answers `finished: true` without a native
240
+ call and the record ends `finished`. The first finish after the grant never
241
+ does: right after a purchase the list may not carry the new transaction yet,
242
+ so a missing one stays `{ finished: false }` and the reconciler retries (by
243
+ then StoreKit lists it and it is finished natively). A caller that never
244
+ sets `retry` (an older reconciler) keeps the earlier behaviour. The
245
+ reconciler finishes only records holding a ledger grant, and nothing is
246
+ verified again. A durable missing from the list is never answered finished
247
+ (a finished durable stays listed with `unfinished: false`, so a missing one
248
+ is revoked or unverifiable), nor is an entry of that id in a malformed
249
+ shape. The kind of a missing transaction is the caller's `isConsumable`
250
+ (the list no longer says, and reading `Transaction.all` would need a native
251
+ change); a durable the catalog maps as a consumable never reaches a grant
252
+ through this SDK, which excludes kind mismatches (`kind-mismatch`).
253
+
254
+ Should StoreKit list the consumable again (an entry the native read skipped
255
+ as unverifiable), the reconciler finishes it again with the journaled ledger
256
+ entry, once per session, without a second verify. If the finished record has
257
+ already left the journal (the generated game's journal keeps terminal
258
+ records for 30 days), the transaction is verified under its derived key; the
259
+ reference backend answers that with this player's recorded grant
260
+ (`alreadyProcessed: true`, the same ledger entry, no second grant) because
261
+ the App Store entry is a `client`-finish consumable, and the reconciler then
262
+ finishes it. Another player's ask under another key stays refused
263
+ (`EVIDENCE_ALREADY_PROCESSED`), so nothing is granted twice. Ask to Buy is
264
+ unaffected: a deferred purchase has no transaction
265
+ until it is approved, so its `launched` record holds no ledger grant and is
266
+ never finished; the approved transaction arrives through `transactionUpdated`
267
+ and is verified and finished once.
268
+
214
269
  ### Migrating from v1
215
270
 
216
271
  Commerce v2 PR C4 removed the v1 provider and its recovery driver.
@@ -1,5 +1,6 @@
1
1
  import { type CapacitorCommerceServiceProvider, type StoreCommerceProductMapping, type StoreCommerceProvider, type StoreSdkErrorCodeMap } from '@mpgd/adapter-capacitor';
2
2
  import { appStoreSignedTransactionEvidenceSchema } from './commerce-profile.js';
3
+ import { type StorePurchaseGate } from './purchase-gate-support.js';
3
4
  import { type CapacitorStoreKitDiagnostic, type CapacitorStoreKitStoreSdkPlugin } from './store-sdk.js';
4
5
  export { appStoreSignedTransactionEvidenceSchema };
5
6
  /** Payload of an `app-store.signed-transaction.v1` envelope. */
@@ -14,9 +15,18 @@ export interface CreateCapacitorStoreKitCommerceProviderInput {
14
15
  readonly products: readonly StoreCommerceProductMapping[];
15
16
  /**
16
17
  * Authenticated, stable UUID app account token of the current player. The
17
- * backend's `resolveAppAccountToken` must resolve the same value.
18
+ * backend's `resolveAppAccountToken` must resolve the same value. It also
19
+ * filters recovery and finishing to this player, so it must keep answering
20
+ * while sales are paused; pause sales with `purchaseGate` instead.
18
21
  */
19
22
  readonly getAppAccountToken: () => string | Promise<string>;
23
+ /**
24
+ * Optional sales switch, asked before a new purchase opens the StoreKit
25
+ * sheet and never by recovery, restore, or finishing, which keep using
26
+ * `getAppAccountToken` alone; a refusal is `ITEM_UNAVAILABLE` with
27
+ * `PURCHASES_PAUSED` (see `StorePurchaseGate` in `@mpgd/adapter-capacitor`).
28
+ */
29
+ readonly purchaseGate?: StorePurchaseGate;
20
30
  /** Recorded in the evidence payload when known; the backend checks the signed value anyway. */
21
31
  readonly bundleId?: string;
22
32
  readonly sdk?: CapacitorStoreKitStoreSdkPlugin;
@@ -1,6 +1,7 @@
1
1
  import { createCommerceServiceProvider, createStoreCommerceProvider, } from '@mpgd/adapter-capacitor';
2
2
  import { CommerceError } from '@mpgd/platform/commerce';
3
3
  import { appStoreSignedTransactionEvidenceSchema, appStoreStoreId, storeKitCommerceAuthority, } from './commerce-profile.js';
4
+ import { refuseNewPurchasesUnlessGated, resolvePurchaseGateSupport, } from './purchase-gate-support.js';
4
5
  import { capacitorStoreKitErrorCodeMap, createCapacitorStoreKitStoreSdk, readAppAccountToken, } from './store-sdk.js';
5
6
  // The profile constants live in the SDK-free `commerce-profile.ts` so a tools
6
7
  // project can read them without loading Capacitor; they stay exported here.
@@ -31,6 +32,7 @@ export function createCapacitorStoreKitCommerceProvider(input) {
31
32
  throw new TypeError('StoreKit bundleId must be a non-empty string when provided.');
32
33
  }
33
34
  const bundleId = input.bundleId;
35
+ const gate = resolvePurchaseGateSupport(input.purchaseGate);
34
36
  const sdk = createCapacitorStoreKitStoreSdk({
35
37
  products: input.products.map((product) => ({
36
38
  sku: product.storeSku,
@@ -41,7 +43,7 @@ export function createCapacitorStoreKitCommerceProvider(input) {
41
43
  ...(input.isIos === undefined ? {} : { isIos: input.isIos }),
42
44
  ...(input.onDiagnostic === undefined ? {} : { onDiagnostic: input.onDiagnostic }),
43
45
  });
44
- return createStoreCommerceProvider({
46
+ return refuseNewPurchasesUnlessGated(createStoreCommerceProvider({
45
47
  sdk,
46
48
  store: appStoreStoreId,
47
49
  products: input.products,
@@ -63,6 +65,9 @@ export function createCapacitorStoreKitCommerceProvider(input) {
63
65
  value = await input.getAppAccountToken();
64
66
  }
65
67
  catch (error) {
68
+ if (error instanceof CommerceError) {
69
+ throw error;
70
+ }
66
71
  throw new CommerceError('ACCOUNT_BINDING_REQUIRED', {
67
72
  message: 'The StoreKit app account token could not be resolved.',
68
73
  storeCode: 'STOREKIT_ACCOUNT_LOOKUP_FAILED',
@@ -86,9 +91,10 @@ export function createCapacitorStoreKitCommerceProvider(input) {
86
91
  });
87
92
  }
88
93
  },
94
+ ...(gate.purchaseGate === undefined ? {} : { purchaseGate: gate.purchaseGate }),
89
95
  authority: storeKitCommerceAuthority,
90
96
  mapError: { ...capacitorStoreKitErrorCodeMap, ...input.mapError },
91
- });
97
+ }), gate);
92
98
  }
93
99
  /**
94
100
  * The commerce provider wrapped as a `CapacitorServiceProvider` for
package/dist/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export * from './commerce-provider.js';
3
3
  export * from './definitions.js';
4
4
  export * from './plugin.js';
5
5
  export * from './store-sdk.js';
6
+ export { purchaseGateUnsupportedStoreCode, type StorePurchaseGate, type StorePurchaseGateDecision, type StorePurchaseGateRequest, } from './purchase-gate-support.js';
package/dist/index.js CHANGED
@@ -3,3 +3,4 @@ export * from './commerce-provider.js';
3
3
  export * from './definitions.js';
4
4
  export * from './plugin.js';
5
5
  export * from './store-sdk.js';
6
+ export { purchaseGateUnsupportedStoreCode, } from './purchase-gate-support.js';
@@ -0,0 +1,50 @@
1
+ import { CommerceError, type CommerceLogicalProductId, type CommerceProvider, type PurchaseKind, type PurchaseSource, type StoreId } from '@mpgd/platform/commerce';
2
+ /** What a purchase gate is asked about: one new purchase, before any store UI opens. */
3
+ export interface StorePurchaseGateRequest {
4
+ readonly store: StoreId;
5
+ readonly logicalId: CommerceLogicalProductId;
6
+ readonly storeSku: string;
7
+ readonly kind: PurchaseKind;
8
+ readonly source: PurchaseSource;
9
+ }
10
+ /** A purchase gate's answer; see `StorePurchaseGateDecision` in `@mpgd/adapter-capacitor`. */
11
+ export type StorePurchaseGateDecision = {
12
+ readonly allowed: true;
13
+ } | {
14
+ readonly allowed: false;
15
+ readonly storeCode?: string;
16
+ readonly message?: string;
17
+ readonly retryable?: boolean;
18
+ };
19
+ /** The game's sales switch for new purchases; see `StorePurchaseGate` in `@mpgd/adapter-capacitor`. */
20
+ export type StorePurchaseGate = (request: StorePurchaseGateRequest) => StorePurchaseGateDecision | Promise<StorePurchaseGateDecision>;
21
+ /** `storeCode` of every new purchase refused because the adapter cannot run the gate. */
22
+ export declare const purchaseGateUnsupportedStoreCode: 'PURCHASE_GATE_UNSUPPORTED';
23
+ /**
24
+ * Whether the installed `@mpgd/adapter-capacitor` runs `purchaseGate`. An
25
+ * adapter from before the gate ignores the option and would keep selling, so
26
+ * the probe reads an export the gate release added (`purchasesPausedStoreCode`)
27
+ * through the module namespace, where a missing export is `undefined` rather
28
+ * than a link error.
29
+ */
30
+ export declare function adapterSupportsPurchaseGate(adapterExports?: object): boolean;
31
+ export interface PurchaseGateSupport {
32
+ /** The gate to hand the adapter, when it runs one. */
33
+ readonly purchaseGate?: StorePurchaseGate;
34
+ /**
35
+ * Set when a gate was given but the adapter cannot run it: the plugin then
36
+ * answers every new purchase itself (`refuseNewPurchasesUnlessGated`).
37
+ */
38
+ readonly refuseNewPurchases: boolean;
39
+ }
40
+ /** Fails closed: a gate the adapter would ignore refuses every new purchase instead. */
41
+ export declare function resolvePurchaseGateSupport(purchaseGate: StorePurchaseGate | undefined, supported?: boolean): PurchaseGateSupport;
42
+ export declare function createPurchaseGateUnsupportedError(): CommerceError;
43
+ /**
44
+ * Refuses every new purchase at the plugin boundary when the adapter cannot
45
+ * run the configured gate, before the adapter's `requestPurchase` (which
46
+ * would open the store) is reached. The refusal is a `failed` result, not a
47
+ * throw, so `PURCHASE_GATE_UNSUPPORTED` survives the bridge whatever the
48
+ * adapter version. Recovery, finishing, restore, and events are untouched.
49
+ */
50
+ export declare function refuseNewPurchasesUnlessGated<TProvider extends CommerceProvider>(provider: TProvider, support: PurchaseGateSupport): TProvider;
@@ -0,0 +1,50 @@
1
+ import * as adapterCapacitor from '@mpgd/adapter-capacitor';
2
+ import { CommerceError, } from '@mpgd/platform/commerce';
3
+ /** `storeCode` of every new purchase refused because the adapter cannot run the gate. */
4
+ export const purchaseGateUnsupportedStoreCode = 'PURCHASE_GATE_UNSUPPORTED';
5
+ /**
6
+ * Whether the installed `@mpgd/adapter-capacitor` runs `purchaseGate`. An
7
+ * adapter from before the gate ignores the option and would keep selling, so
8
+ * the probe reads an export the gate release added (`purchasesPausedStoreCode`)
9
+ * through the module namespace, where a missing export is `undefined` rather
10
+ * than a link error.
11
+ */
12
+ export function adapterSupportsPurchaseGate(adapterExports = adapterCapacitor) {
13
+ return typeof adapterExports.purchasesPausedStoreCode
14
+ === 'string';
15
+ }
16
+ /** Fails closed: a gate the adapter would ignore refuses every new purchase instead. */
17
+ export function resolvePurchaseGateSupport(purchaseGate, supported = adapterSupportsPurchaseGate()) {
18
+ if (purchaseGate === undefined) {
19
+ return { refuseNewPurchases: false };
20
+ }
21
+ return supported ? { purchaseGate, refuseNewPurchases: false } : { refuseNewPurchases: true };
22
+ }
23
+ export function createPurchaseGateUnsupportedError() {
24
+ return new CommerceError('ITEM_UNAVAILABLE', {
25
+ message: 'A purchase gate is configured but @mpgd/adapter-capacitor is too old to run it; '
26
+ + 'new purchases are refused until the adapter is upgraded.',
27
+ storeCode: purchaseGateUnsupportedStoreCode,
28
+ retryable: false,
29
+ });
30
+ }
31
+ /**
32
+ * Refuses every new purchase at the plugin boundary when the adapter cannot
33
+ * run the configured gate, before the adapter's `requestPurchase` (which
34
+ * would open the store) is reached. The refusal is a `failed` result, not a
35
+ * throw, so `PURCHASE_GATE_UNSUPPORTED` survives the bridge whatever the
36
+ * adapter version. Recovery, finishing, restore, and events are untouched.
37
+ */
38
+ export function refuseNewPurchasesUnlessGated(provider, support) {
39
+ if (!support.refuseNewPurchases) {
40
+ return provider;
41
+ }
42
+ return Object.assign(provider, {
43
+ async requestPurchase() {
44
+ return {
45
+ outcome: 'failed',
46
+ error: createPurchaseGateUnsupportedError().toCommerceErrorInfo(),
47
+ };
48
+ },
49
+ });
50
+ }
package/dist/store-sdk.js CHANGED
@@ -259,9 +259,33 @@ export function createCapacitorStoreKitStoreSdk(input) {
259
259
  /**
260
260
  * Mirrors the native finish gate: the transaction must be listed, not
261
261
  * revoked, and bound to the session app account token. A missing list is
262
- * an invalid response, not a quiet "not finished". Two exceptions for a
263
- * listed, non-revoked durable, the only kind the restore read admits from
264
- * another token:
262
+ * an invalid response, not a quiet "not finished".
263
+ *
264
+ * On a retried finish (`retry`: the caller asked to finish this purchase
265
+ * before, in an earlier attempt or session), a consumable the list no
266
+ * longer carries is already finished: StoreKit keeps a transaction in
267
+ * `Transaction.unfinished` until `finish()`, and a finished consumable is in
268
+ * neither that list nor `currentEntitlements`. That is the lost
269
+ * acknowledgement: an earlier finish reached StoreKit but its answer never
270
+ * reached the journal (the app was closed between the two), and the record
271
+ * would otherwise stay `finishing` forever. A first finish never reads a
272
+ * missing transaction that way: right after the grant the list may not
273
+ * carry the new transaction yet, so it stays refused and the caller retries.
274
+ * Nothing native is called, and an entry of that id the native side reports
275
+ * in any shape keeps it refused. If the transaction is listed again later
276
+ * (an unverifiable entry the native read skipped, or a list read before
277
+ * StoreKit carried it), the reconciler finishes it again without a second
278
+ * verify. A durable missing from the list is refused: a durable stays in
279
+ * `currentEntitlements` after its finish, so a missing one is revoked or
280
+ * unverifiable, never a lost acknowledgement. The kind of a missing
281
+ * transaction is the caller's `isConsumable` (the list no longer says), so a
282
+ * catalog that maps a durable as a consumable would be answered finished
283
+ * here; the SDK never admits such a transaction (`kind-mismatch`), so the
284
+ * reconciler cannot hold a grant for it from this SDK, and the answer
285
+ * neither grants nor takes anything.
286
+ *
287
+ * Two exceptions for a listed, non-revoked durable, the only kind the
288
+ * restore read admits from another token:
265
289
  *
266
290
  * - StoreKit already finished it (`unfinished: false`): nothing is left to
267
291
  * finish whatever its token, so a restore shared to this player ends
@@ -279,13 +303,17 @@ export function createCapacitorStoreKitStoreSdk(input) {
279
303
  * A consumable of another token, or a transaction bound to no token, is
280
304
  * never finished here.
281
305
  */
282
- const finishPermitted = async (transactionId, appAccountToken) => {
306
+ const finishPermitted = async (transactionId, appAccountToken, lostAcknowledgement) => {
283
307
  const response = await sdk.getTransactions();
284
308
  if (!isRecord(response) || !Array.isArray(response.transactions)) {
285
309
  throw evidenceInvalid('StoreKit returned an invalid transaction list.');
286
310
  }
287
- const transaction = response.transactions.find((candidate) => isValidStoreKitTransaction(candidate) && candidate.transactionId === transactionId);
288
- if (transaction === undefined || transaction.revokedAt !== undefined) {
311
+ const entries = response.transactions.filter((candidate) => isRecord(candidate) && candidate.transactionId === transactionId);
312
+ const transaction = entries.find(isValidStoreKitTransaction);
313
+ if (transaction === undefined) {
314
+ return { verdict: entries.length === 0 && lostAcknowledgement ? 'finished' : 'refused' };
315
+ }
316
+ if (transaction.revokedAt !== undefined) {
289
317
  return { verdict: 'refused' };
290
318
  }
291
319
  if (transaction.type === 'non_consumable' && transaction.unfinished === false) {
@@ -409,8 +437,10 @@ export function createCapacitorStoreKitStoreSdk(input) {
409
437
  const promise = (async () => {
410
438
  // Same match the native plugin applies: missing, revoked, unbound, or
411
439
  // another account never reaches `transaction.finish()`, except a
412
- // durable of another token (see `finishPermitted`).
413
- const permitted = await finishPermitted(transactionId, appAccountToken);
440
+ // durable of another token (see `finishPermitted`). On a retry, a
441
+ // consumable that left the unfinished list is already finished (a
442
+ // lost acknowledgement).
443
+ const permitted = await finishPermitted(transactionId, appAccountToken, request.isConsumable === true && request.retry === true);
414
444
  if (permitted.verdict !== 'finish') {
415
445
  return { finished: permitted.verdict === 'finished' };
416
446
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/capacitor-storekit",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "description": "Opt-in StoreKit 2 one-time purchase collector and commerce v2 provider for mpgd Capacitor games.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,18 +38,18 @@
38
38
  ],
39
39
  "peerDependencies": {
40
40
  "@capacitor/core": "^8.5.1",
41
- "@mpgd/adapter-capacitor": "^0.16.0",
42
- "@mpgd/bridge": "^0.16.0",
43
- "@mpgd/platform": "^0.32.0"
41
+ "@mpgd/adapter-capacitor": "^0.17.0",
42
+ "@mpgd/bridge": "^0.17.0",
43
+ "@mpgd/platform": "^0.33.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@capacitor/core": "8.5.2",
47
- "ttsc": "0.32.1",
47
+ "ttsc": "0.34.2",
48
48
  "typescript": "7.0.2",
49
- "@mpgd/adapter-capacitor": "0.16.2",
50
- "@mpgd/game-services": "0.30.4",
51
- "@mpgd/bridge": "0.16.4",
52
- "@mpgd/platform": "0.32.0"
49
+ "@mpgd/adapter-capacitor": "0.17.0",
50
+ "@mpgd/bridge": "0.17.0",
51
+ "@mpgd/game-services": "0.31.0",
52
+ "@mpgd/platform": "0.33.0"
53
53
  },
54
54
  "main": "./dist/index.js",
55
55
  "types": "./dist/index.d.ts",