@mpgd/adapter-capacitor 0.4.11 → 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.
@@ -0,0 +1,309 @@
1
+ import { PlatformOperationError, } from '@mpgd/platform';
2
+ const featureMethods = {
3
+ nativeIap: [
4
+ 'commerce.getProducts',
5
+ 'commerce.purchase',
6
+ 'commerce.restore',
7
+ 'commerce.getEntitlements',
8
+ ],
9
+ subscriptionIap: [
10
+ 'commerce.getProducts',
11
+ 'commerce.purchase',
12
+ 'commerce.restore',
13
+ 'commerce.getEntitlements',
14
+ ],
15
+ rewardedAds: ['ads.preload', 'ads.showRewarded'],
16
+ interstitialAds: ['ads.preload', 'ads.showInterstitial'],
17
+ bannerAds: ['ads.mountBanner', 'ads.unmountBanner'],
18
+ nativeLeaderboard: ['leaderboard.submitScore', 'leaderboard.open'],
19
+ identityUpgrade: ['identity.getPlayer', 'identity.getSession', 'identity.requestUpgrade'],
20
+ pushNotifications: ['notifications.getStatus', 'notifications.requestSubscription'],
21
+ };
22
+ const providerFeatures = Object.keys(featureMethods);
23
+ const providerMethodSet = new Set(Object.values(featureMethods).flat());
24
+ const availabilitySet = new Set([
25
+ 'unsupported',
26
+ 'configuration-required',
27
+ 'action-required',
28
+ 'temporarily-unavailable',
29
+ 'available',
30
+ ]);
31
+ const availabilityTimeoutMs = 3_000;
32
+ export function createCapacitorProviderRegistry(input) {
33
+ const providers = input.map((provider) => ({
34
+ ...provider,
35
+ methods: [...provider.methods],
36
+ features: [...provider.features],
37
+ getAvailability: () => provider.getAvailability(),
38
+ }));
39
+ const byMethod = new Map();
40
+ const byFeature = new Map();
41
+ const ids = new Set();
42
+ for (const provider of providers) {
43
+ if (provider.id.trim() === '' || ids.has(provider.id)) {
44
+ throw new Error(`Capacitor provider ID is empty or registered twice: ${provider.id}`);
45
+ }
46
+ ids.add(provider.id);
47
+ if (provider.features.length === 0 || provider.methods.length === 0) {
48
+ throw new Error(`Capacitor provider ${provider.id} must declare features and methods.`);
49
+ }
50
+ const methods = new Set(provider.methods);
51
+ if (methods.size !== provider.methods.length) {
52
+ throw new Error(`Capacitor provider ${provider.id} repeats a bridge method.`);
53
+ }
54
+ for (const method of methods) {
55
+ if (!providerMethodSet.has(method)) {
56
+ throw new Error(`Capacitor provider ${provider.id} cannot replace base method ${method}.`);
57
+ }
58
+ if (byMethod.has(method)) {
59
+ throw new Error(`Capacitor bridge method ${method} has multiple providers.`);
60
+ }
61
+ }
62
+ for (const feature of provider.features) {
63
+ const required = featureMethods[feature];
64
+ if (required === undefined || byFeature.has(feature)) {
65
+ throw new Error(`Capacitor feature ${feature} has multiple or invalid providers.`);
66
+ }
67
+ if (required.some((method) => !methods.has(method))) {
68
+ throw new Error(`Capacitor provider ${provider.id} lacks methods for ${feature}.`);
69
+ }
70
+ byFeature.set(feature, provider);
71
+ }
72
+ if ([...methods].some((method) => !provider.features.some((feature) => featureMethods[feature].includes(method)))) {
73
+ throw new Error(`Capacitor provider ${provider.id} declares an unowned method.`);
74
+ }
75
+ for (const method of methods) {
76
+ byMethod.set(method, provider);
77
+ }
78
+ }
79
+ const readProviderStates = async (provider) => {
80
+ let reported;
81
+ let timer;
82
+ try {
83
+ reported = await Promise.race([
84
+ Promise.resolve().then(() => provider.getAvailability()),
85
+ new Promise((_resolve, reject) => {
86
+ timer = setTimeout(() => reject(new Error('Provider availability timed out.')), availabilityTimeoutMs);
87
+ }),
88
+ ]);
89
+ if (!isRecord(reported)) {
90
+ throw new Error('Invalid provider availability.');
91
+ }
92
+ }
93
+ catch {
94
+ reported = {};
95
+ }
96
+ finally {
97
+ if (timer !== undefined) {
98
+ clearTimeout(timer);
99
+ }
100
+ }
101
+ const states = {};
102
+ for (const feature of provider.features) {
103
+ const state = reported[feature];
104
+ states[feature] = availabilitySet.has(state)
105
+ ? state
106
+ : 'temporarily-unavailable';
107
+ }
108
+ return states;
109
+ };
110
+ const getAvailability = async () => {
111
+ const states = {};
112
+ for (const feature of providerFeatures) {
113
+ states[feature] = 'unsupported';
114
+ }
115
+ const snapshots = await Promise.all(providers.map(readProviderStates));
116
+ for (const snapshot of snapshots) {
117
+ Object.assign(states, snapshot);
118
+ }
119
+ return states;
120
+ };
121
+ return {
122
+ byMethod,
123
+ getAvailability,
124
+ async assertMethodReady(method, payload, productType) {
125
+ const provider = byMethod.get(method);
126
+ if (provider === undefined) {
127
+ return;
128
+ }
129
+ if (method === 'ads.unmountBanner') {
130
+ // Teardown must reach the provider that mounted the banner even after
131
+ // its serving state becomes unavailable.
132
+ return;
133
+ }
134
+ // An unrelated provider must not delay a purchase, ad, or identity call.
135
+ const states = await readProviderStates(provider);
136
+ let relevant = provider.features.filter((feature) => featureMethods[feature].includes(method));
137
+ if (method === 'commerce.purchase' && relevant.length > 1) {
138
+ relevant = relevant.filter((feature) => feature === (productType === 'subscription' ? 'subscriptionIap'
139
+ : productType === 'consumable' || productType === 'non_consumable'
140
+ ? 'nativeIap' : undefined));
141
+ }
142
+ const preloadFormat = method === 'ads.preload' && isRecord(payload)
143
+ ? payload.format
144
+ : undefined;
145
+ if (method === 'ads.preload' && preloadFormat !== undefined) {
146
+ const feature = preloadFormat === 'rewarded'
147
+ ? 'rewardedAds'
148
+ : preloadFormat === 'interstitial'
149
+ ? 'interstitialAds'
150
+ : undefined;
151
+ relevant = feature === undefined ? [] : relevant.filter((item) => item === feature);
152
+ }
153
+ if (relevant.length === 0) {
154
+ throw new PlatformOperationError({
155
+ code: method === 'commerce.purchase'
156
+ ? 'NATIVE_PROVIDER_PRODUCT_TYPE_REQUIRED'
157
+ : 'NATIVE_PROVIDER_UNSUPPORTED',
158
+ });
159
+ }
160
+ const ready = (method === 'ads.preload' && preloadFormat === undefined)
161
+ || ((method === 'commerce.restore' || method === 'commerce.getEntitlements')
162
+ && relevant.length > 1)
163
+ ? relevant.every((feature) => states[feature] === 'available')
164
+ : relevant.some((feature) => states[feature] === 'available');
165
+ if (ready) {
166
+ return;
167
+ }
168
+ // These methods exist to explain or resolve an action-required state.
169
+ if ((method === 'identity.getPlayer' || method === 'identity.getSession'
170
+ || method === 'identity.requestUpgrade'
171
+ || method === 'notifications.getStatus'
172
+ || method === 'notifications.requestSubscription')
173
+ && relevant.some((feature) => states[feature] === 'action-required')) {
174
+ return;
175
+ }
176
+ const state = relevant.map((feature) => states[feature] ?? 'temporarily-unavailable').find((value) => value !== 'unsupported' && value !== 'available') ?? 'unsupported';
177
+ const code = {
178
+ unsupported: 'NATIVE_PROVIDER_UNSUPPORTED',
179
+ 'configuration-required': 'NATIVE_PROVIDER_CONFIGURATION_REQUIRED',
180
+ 'action-required': 'NATIVE_PROVIDER_ACTION_REQUIRED',
181
+ 'temporarily-unavailable': 'NATIVE_PROVIDER_TEMPORARILY_UNAVAILABLE',
182
+ available: 'NATIVE_PROVIDER_NOT_READY',
183
+ };
184
+ throw new PlatformOperationError({
185
+ code: code[state],
186
+ retryable: state === 'temporarily-unavailable',
187
+ });
188
+ },
189
+ };
190
+ }
191
+ export function assertProviderResponseData(method, data) {
192
+ const valid = isValidProviderData(method, data);
193
+ if (!valid) {
194
+ throw new PlatformOperationError({
195
+ code: 'NATIVE_PROVIDER_INVALID_RESPONSE',
196
+ message: `Capacitor provider returned invalid data for ${method}.`,
197
+ });
198
+ }
199
+ }
200
+ function isRecord(value) {
201
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
202
+ }
203
+ function isString(value) {
204
+ return typeof value === 'string';
205
+ }
206
+ function isStringArray(value) {
207
+ return Array.isArray(value) && value.every(isString);
208
+ }
209
+ function isVoidData(value) {
210
+ return value === undefined || value === null || (isRecord(value) && Object.keys(value).length === 0);
211
+ }
212
+ function isValidProviderData(method, value) {
213
+ switch (method) {
214
+ case 'commerce.getProducts':
215
+ return Array.isArray(value) && value.every((product) => isRecord(product)
216
+ && isString(product.id)
217
+ && ['consumable', 'non_consumable', 'subscription'].includes(product.type)
218
+ && isString(product.title)
219
+ && isString(product.description)
220
+ && isRecord(product.price)
221
+ && isString(product.price.formatted)
222
+ && isString(product.price.currencyCode));
223
+ case 'commerce.purchase':
224
+ return isRecord(value)
225
+ && ['completed', 'cancelled', 'pending', 'failed'].includes(value.status)
226
+ && isStringArray(value.entitlementIds)
227
+ && (value.transactionId === undefined || isString(value.transactionId))
228
+ && (value.authoritativeGrant === undefined || (value.status === 'completed'
229
+ && isString(value.transactionId)
230
+ && isRecord(value.authoritativeGrant)
231
+ && isString(value.authoritativeGrant.ledgerEntryId)
232
+ && value.authoritativeGrant.ledgerEntryId.length > 0
233
+ && (value.authoritativeGrant.alreadyProcessed === undefined
234
+ || typeof value.authoritativeGrant.alreadyProcessed === 'boolean')))
235
+ && (value.evidence === undefined || isEvidence(value.evidence));
236
+ case 'commerce.restore':
237
+ return isRecord(value)
238
+ && Array.isArray(value.restoredEntitlements)
239
+ && value.restoredEntitlements.every(isEntitlement)
240
+ && (value.settledPurchases === undefined || (Array.isArray(value.settledPurchases)
241
+ && value.settledPurchases.every((settlement) => isRecord(settlement)
242
+ && isString(settlement.transactionId)
243
+ && isString(settlement.productId)
244
+ && ['granted', 'refunded'].includes(settlement.status)
245
+ && (settlement.ledgerEntryId === undefined || isString(settlement.ledgerEntryId))
246
+ && (settlement.alreadyProcessed === undefined
247
+ || typeof settlement.alreadyProcessed === 'boolean'))));
248
+ case 'commerce.getEntitlements':
249
+ return Array.isArray(value) && value.every(isEntitlement);
250
+ case 'ads.preload':
251
+ case 'ads.unmountBanner':
252
+ case 'leaderboard.open':
253
+ return isVoidData(value);
254
+ case 'ads.showRewarded':
255
+ return isRecord(value)
256
+ && ['completed', 'skipped', 'unavailable', 'failed'].includes(value.status)
257
+ && typeof value.rewardGranted === 'boolean'
258
+ && (!value.rewardGranted || (value.status === 'completed'
259
+ && isString(value.ledgerEntryId)
260
+ && value.ledgerEntryId.length > 0))
261
+ && (value.evidence === undefined || isEvidence(value.evidence));
262
+ case 'ads.showInterstitial':
263
+ return isRecord(value)
264
+ && ['shown', 'skipped', 'unavailable'].includes(value.status);
265
+ case 'ads.mountBanner':
266
+ return isRecord(value)
267
+ && ['mounted', 'unavailable', 'failed'].includes(value.status);
268
+ case 'leaderboard.submitScore':
269
+ return isRecord(value)
270
+ && typeof value.submitted === 'boolean'
271
+ && (value.rank === undefined || (typeof value.rank === 'number' && Number.isFinite(value.rank)));
272
+ case 'identity.getPlayer':
273
+ return value === null || (isRecord(value)
274
+ && isString(value.playerId)
275
+ && (value.displayName === undefined || isString(value.displayName))
276
+ && (value.avatarUrl === undefined || isString(value.avatarUrl)));
277
+ case 'identity.getSession':
278
+ return isRecord(value)
279
+ && ['guest', 'platform-anonymous', 'authenticated'].includes(value.identityLevel)
280
+ && ['local', 'platform-asserted', 'server-verified'].includes(value.trustLevel)
281
+ && (value.playerId === undefined || isString(value.playerId));
282
+ case 'identity.requestUpgrade':
283
+ return isRecord(value)
284
+ && ['completed', 'cancelled', 'unavailable'].includes(value.status)
285
+ && typeof value.reloadExpected === 'boolean';
286
+ case 'notifications.getStatus':
287
+ return [
288
+ 'subscribed', 'not-subscribed', 'approval-required',
289
+ 'configuration-required', 'unsupported',
290
+ ].includes(value);
291
+ case 'notifications.requestSubscription':
292
+ return ['subscribed', 'rejected', 'unavailable'].includes(value);
293
+ default:
294
+ return false;
295
+ }
296
+ }
297
+ function isEntitlement(value) {
298
+ return isRecord(value)
299
+ && isString(value.id)
300
+ && ['purchase', 'promotion', 'admin'].includes(value.source)
301
+ && isString(value.grantedAt)
302
+ && (value.expiresAt === undefined || isString(value.expiresAt));
303
+ }
304
+ function isEvidence(value) {
305
+ return isRecord(value)
306
+ && isString(value.schema)
307
+ && isRecord(value.payload)
308
+ && Object.values(value.payload).every((entry) => isString(entry) || typeof entry === 'number' || typeof entry === 'boolean');
309
+ }
@@ -0,0 +1,25 @@
1
+ import type { PlatformViewportOccupiedSurface, PlatformViewportState, ViewportAdapter } from '@mpgd/platform';
2
+ export type CapacitorViewportBaseState = Omit<PlatformViewportState, 'occupiedSurfaces'>;
3
+ export interface CapacitorViewportHost {
4
+ readState(): CapacitorViewportBaseState;
5
+ onChange(callback: () => void): () => void;
6
+ }
7
+ export interface CapacitorOccupiedSurfaceInput extends PlatformViewportOccupiedSurface {
8
+ /** Native providers report physical pixels with their own density scale. */
9
+ readonly unit: 'css-px' | 'physical-px';
10
+ readonly pixelsPerCssPixel?: number;
11
+ }
12
+ export interface CapacitorViewportController extends ViewportAdapter {
13
+ setOccupiedSurface(input: CapacitorOccupiedSurfaceInput): void;
14
+ clearOccupiedSurface(surfaceId: string): void;
15
+ dispose(): void;
16
+ }
17
+ /**
18
+ * Owns only its viewport listeners. CSS safe-area values are the default
19
+ * system-inset source; a host can inject separate native system-bar readings
20
+ * without summing the same occupied pixels twice.
21
+ */
22
+ export declare function createCapacitorViewport(input?: {
23
+ readonly host?: CapacitorViewportHost;
24
+ readonly onError?: (error: unknown) => void;
25
+ }): CapacitorViewportController;
@@ -0,0 +1,221 @@
1
+ import { readTargetViewportSafeAreaInsets } from '@mpgd/target-config';
2
+ /**
3
+ * Owns only its viewport listeners. CSS safe-area values are the default
4
+ * system-inset source; a host can inject separate native system-bar readings
5
+ * without summing the same occupied pixels twice.
6
+ */
7
+ export function createCapacitorViewport(input = {}) {
8
+ const host = input.host ?? createWebViewHost();
9
+ const listeners = new Set();
10
+ const surfaces = new Map();
11
+ let unsubscribeHost;
12
+ let disposed = false;
13
+ let lastStateJson;
14
+ function reportError(error) {
15
+ try {
16
+ input.onError?.(error);
17
+ }
18
+ catch {
19
+ // A diagnostic callback must not prevent another viewport subscriber
20
+ // from seeing the same state change.
21
+ }
22
+ }
23
+ function getState() {
24
+ if (disposed) {
25
+ throw new Error('Capacitor viewport is disposed.');
26
+ }
27
+ const base = host.readState();
28
+ if (!Number.isFinite(base.width) || !Number.isFinite(base.height)
29
+ || base.width <= 0 || base.height <= 0) {
30
+ throw new Error('Capacitor viewport dimensions must be positive CSS pixels.');
31
+ }
32
+ const occupiedSurfaces = [...surfaces.values()].map((surface) => ({
33
+ ...surface,
34
+ bounds: clipBounds(surface.bounds, base.width, base.height),
35
+ }));
36
+ return {
37
+ width: base.width,
38
+ height: base.height,
39
+ safeAreaInsets: { ...base.safeAreaInsets },
40
+ systemBarInsets: { ...base.systemBarInsets },
41
+ keyboardInsets: { ...base.keyboardInsets },
42
+ occupiedSurfaces,
43
+ };
44
+ }
45
+ function notify() {
46
+ if (disposed) {
47
+ return;
48
+ }
49
+ let state;
50
+ try {
51
+ state = getState();
52
+ }
53
+ catch (error) {
54
+ reportError(error);
55
+ return;
56
+ }
57
+ const stateJson = JSON.stringify(state);
58
+ if (stateJson === lastStateJson) {
59
+ return;
60
+ }
61
+ lastStateJson = stateJson;
62
+ for (const listener of [...listeners]) {
63
+ try {
64
+ listener(state);
65
+ }
66
+ catch (error) {
67
+ reportError(error);
68
+ }
69
+ }
70
+ }
71
+ return {
72
+ getState,
73
+ onChange(callback) {
74
+ if (disposed) {
75
+ throw new Error('Capacitor viewport is disposed.');
76
+ }
77
+ listeners.add(callback);
78
+ if (unsubscribeHost === undefined) {
79
+ try {
80
+ lastStateJson = JSON.stringify(getState());
81
+ unsubscribeHost = host.onChange(notify);
82
+ }
83
+ catch (error) {
84
+ listeners.delete(callback);
85
+ lastStateJson = undefined;
86
+ throw error;
87
+ }
88
+ }
89
+ let active = true;
90
+ return () => {
91
+ if (!active) {
92
+ return;
93
+ }
94
+ active = false;
95
+ listeners.delete(callback);
96
+ if (listeners.size === 0) {
97
+ unsubscribeHost?.();
98
+ unsubscribeHost = undefined;
99
+ lastStateJson = undefined;
100
+ }
101
+ };
102
+ },
103
+ setOccupiedSurface(surface) {
104
+ if (disposed) {
105
+ throw new Error('Capacitor viewport is disposed.');
106
+ }
107
+ if (surface.surfaceId.trim() === '') {
108
+ throw new Error('Occupied surface ID is required.');
109
+ }
110
+ if (!['top', 'right', 'bottom', 'left'].includes(surface.edge)) {
111
+ throw new Error('Occupied surface edge is invalid.');
112
+ }
113
+ if (surface.unit !== 'css-px' && surface.unit !== 'physical-px') {
114
+ throw new Error('Occupied surface unit is invalid.');
115
+ }
116
+ const scale = surface.unit === 'physical-px' ? surface.pixelsPerCssPixel : 1;
117
+ if (scale === undefined || !Number.isFinite(scale) || scale <= 0) {
118
+ throw new Error('Native occupied surfaces require a positive pixels-per-CSS-pixel scale.');
119
+ }
120
+ const { x, y, width, height } = surface.bounds;
121
+ if (![x, y, width, height].every(Number.isFinite)
122
+ || x < 0 || y < 0 || width < 0 || height < 0) {
123
+ throw new Error('Occupied surface bounds must be non-negative finite pixels.');
124
+ }
125
+ surfaces.set(surface.surfaceId, {
126
+ surfaceId: surface.surfaceId,
127
+ edge: surface.edge,
128
+ bounds: { x: x / scale, y: y / scale, width: width / scale, height: height / scale },
129
+ });
130
+ if (listeners.size > 0) {
131
+ notify();
132
+ }
133
+ },
134
+ clearOccupiedSurface(surfaceId) {
135
+ if (disposed) {
136
+ return;
137
+ }
138
+ if (surfaces.delete(surfaceId) && listeners.size > 0) {
139
+ notify();
140
+ }
141
+ },
142
+ dispose() {
143
+ if (disposed) {
144
+ return;
145
+ }
146
+ disposed = true;
147
+ unsubscribeHost?.();
148
+ unsubscribeHost = undefined;
149
+ listeners.clear();
150
+ surfaces.clear();
151
+ lastStateJson = undefined;
152
+ },
153
+ };
154
+ }
155
+ function clipBounds(bounds, width, height) {
156
+ const x = Math.max(0, Math.min(width, bounds.x));
157
+ const y = Math.max(0, Math.min(height, bounds.y));
158
+ return {
159
+ x,
160
+ y,
161
+ width: Math.max(0, Math.min(width - x, bounds.width)),
162
+ height: Math.max(0, Math.min(height - y, bounds.height)),
163
+ };
164
+ }
165
+ function createWebViewHost() {
166
+ const systemBarCssVariables = {
167
+ top: '--safe-area-inset-top',
168
+ right: '--safe-area-inset-right',
169
+ bottom: '--safe-area-inset-bottom',
170
+ left: '--safe-area-inset-left',
171
+ };
172
+ function readState() {
173
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
174
+ throw new Error('Capacitor viewport requires a WebView host.');
175
+ }
176
+ const width = Math.max(1, Math.round(window.innerWidth));
177
+ const height = Math.max(1, Math.round(window.innerHeight));
178
+ const visual = window.visualViewport;
179
+ const style = getComputedStyle(document.documentElement);
180
+ // The visual viewport can be panned above the keyboard. At scale 1 and
181
+ // full layout width, both its top offset and remaining bottom gap obscure
182
+ // persistent controls; zoom/pan at another scale is not a keyboard inset.
183
+ const visualUsable = visual !== null && visual !== undefined
184
+ && visual.scale === 1 && visual.width >= width - 1;
185
+ const keyboardTop = visualUsable ? Math.max(0, Math.round(visual.offsetTop)) : 0;
186
+ const keyboardBottom = visualUsable
187
+ ? Math.max(0, Math.round(height - visual.offsetTop - visual.height))
188
+ : 0;
189
+ return {
190
+ width,
191
+ height,
192
+ safeAreaInsets: readTargetViewportSafeAreaInsets(style),
193
+ systemBarInsets: readTargetViewportSafeAreaInsets(style, systemBarCssVariables),
194
+ keyboardInsets: { top: keyboardTop, right: 0, bottom: keyboardBottom, left: 0 },
195
+ };
196
+ }
197
+ return {
198
+ readState,
199
+ onChange(callback) {
200
+ window.addEventListener('resize', callback);
201
+ window.addEventListener('orientationchange', callback);
202
+ const visual = window.visualViewport;
203
+ visual?.addEventListener('resize', callback);
204
+ visual?.addEventListener('scroll', callback);
205
+ const rootStyleObserver = typeof MutationObserver === 'undefined'
206
+ ? undefined
207
+ : new MutationObserver(callback);
208
+ rootStyleObserver?.observe(document.documentElement, {
209
+ attributes: true,
210
+ attributeFilter: ['style', 'class'],
211
+ });
212
+ return () => {
213
+ window.removeEventListener('resize', callback);
214
+ window.removeEventListener('orientationchange', callback);
215
+ visual?.removeEventListener('resize', callback);
216
+ visual?.removeEventListener('scroll', callback);
217
+ rootStyleObserver?.disconnect();
218
+ };
219
+ },
220
+ };
221
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/adapter-capacitor",
3
- "version": "0.4.11",
3
+ "version": "0.5.0",
4
4
  "description": "Capacitor platform adapter for mpgd Android and iOS shells.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,14 +34,21 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@mpgd/bridge": "0.9.1",
