@mpgd/adapter-capacitor 0.4.12 → 0.6.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 +318 -0
- package/dist/admob.d.ts +26 -0
- package/dist/admob.js +288 -0
- package/dist/app-events.d.ts +23 -0
- package/dist/app-events.js +332 -0
- package/dist/app-store-server.d.ts +12 -0
- package/dist/app-store-server.js +119 -0
- package/dist/index.d.ts +14 -5
- package/dist/index.js +162 -13
- package/dist/native-http.d.ts +50 -0
- package/dist/native-http.js +259 -0
- package/dist/providers.d.ts +25 -0
- package/dist/providers.js +312 -0
- package/dist/viewport.d.ts +25 -0
- package/dist/viewport.js +221 -0
- package/package.json +29 -5
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { App } from '@capacitor/app';
|
|
2
|
+
const maxWarmUrlsBeforeInitial = 32;
|
|
3
|
+
// Covers a near-simultaneous duplicate from getLaunchUrl and appUrlOpen, not a later tap.
|
|
4
|
+
const coldUrlDedupeWindowMs = 2_000;
|
|
5
|
+
function defaultVisibility() {
|
|
6
|
+
return typeof document === 'undefined' ? null : document;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Owns only handles it registers. Disposing during an awaited addListener
|
|
10
|
+
* removes the eventual handle without touching another gateway's listeners.
|
|
11
|
+
*/
|
|
12
|
+
export function createCapacitorAppEvents(input) {
|
|
13
|
+
const app = input.app ?? App;
|
|
14
|
+
const visibility = input.visibility === undefined ? defaultVisibility() : input.visibility;
|
|
15
|
+
const pauseCallbacks = new Set();
|
|
16
|
+
const resumeCallbacks = new Set();
|
|
17
|
+
const gameUrlCallbacks = new Set();
|
|
18
|
+
const oauthCallbacks = new Set();
|
|
19
|
+
const backHandlers = [];
|
|
20
|
+
const handles = [];
|
|
21
|
+
const warmUrlsBeforeInitial = new Set();
|
|
22
|
+
let disposed = false;
|
|
23
|
+
let initialResolved = false;
|
|
24
|
+
let appActive = true;
|
|
25
|
+
let visibilityActive = visibility?.hidden !== true;
|
|
26
|
+
let currentActive = true;
|
|
27
|
+
let transitionVersion = 0;
|
|
28
|
+
let stateEvents = 0;
|
|
29
|
+
let externalActivities = 0;
|
|
30
|
+
let backInProgress = false;
|
|
31
|
+
let startFailureReported = false;
|
|
32
|
+
let recentColdUrl;
|
|
33
|
+
let recentColdUntil = 0;
|
|
34
|
+
let startPromise;
|
|
35
|
+
let backPromise;
|
|
36
|
+
let initialPromise;
|
|
37
|
+
const reportError = (error) => {
|
|
38
|
+
try {
|
|
39
|
+
if (input.onError !== undefined) {
|
|
40
|
+
input.onError(error);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.error('Capacitor app event failed.', error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Reporting must not replace the original listener or registration error.
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
function emit(callbacks, version) {
|
|
51
|
+
for (const callback of [...callbacks]) {
|
|
52
|
+
// A callback can open or close external UI and reenter reconcile().
|
|
53
|
+
// Never continue an older resume/pause emission after that transition.
|
|
54
|
+
if (disposed || version !== transitionVersion) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
callback();
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
reportError(error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function reconcile() {
|
|
66
|
+
if (disposed) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const active = appActive && visibilityActive && externalActivities === 0;
|
|
70
|
+
if (active === currentActive) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
currentActive = active;
|
|
74
|
+
transitionVersion += 1;
|
|
75
|
+
emit(active ? resumeCallbacks : pauseCallbacks, transitionVersion);
|
|
76
|
+
}
|
|
77
|
+
function classify(url) {
|
|
78
|
+
try {
|
|
79
|
+
const kind = input.classifyIncomingUrl?.(url);
|
|
80
|
+
return kind === 'game' || kind === 'oauth' ? kind : null;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
reportError(error);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function dispatchWarmUrl(url) {
|
|
88
|
+
if (disposed || url.trim() === '') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (url === recentColdUrl && Date.now() <= recentColdUntil) {
|
|
92
|
+
// Capacitor can deliver the same launch through getLaunchUrl and
|
|
93
|
+
// appUrlOpen. Suppress only that immediate duplicate, not a later tap.
|
|
94
|
+
recentColdUrl = undefined;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const kind = classify(url);
|
|
98
|
+
if (kind === null) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const callbacks = kind === 'game' ? gameUrlCallbacks : oauthCallbacks;
|
|
102
|
+
if (!initialResolved && callbacks.size > 0) {
|
|
103
|
+
// Keep enough recent entries to suppress a cold/warm duplicate without
|
|
104
|
+
// retaining an unbounded history when the host never requests cold URLs.
|
|
105
|
+
if (warmUrlsBeforeInitial.size >= maxWarmUrlsBeforeInitial) {
|
|
106
|
+
const oldest = warmUrlsBeforeInitial.values().next().value;
|
|
107
|
+
if (oldest !== undefined) {
|
|
108
|
+
warmUrlsBeforeInitial.delete(oldest);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
warmUrlsBeforeInitial.add(url);
|
|
112
|
+
}
|
|
113
|
+
const event = { url, source: 'warm' };
|
|
114
|
+
for (const callback of [...callbacks]) {
|
|
115
|
+
try {
|
|
116
|
+
callback(event);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
reportError(error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function own(registration) {
|
|
124
|
+
const handle = await registration;
|
|
125
|
+
if (disposed) {
|
|
126
|
+
await handle.remove();
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
handles.push(handle);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function onVisibilityChange() {
|
|
133
|
+
visibilityActive = visibility?.hidden !== true;
|
|
134
|
+
reconcile();
|
|
135
|
+
}
|
|
136
|
+
function start() {
|
|
137
|
+
if (disposed) {
|
|
138
|
+
return Promise.reject(new Error('Capacitor app events are disposed.'));
|
|
139
|
+
}
|
|
140
|
+
if (startPromise !== undefined) {
|
|
141
|
+
return startPromise;
|
|
142
|
+
}
|
|
143
|
+
visibility?.addEventListener('visibilitychange', onVisibilityChange);
|
|
144
|
+
onVisibilityChange();
|
|
145
|
+
const beforeRead = stateEvents;
|
|
146
|
+
startPromise = Promise.all([
|
|
147
|
+
own(app.addListener('appStateChange', ({ isActive }) => {
|
|
148
|
+
stateEvents += 1;
|
|
149
|
+
appActive = isActive;
|
|
150
|
+
reconcile();
|
|
151
|
+
})),
|
|
152
|
+
own(app.addListener('appUrlOpen', ({ url }) => dispatchWarmUrl(url))),
|
|
153
|
+
app.getState().then(({ isActive }) => {
|
|
154
|
+
if (!disposed && stateEvents === beforeRead) {
|
|
155
|
+
appActive = isActive;
|
|
156
|
+
reconcile();
|
|
157
|
+
}
|
|
158
|
+
}),
|
|
159
|
+
]).then(() => undefined);
|
|
160
|
+
return startPromise;
|
|
161
|
+
}
|
|
162
|
+
function startForSubscription() {
|
|
163
|
+
void start().catch((error) => {
|
|
164
|
+
if (startFailureReported) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
startFailureReported = true;
|
|
168
|
+
reportError(error);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async function dispatchBack(event) {
|
|
172
|
+
for (const handler of [...backHandlers].reverse()) {
|
|
173
|
+
try {
|
|
174
|
+
if (await handler(event)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (disposed) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
reportError(error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (disposed) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (event.canGoBack) {
|
|
189
|
+
if (input.historyBack !== undefined) {
|
|
190
|
+
input.historyBack();
|
|
191
|
+
}
|
|
192
|
+
else if (typeof history !== 'undefined') {
|
|
193
|
+
history.back();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
await app.exitApp();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function startBackListener() {
|
|
201
|
+
if (input.target !== 'android' || backPromise !== undefined || disposed) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
backPromise = own(app.addListener('backButton', (event) => {
|
|
205
|
+
if (backInProgress || disposed) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
backInProgress = true;
|
|
209
|
+
void dispatchBack(event).catch(reportError).finally(() => {
|
|
210
|
+
backInProgress = false;
|
|
211
|
+
});
|
|
212
|
+
}));
|
|
213
|
+
void backPromise.catch(reportError);
|
|
214
|
+
}
|
|
215
|
+
function subscribe(callbacks, callback) {
|
|
216
|
+
if (disposed) {
|
|
217
|
+
return () => { };
|
|
218
|
+
}
|
|
219
|
+
callbacks.add(callback);
|
|
220
|
+
startForSubscription();
|
|
221
|
+
return () => {
|
|
222
|
+
callbacks.delete(callback);
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function getInitial() {
|
|
226
|
+
if (initialPromise !== undefined) {
|
|
227
|
+
return initialPromise;
|
|
228
|
+
}
|
|
229
|
+
startForSubscription();
|
|
230
|
+
initialPromise = app.getLaunchUrl().then((launch) => {
|
|
231
|
+
initialResolved = true;
|
|
232
|
+
const url = launch?.url;
|
|
233
|
+
const seenWarm = url === undefined ? false : warmUrlsBeforeInitial.has(url);
|
|
234
|
+
warmUrlsBeforeInitial.clear();
|
|
235
|
+
if (disposed || url === undefined || url.trim() === '' || seenWarm) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
const kind = classify(url);
|
|
239
|
+
if (kind === null) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
recentColdUrl = url;
|
|
243
|
+
recentColdUntil = Date.now() + coldUrlDedupeWindowMs;
|
|
244
|
+
return { kind, event: { url, source: 'cold' } };
|
|
245
|
+
}, (error) => {
|
|
246
|
+
initialResolved = true;
|
|
247
|
+
warmUrlsBeforeInitial.clear();
|
|
248
|
+
throw error;
|
|
249
|
+
});
|
|
250
|
+
return initialPromise;
|
|
251
|
+
}
|
|
252
|
+
async function dispose() {
|
|
253
|
+
if (disposed) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
disposed = true;
|
|
257
|
+
visibility?.removeEventListener('visibilitychange', onVisibilityChange);
|
|
258
|
+
pauseCallbacks.clear();
|
|
259
|
+
resumeCallbacks.clear();
|
|
260
|
+
gameUrlCallbacks.clear();
|
|
261
|
+
oauthCallbacks.clear();
|
|
262
|
+
backHandlers.length = 0;
|
|
263
|
+
warmUrlsBeforeInitial.clear();
|
|
264
|
+
recentColdUrl = undefined;
|
|
265
|
+
// Pending registrations remove themselves in own() if they later resolve.
|
|
266
|
+
// Teardown must not hang on an SDK registration that never does.
|
|
267
|
+
await Promise.allSettled(handles.splice(0).map((handle) => handle.remove()));
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
onPause(callback) {
|
|
271
|
+
// start() may synchronously emit the first pause while subscribing.
|
|
272
|
+
const alreadyPaused = !disposed && currentActive === false;
|
|
273
|
+
const unsubscribe = subscribe(pauseCallbacks, callback);
|
|
274
|
+
if (alreadyPaused) {
|
|
275
|
+
try {
|
|
276
|
+
callback();
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
reportError(error);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return unsubscribe;
|
|
283
|
+
},
|
|
284
|
+
onResume(callback) {
|
|
285
|
+
return subscribe(resumeCallbacks, callback);
|
|
286
|
+
},
|
|
287
|
+
onBackButton(handler) {
|
|
288
|
+
if (disposed || input.target !== 'android') {
|
|
289
|
+
return () => { };
|
|
290
|
+
}
|
|
291
|
+
backHandlers.push(handler);
|
|
292
|
+
startBackListener();
|
|
293
|
+
return () => {
|
|
294
|
+
const index = backHandlers.indexOf(handler);
|
|
295
|
+
if (index >= 0) {
|
|
296
|
+
backHandlers.splice(index, 1);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
},
|
|
300
|
+
onGameUrlOpen(callback) {
|
|
301
|
+
return subscribe(gameUrlCallbacks, callback);
|
|
302
|
+
},
|
|
303
|
+
onOAuthRedirect(callback) {
|
|
304
|
+
return subscribe(oauthCallbacks, callback);
|
|
305
|
+
},
|
|
306
|
+
async getInitialGameUrl() {
|
|
307
|
+
const initial = await getInitial();
|
|
308
|
+
return initial?.kind === 'game' ? initial.event : null;
|
|
309
|
+
},
|
|
310
|
+
async getInitialOAuthRedirect() {
|
|
311
|
+
const initial = await getInitial();
|
|
312
|
+
return initial?.kind === 'oauth' ? initial.event : null;
|
|
313
|
+
},
|
|
314
|
+
beginExternalActivity() {
|
|
315
|
+
if (disposed) {
|
|
316
|
+
return () => { };
|
|
317
|
+
}
|
|
318
|
+
externalActivities += 1;
|
|
319
|
+
reconcile();
|
|
320
|
+
let released = false;
|
|
321
|
+
return () => {
|
|
322
|
+
if (released) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
released = true;
|
|
326
|
+
externalActivities -= 1;
|
|
327
|
+
reconcile();
|
|
328
|
+
};
|
|
329
|
+
},
|
|
330
|
+
dispose,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AppStoreEnvironment, type AppStoreSignedTransactionVerifier } from '@mpgd/game-services/app-store-verifier';
|
|
2
|
+
export interface CreateAppleSignedTransactionVerifierOptions {
|
|
3
|
+
/** DER-encoded Apple root certificates, supplied by the server operator. */
|
|
4
|
+
readonly rootCertificates: readonly Uint8Array[];
|
|
5
|
+
readonly environment: AppStoreEnvironment;
|
|
6
|
+
readonly bundleId: string;
|
|
7
|
+
/** Required by Apple's verifier in production. */
|
|
8
|
+
readonly appAppleId?: number;
|
|
9
|
+
readonly enableOnlineChecks?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Verify the App Store JWS before exposing any decoded purchase fields. */
|
|
12
|
+
export declare function createAppleSignedTransactionVerifier(options: CreateAppleSignedTransactionVerifierOptions): AppStoreSignedTransactionVerifier;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Environment, SignedDataVerifier, VerificationException, VerificationStatus, } from '@apple/app-store-server-library';
|
|
2
|
+
import { AppStoreDependencyUnavailableError, } from '@mpgd/game-services/app-store-verifier';
|
|
3
|
+
/** Verify the App Store JWS before exposing any decoded purchase fields. */
|
|
4
|
+
export function createAppleSignedTransactionVerifier(options) {
|
|
5
|
+
if (!Array.isArray(options.rootCertificates)
|
|
6
|
+
|| options.rootCertificates.length === 0
|
|
7
|
+
|| options.rootCertificates.some((certificate) => !(certificate instanceof Uint8Array)
|
|
8
|
+
|| certificate.length === 0)) {
|
|
9
|
+
throw new TypeError('Apple root certificates are required.');
|
|
10
|
+
}
|
|
11
|
+
if (options.environment !== 'Production' && options.environment !== 'Sandbox') {
|
|
12
|
+
throw new TypeError('App Store environment must be Production or Sandbox.');
|
|
13
|
+
}
|
|
14
|
+
if (options.bundleId.trim() === '') {
|
|
15
|
+
throw new TypeError('App Store bundle ID is required.');
|
|
16
|
+
}
|
|
17
|
+
if (options.environment === 'Production'
|
|
18
|
+
&& (!Number.isSafeInteger(options.appAppleId) || (options.appAppleId ?? 0) <= 0)) {
|
|
19
|
+
throw new TypeError('Production App Store verification requires appAppleId.');
|
|
20
|
+
}
|
|
21
|
+
if (options.environment === 'Production' && options.enableOnlineChecks === false) {
|
|
22
|
+
throw new TypeError('Production App Store verification requires online certificate checks.');
|
|
23
|
+
}
|
|
24
|
+
const verifier = new SignedDataVerifier(options.rootCertificates.map((certificate) => Buffer.from(certificate)), options.enableOnlineChecks ?? true, options.environment === 'Production' ? Environment.PRODUCTION : Environment.SANDBOX, options.bundleId, options.appAppleId);
|
|
25
|
+
return {
|
|
26
|
+
async verifyAndDecode(input) {
|
|
27
|
+
if (input.environment !== options.environment || input.bundleId !== options.bundleId) {
|
|
28
|
+
return { status: 'rejected', reason: 'APP_STORE_VERIFIER_SCOPE_MISMATCH' };
|
|
29
|
+
}
|
|
30
|
+
if (typeof input.signedTransaction !== 'string'
|
|
31
|
+
|| input.signedTransaction.length === 0
|
|
32
|
+
|| input.signedTransaction.length > 512 * 1024) {
|
|
33
|
+
return { status: 'rejected', reason: 'APP_STORE_SIGNED_TRANSACTION_INVALID' };
|
|
34
|
+
}
|
|
35
|
+
input.signal.throwIfAborted();
|
|
36
|
+
let decoded;
|
|
37
|
+
try {
|
|
38
|
+
decoded = await waitForVerification(verifier.verifyAndDecodeTransaction(input.signedTransaction), input.signal);
|
|
39
|
+
input.signal.throwIfAborted();
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (input.signal.aborted) {
|
|
43
|
+
throw input.signal.reason;
|
|
44
|
+
}
|
|
45
|
+
if (error instanceof VerificationException) {
|
|
46
|
+
if (error.status === VerificationStatus.RETRYABLE_VERIFICATION_FAILURE) {
|
|
47
|
+
throw new AppStoreDependencyUnavailableError('Apple signature checks are temporarily unavailable.', error);
|
|
48
|
+
}
|
|
49
|
+
return { status: 'rejected', reason: 'APP_STORE_SIGNATURE_INVALID' };
|
|
50
|
+
}
|
|
51
|
+
// Only Apple's explicit retryable status is a transient dependency
|
|
52
|
+
// failure. Unexpected adapter/library faults must reach the caller.
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
const payload = decodeOneTimeTransaction(decoded);
|
|
56
|
+
return payload === undefined
|
|
57
|
+
? { status: 'rejected', reason: 'APP_STORE_SIGNED_TRANSACTION_INVALID' }
|
|
58
|
+
: { status: 'verified', payload };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async function waitForVerification(operation, signal) {
|
|
63
|
+
signal.throwIfAborted();
|
|
64
|
+
let onAbort;
|
|
65
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
66
|
+
onAbort = () => reject(signal.reason ?? new Error('Verification was aborted.'));
|
|
67
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
68
|
+
if (signal.aborted) {
|
|
69
|
+
onAbort();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
try {
|
|
73
|
+
return await Promise.race([operation, aborted]);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
if (onAbort !== undefined) {
|
|
77
|
+
signal.removeEventListener('abort', onAbort);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function decodeOneTimeTransaction(decoded) {
|
|
82
|
+
if (!nonEmpty(decoded.transactionId)
|
|
83
|
+
|| !nonEmpty(decoded.originalTransactionId)
|
|
84
|
+
|| !nonEmpty(decoded.bundleId)
|
|
85
|
+
|| !nonEmpty(decoded.productId)
|
|
86
|
+
|| !validTimestamp(decoded.purchaseDate)
|
|
87
|
+
|| !validTimestamp(decoded.signedDate)
|
|
88
|
+
|| (decoded.environment !== 'Production' && decoded.environment !== 'Sandbox')
|
|
89
|
+
|| (decoded.type !== 'Consumable' && decoded.type !== 'Non-Consumable')
|
|
90
|
+
|| (decoded.quantity !== undefined
|
|
91
|
+
&& (!Number.isSafeInteger(decoded.quantity) || decoded.quantity <= 0))
|
|
92
|
+
|| (decoded.appAccountToken !== undefined && !nonEmpty(decoded.appAccountToken))
|
|
93
|
+
|| (decoded.revocationDate !== undefined && !validTimestamp(decoded.revocationDate))
|
|
94
|
+
|| (decoded.expiresDate !== undefined && !validTimestamp(decoded.expiresDate))
|
|
95
|
+
|| (decoded.isUpgraded !== undefined && typeof decoded.isUpgraded !== 'boolean')) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
transactionId: decoded.transactionId,
|
|
100
|
+
originalTransactionId: decoded.originalTransactionId,
|
|
101
|
+
bundleId: decoded.bundleId,
|
|
102
|
+
productId: decoded.productId,
|
|
103
|
+
purchaseDate: decoded.purchaseDate,
|
|
104
|
+
signedDate: decoded.signedDate,
|
|
105
|
+
environment: decoded.environment,
|
|
106
|
+
type: decoded.type,
|
|
107
|
+
...(decoded.quantity === undefined ? {} : { quantity: decoded.quantity }),
|
|
108
|
+
...(decoded.appAccountToken === undefined ? {} : { appAccountToken: decoded.appAccountToken }),
|
|
109
|
+
...(decoded.revocationDate === undefined ? {} : { revocationDate: decoded.revocationDate }),
|
|
110
|
+
...(decoded.expiresDate === undefined ? {} : { expiresDate: decoded.expiresDate }),
|
|
111
|
+
...(decoded.isUpgraded === undefined ? {} : { isUpgraded: decoded.isUpgraded }),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function nonEmpty(value) {
|
|
115
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
116
|
+
}
|
|
117
|
+
function validTimestamp(value) {
|
|
118
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
|
|
119
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
1
|
+
import { type PlatformGateway, type PlatformTarget, type ViewportAdapter } from '@mpgd/platform';
|
|
2
|
+
import { type CapacitorAppEventsApi, type CapacitorIncomingUrlKind, type CapacitorVisibilitySource } from './app-events.js';
|
|
3
|
+
import { type CapacitorServiceProvider, type NativeBridge } from './providers.js';
|
|
4
|
+
export { createCapacitorProviderRegistry, type CapacitorServiceProvider, type NativeBridge, } from './providers.js';
|
|
5
|
+
export { createCapacitorAppEvents, type CapacitorAppEventsApi, type CapacitorIncomingUrlKind, type CapacitorVisibilitySource, } from './app-events.js';
|
|
6
|
+
export { CapacitorNativeHttpError, createCapacitorNativeJsonTransport, type CapacitorJsonRequest, type CapacitorJsonResponse, type CapacitorNativeHttpErrorCode, type CapacitorNativeJsonTransport, type CreateCapacitorNativeJsonTransportInput, } from './native-http.js';
|
|
7
|
+
export { createCapacitorViewport, type CapacitorViewportBaseState, type CapacitorOccupiedSurfaceInput, type CapacitorViewportController, type CapacitorViewportHost, } from './viewport.js';
|
|
6
8
|
export declare function createCapacitorPlatformGateway(input: {
|
|
7
9
|
readonly target: Extract<PlatformTarget, 'android' | 'ios'>;
|
|
8
10
|
readonly appVersion: string;
|
|
9
11
|
readonly buildId: string;
|
|
10
12
|
readonly bridge?: NativeBridge;
|
|
13
|
+
readonly providers?: readonly CapacitorServiceProvider[];
|
|
14
|
+
readonly app?: CapacitorAppEventsApi;
|
|
15
|
+
readonly visibility?: CapacitorVisibilitySource | null;
|
|
16
|
+
readonly classifyIncomingUrl?: (url: string) => CapacitorIncomingUrlKind | null;
|
|
17
|
+
readonly historyBack?: () => void;
|
|
18
|
+
readonly onAppEventError?: (error: unknown) => void;
|
|
19
|
+
readonly viewport?: ViewportAdapter;
|
|
11
20
|
}): PlatformGateway;
|