38
- "@mpgd/capacitor-game-services": "0.5.3",
39
- "@mpgd/platform": "0.12.1"
37
+ "@capacitor/app": "8.1.1",
38
+ "@mpgd/capacitor-game-services": "0.6.0",
39
+ "@mpgd/platform": "0.13.0",
40
+ "@mpgd/target-config": "0.16.0",
41
+ "@mpgd/bridge": "0.10.0"
42
+ },
43
+ "peerDependencies": {
44
+ "@capacitor/core": "^8.5.1"
40
45
  },
41
46
  "devDependencies": {
47
+ "@capacitor/core": "8.5.2",
42
48
  "ttsc": "0.30.4",
43
49
  "typescript": "7.0.2",
44
- "vitest": "^4.0.0"
50
+ "vitest": "^4.0.0",
51
+ "@mpgd/game-services": "0.16.0"
45
52
  },
46
53
  "main": "./dist/index.js",
47
54
  "types": "./dist/index.d.ts",
@@ -53,6 +60,7 @@
53
60
  "lint": "ttsc --noEmit",
54
61
  "format": "ttsc format",
55
62
  "fix": "ttsc fix",
56
- "test": "vitest run"
63
+ "test:native-http-runtime": "node test/native-http-runtime.mjs",
64
+ "test": "vitest run && node test/package-import.mjs && node test/native-http-runtime.mjs"
57
65
  }
58
66
  }