@makefully/adaptfully 3.9.0 → 3.10.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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 3.10.0 — 2026-07-17
6
+
7
+ ### Added
8
+
9
+ - **`social-auth`** — Capgo `@capgo/capacitor-social-login` auth plugin for Capacitor platforms. Requires `packager: "capacitor"` and `platforms.<name>.socialLogin` provider config. Prebuild writes `social-login-config.js`; Wrapfully installs the native plugin when `socialLogin` is set on the build spec.
10
+ - **`CapacitorPackager`** CSP/viewport HTML extras and social-auth validation/templates.
11
+
12
+ ### Changed
13
+
14
+ - **Builder family defaults** — `android` / `ios` / `ios-sim` (and `-dev`) resolve to Wrapfully family **`capacitor`**. Explicit `packager: "cordova"` still selects Cordova for that platform. Composite `cordova` / `apple` routes are not used.
15
+ - **`resolveBuildSpec`** includes `socialLogin: boolean`.
16
+
5
17
  ## 3.9.0 — 2026-07-15
6
18
 
7
19
  ### Added
package/README.md CHANGED
@@ -91,7 +91,8 @@ storage.getObject('currentGame');
91
91
  |------------|--------------|---------|
92
92
  | `google-auth` | `adaptfully.register('auth', adaptfully.auth.Google)` | Web, Android, iOS |
93
93
  | `steam-auth` | `adaptfully.register('auth', adaptfully.auth.Steam)` | Steam / Electron (via [steamworks.js](https://github.com/ceifa/steamworks.js)) |
94
- | `dev-auth` | `adaptfully.register('auth', adaptfully.auth.Dev)` | Local dev (test user) |
94
+ | `social-auth` | `adaptfully.register('auth', adaptfully.auth.Social)` | Capacitor (via [@capgo/capacitor-social-login](https://github.com/Cap-go/capacitor-social-login)) |
95
+ | `dev-auth` | `adaptfully.register('auth', adaptfully.auth.Dev)` | Local / testing |
95
96
 
96
97
  Use plugin keys in `config.platforms.<platform>.registrations`. Custom deploy scripts use a path relative to the deploy folder instead:
97
98
 
@@ -133,10 +134,14 @@ Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or `
133
134
  | `web` | `game-config.js` for **`uwp`** platform prebuilds |
134
135
  | `electron` | `main.js` (Electron shell); `preload.js` when **`steam-auth`** is registered |
135
136
  | `cordova` | `cordova.js` stub, `game-config.js`, CSP/viewport HTML extras |
136
- | `capacitor` | `game-config.js` (more Capacitor-specific output planned) |
137
+ | `capacitor` | `game-config.js`, Capacitor CSP/viewport; `social-login-config.js` when **`social-auth`** is registered |
138
+
139
+ Android/iOS Wrapfully builds default to the **`capacitor`** builder family. Set `packager: "cordova"` on a platform to use Cordova for that target.
137
140
 
138
141
  **`steam-auth` requires `packager: "electron"`** and `platforms.<name>.steamId` (Steamworks app ID for the client preload). Steam **upload** app IDs live separately in each Steam deployment’s `manifest.json` — see Wrapfully docs.
139
142
 
143
+ **`social-auth` requires `packager: "capacitor"`** and `platforms.<name>.socialLogin` (Capgo provider client IDs). Wrapfully installs `@capgo/capacitor-social-login` when building.
144
+
140
145
  ```json
141
146
  {
142
147
  "config": {
@@ -149,6 +154,20 @@ Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or `
149
154
  "packager": "electron",
150
155
  "steamId": 1234567,
151
156
  "registrations": { "auth": "steam-auth" }
157
+ },
158
+ "android": {
159
+ "packager": "capacitor",
160
+ "registrations": { "auth": "social-auth", "storage": "localStorage" },
161
+ "socialLogin": {
162
+ "providers": { "google": true, "apple": true },
163
+ "google": {
164
+ "webClientId": "….apps.googleusercontent.com",
165
+ "iOSClientId": "….apps.googleusercontent.com"
166
+ },
167
+ "apple": {
168
+ "clientId": "com.example.service"
169
+ }
170
+ }
152
171
  }
153
172
  }
154
173
  }
@@ -169,6 +188,17 @@ The renderer `steam-auth` plugin reads the Steam ID from that bridge. When Steam
169
188
  | `autoLoginStorageKey` | `lastLoggedIn` | Storage key written with the Steam ID on login |
170
189
  | `steamReadyTimeoutMs` | `10000` | Max wait when a bridge exists but identity is not yet ready |
171
190
 
191
+ #### Social auth (`social-auth`)
192
+
193
+ When `social-auth` is registered on a **`capacitor`** platform, Adaptfully prebuild writes **`social-login-config.js`** (`window.__ADAPTFULLY_SOCIAL_LOGIN__`) from `platforms.<name>.socialLogin`. Wrapfully installs [`@capgo/capacitor-social-login`](https://github.com/Cap-go/capacitor-social-login) and syncs it into the native project (same role as `steamworks.js` for Electron).
194
+
195
+ The runtime plugin calls Capgo `SocialLogin.initialize` / `login` / `logout` / `isLoggedIn`. Default provider is `google` on Android and `apple` on iOS when both are enabled (override with `socialLogin.defaultProvider`). Optional config keys:
196
+
197
+ | Key | Default | Purpose |
198
+ |-----|---------|---------|
199
+ | `autoLoginStorageKey` | `lastLoggedIn` | Storage key written with the user id on login |
200
+ | `socialReadyTimeoutMs` | `10000` | Max wait for the Capgo plugin bridge |
201
+
172
202
  ### Node API
173
203
 
174
204
  ```javascript
@@ -211,7 +241,7 @@ After prebuild, the build and deploy stages zip `output/<platform>-prebuild/` an
211
241
 
212
242
  ```bash
213
243
  npx adaptfully prebuild web
214
- npx adaptfully deploy steam http://build.example.com:9630/
244
+ npx adaptfully deploy steam http://build.example.com:9633/
215
245
  ```
216
246
 
217
247
  For web-only hosting (no Wrapfully), stop after prebuild and upload `output/web-prebuild/` yourself.
@@ -246,10 +276,10 @@ Examples:
246
276
  npx adaptfully prebuild web
247
277
 
248
278
  # Build for Steam via Wrapfully
249
- npx adaptfully build steam http://build.example.com:9630/
279
+ npx adaptfully build steam http://build.example.com:9633/
250
280
 
251
281
  # Full Steam deploy (build + upload when steam.json credentials are present)
252
- npx adaptfully deploy steam http://build.example.com:9630/
282
+ npx adaptfully deploy steam http://build.example.com:9633/
253
283
  ```
254
284
 
255
285
  Add scripts to your project's `package.json`:
@@ -278,7 +308,7 @@ The server URL is resolved in this order:
278
308
  1. CLI argument
279
309
  2. `WRAPFULLY_SERVER` environment variable
280
310
  3. `server` field in `wrapfully.json`
281
- 4. `http://localhost:9630/`
311
+ 4. `http://localhost:9633/`
282
312
 
283
313
  Keep server addresses and credentials out of version control — use environment variables or a gitignored `wrapfully.json`.
284
314
 
@@ -293,7 +323,7 @@ The client POSTs a zip stream built from `output/<platform>-prebuild/` to:
293
323
  For example, a project named `mygame` at version `1.2.0` with builder `android`:
294
324
 
295
325
  ```
296
- http://build.example.com:9630/android/mygame-1.2.0
326
+ http://build.example.com:9633/android/mygame-1.2.0
297
327
  ```
298
328
 
299
329
  The server extracts the zip, reads the embedded `package.json`, runs the build for that platform, and streams a zip of artifacts back to the client.
@@ -415,6 +445,7 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
415
445
  | `platforms.<name>.builders` | wrapfully-deploy | Map additional Wrapfully builder names to a platform |
416
446
  | `platforms.<name>.packager` | Prebuild | `web` (default), `electron`, `cordova`, or `capacitor` — selects the packager class that adds platform-specific files during prebuild |
417
447
  | `platforms.<name>.steamId` | Electron + steam-auth | Steamworks app ID baked into `preload.js` (client). Upload app IDs belong in the Steam deployment `manifest.json` |
448
+ | `platforms.<name>.socialLogin` | Capacitor + social-auth | Capgo provider config (`providers`, `google.webClientId`, `apple.clientId`, optional `defaultProvider`) |
418
449
  | `properties` | Cordova | Cordova config.xml entries (plugins, allow-navigation, etc.) |
419
450
 
420
451
  ### `wrapfully.json`
@@ -424,7 +455,7 @@ Optional. Fields are shallow-merged into `package.json`'s `config`:
424
455
  ```json
425
456
  {
426
457
  "deployFolder": "dist",
427
- "server": "http://build.example.com:9630/",
458
+ "server": "http://build.example.com:9633/",
428
459
  "title": "My Game",
429
460
  "packageName": "com.example.mygame"
430
461
  }
@@ -453,12 +484,8 @@ Each builder name becomes a path segment on the server. Some builds require a sp
453
484
  | `webapp` | Service-worker web app (optionally SFTP deploy) |
454
485
  | `steam` | Windows + Mac + Linux, uploads to Steam |
455
486
  | `steam-dev` | Debug Windows + Mac + Linux, no Steam upload |
456
- | `cordova` | Release Android + iOS |
457
- | `cordova-dev` | Debug Android + iOS |
458
- | `apple` | Release Mac + iOS |
459
- | `apple-dev` | Release Mac + debug iOS |
460
487
 
461
- For a single platform, pass the specific builder name rather than a composite.
488
+ For a single platform, pass the specific builder name.
462
489
 
463
490
  ### Platform package requirements
464
491
 
@@ -508,7 +535,7 @@ To deploy to Google Play, also include `assets/meta/deployments/<deployment>/goo
508
535
  }
509
536
  ```
510
537
 
511
- #### Apple (`ios`, `ios-dev`, `ios-sim`, `mac`, `apple`, `apple-dev`)
538
+ #### Apple (`ios`, `ios-dev`, `ios-sim`, `mac`)
512
539
 
513
540
  Include `assets/meta/deployments/<deployment>/build.json` with iOS signing settings:
514
541
 
@@ -544,9 +571,9 @@ To deploy to the App Store, include `assets/meta/deployments/<deployment>/apple.
544
571
  }
545
572
  ```
546
573
 
547
- #### Cordova (`cordova`, `cordova-dev`)
574
+ #### Cordova
548
575
 
549
- Requires the Android and Apple package requirements above.
576
+ Set `packager: "cordova"` on an `android` or `ios` platform to use Cordova instead of Capacitor. Requires the Android and Apple package requirements above.
550
577
 
551
578
  #### Steam (`steam`, `steam-dev`)
552
579
 
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
 
3
- const DEFAULT_SERVER = 'http://localhost:9630/';
3
+ const DEFAULT_SERVER = 'http://localhost:9633/';
4
4
 
5
5
  /**
6
6
  * @param {string} [projectRoot='.']
@@ -223,6 +223,14 @@ export class Packager {
223
223
  + `Set config.platforms.${platformLabel}.packager to "electron".`,
224
224
  );
225
225
  }
226
+
227
+ if (this.usesPlugin('social-auth', { scope: 'all' }) && this.name !== 'capacitor') {
228
+ const platformLabel = this.platformKey ?? this.platforms[0] ?? 'unknown';
229
+ throw new Error(
230
+ `Platform "${platformLabel}" uses social-auth but packager is "${this.name}". `
231
+ + `Set config.platforms.${platformLabel}.packager to "capacitor".`,
232
+ );
233
+ }
226
234
  }
227
235
 
228
236
  needsGameConfig() {
@@ -1,5 +1,81 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
1
3
  import { Packager } from './base.js';
2
4
 
5
+ const CAPACITOR_CSP = '<meta http-equiv="Content-Security-Policy" content="default-src * \'self\' data: blob: \'unsafe-inline\' \'unsafe-eval\'; style-src * \'self\' \'unsafe-inline\'; script-src * \'self\' \'unsafe-inline\' \'unsafe-eval\'; connect-src *; img-src * data: blob:; media-src * data: blob:; frame-src *;" />';
6
+
7
+ const CAPACITOR_VIEWPORT = '<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />';
8
+
9
+ /**
10
+ * @param {string | null | undefined} platformKey
11
+ * @param {{ config?: { platforms?: Record<string, { socialLogin?: object }> } }} pkg
12
+ */
13
+ export function resolvePlatformSocialLogin(platformKey, pkg) {
14
+ if (!platformKey) {
15
+ return undefined;
16
+ }
17
+ return pkg.config?.platforms?.[platformKey]?.socialLogin;
18
+ }
19
+
20
+ /**
21
+ * @param {object} socialLogin
22
+ * @param {string} platformKey
23
+ */
24
+ function normalizeSocialLoginConfig(socialLogin = {}, platformKey) {
25
+ const providers = socialLogin.providers ?? { google: true, apple: true };
26
+ const googleEnabled = providers.google !== false;
27
+ const appleEnabled = providers.apple !== false;
28
+
29
+ let defaultProvider = socialLogin.defaultProvider;
30
+ if (!defaultProvider) {
31
+ if (platformKey === 'ios' || platformKey === 'ios-dev' || platformKey === 'ios-sim') {
32
+ defaultProvider = appleEnabled ? 'apple' : 'google';
33
+ } else {
34
+ defaultProvider = googleEnabled ? 'google' : 'apple';
35
+ }
36
+ }
37
+
38
+ return {
39
+ providers: {
40
+ google: googleEnabled,
41
+ apple: appleEnabled,
42
+ facebook: providers.facebook === true,
43
+ twitter: providers.twitter === true,
44
+ },
45
+ google: socialLogin.google ?? {},
46
+ apple: socialLogin.apple ?? {},
47
+ defaultProvider,
48
+ platform: platformKey,
49
+ };
50
+ }
51
+
52
+ /**
53
+ * @param {object} config
54
+ */
55
+ function validateSocialLoginConfig(config) {
56
+ const { providers, google, apple } = config;
57
+
58
+ if (!providers.google && !providers.apple) {
59
+ throw new Error('social-auth requires at least one of socialLogin.providers.google or .apple');
60
+ }
61
+
62
+ if (providers.google) {
63
+ if (!google?.webClientId) {
64
+ throw new Error(
65
+ 'social-auth with Google requires platforms.<key>.socialLogin.google.webClientId',
66
+ );
67
+ }
68
+ }
69
+
70
+ if (providers.apple && config.platform?.startsWith('android')) {
71
+ if (!apple?.clientId) {
72
+ throw new Error(
73
+ 'social-auth with Apple on Android requires platforms.<key>.socialLogin.apple.clientId',
74
+ );
75
+ }
76
+ }
77
+ }
78
+
3
79
  export class CapacitorPackager extends Packager {
4
80
  /** @type {'capacitor'} */
5
81
  static id = 'capacitor';
@@ -7,7 +83,57 @@ export class CapacitorPackager extends Packager {
7
83
  /** @type {string[]} */
8
84
  static defaultPlatforms = ['ios', 'android'];
9
85
 
86
+ validate() {
87
+ super.validate();
88
+
89
+ if (this.usesPlugin('social-auth')) {
90
+ const socialLogin = resolvePlatformSocialLogin(this.platformKey, this.pkg);
91
+ if (!socialLogin && !this.pkg.config?.socialLogin) {
92
+ const platformLabel = this.platformKey ?? this.platforms.join(', ');
93
+ throw new Error(
94
+ `Platform "${platformLabel}" uses social-auth but platforms.${platformLabel}.socialLogin is not set.`,
95
+ );
96
+ }
97
+
98
+ const config = normalizeSocialLoginConfig(
99
+ socialLogin ?? this.pkg.config?.socialLogin,
100
+ this.platformKey ?? 'android',
101
+ );
102
+ validateSocialLoginConfig(config);
103
+ }
104
+ }
105
+
10
106
  needsGameConfig() {
11
107
  return true;
12
108
  }
109
+
110
+ /** @param {string} dest */
111
+ applyTemplates(dest) {
112
+ if (this.usesPlugin('social-auth') && this.platformKey) {
113
+ const socialLogin = resolvePlatformSocialLogin(this.platformKey, this.pkg)
114
+ ?? this.pkg.config?.socialLogin
115
+ ?? {};
116
+ const config = normalizeSocialLoginConfig(socialLogin, this.platformKey);
117
+ const content = `window.__ADAPTFULLY_SOCIAL_LOGIN__ = ${JSON.stringify(config, null, 4)};\n`;
118
+ fs.writeFileSync(path.join(dest, 'social-login-config.js'), content);
119
+ this.log('adaptfully: write social-login-config.js (social-auth)');
120
+ }
121
+
122
+ super.applyTemplates(dest);
123
+ }
124
+
125
+ buildHtmlInjection() {
126
+ const headExtras = [CAPACITOR_CSP, CAPACITOR_VIEWPORT];
127
+ const bodyScripts = [];
128
+
129
+ if (this.usesPlugin('social-auth')) {
130
+ bodyScripts.push('<script src="social-login-config.js"></script>');
131
+ }
132
+
133
+ if (this.needsGameConfig()) {
134
+ bodyScripts.push('<script src="game-config.js"></script>');
135
+ }
136
+
137
+ return this.formatHtmlInjection(headExtras, bodyScripts);
138
+ }
13
139
  }
@@ -1,19 +1,19 @@
1
1
  import { CapacitorPackager } from './capacitor.js';
2
2
  import { CordovaPackager } from './cordova.js';
3
- import { ElectronPackager, buildElectronMain, buildElectronPreload } from './electron.js';
3
+ import { ElectronPackager } from './electron.js';
4
4
  import { Packager, VALID_PACKAGERS } from './base.js';
5
5
  import { WebPackager } from './web.js';
6
6
 
7
- export { Packager, VALID_PACKAGERS } from './base.js';
8
- export { WebPackager } from './web.js';
7
+ export { CapacitorPackager, resolvePlatformSocialLogin } from './capacitor.js';
8
+ export { CordovaPackager } from './cordova.js';
9
9
  export {
10
10
  ElectronPackager,
11
11
  buildElectronMain,
12
12
  buildElectronPreload,
13
13
  resolvePlatformSteamId,
14
14
  } from './electron.js';
15
- export { CordovaPackager } from './cordova.js';
16
- export { CapacitorPackager } from './capacitor.js';
15
+ export { Packager, VALID_PACKAGERS } from './base.js';
16
+ export { WebPackager } from './web.js';
17
17
 
18
18
  /** @typedef {import('./base.js').PackagerName} PackagerName */
19
19
 
@@ -72,6 +72,13 @@ export function usesSteamAuth(registrations) {
72
72
  return Object.values(registrations).includes('steam-auth');
73
73
  }
74
74
 
75
+ /**
76
+ * @param {Record<string, string>} registrations
77
+ */
78
+ export function usesSocialAuth(registrations) {
79
+ return Object.values(registrations).includes('social-auth');
80
+ }
81
+
75
82
  /**
76
83
  * @param {string} dest
77
84
  * @param {string} platformKey
@@ -22,6 +22,10 @@ export const STANDARD_PLUGINS = {
22
22
  scripts: ['core.js', 'platform.js', 'auth/_helpers.js', 'auth/steam-auth.js'],
23
23
  registration: (key) => `adaptfully.register('${key}', adaptfully.auth.Steam);`,
24
24
  },
25
+ 'social-auth': {
26
+ scripts: ['core.js', 'platform.js', 'auth/_helpers.js', 'auth/social-auth.js'],
27
+ registration: (key) => `adaptfully.register('${key}', adaptfully.auth.Social);`,
28
+ },
25
29
  'dev-auth': {
26
30
  scripts: ['core.js', 'platform.js', 'auth/_helpers.js', 'auth/dev-auth.js'],
27
31
  registration: (key) => `adaptfully.register('${key}', adaptfully.auth.Dev);`,
@@ -52,10 +56,6 @@ export const DEFAULT_BUILDER_PLATFORMS = {
52
56
  'ios-dev': 'ios',
53
57
  'ios-sim': 'ios',
54
58
  webapp: 'web',
55
- cordova: 'cordova',
56
- 'cordova-dev': 'cordova',
57
- apple: 'apple',
58
- 'apple-dev': 'apple',
59
59
  uwp: 'uwp',
60
60
  };
61
61
 
@@ -340,7 +340,7 @@ export function resolvePublishDir(deploymentKey, metaDir = 'assets/meta') {
340
340
  const ELECTRON_TARGETS = new Set([
341
341
  'win', 'win-dev', 'mac', 'mac-dev', 'linux', 'linux-dev',
342
342
  ]);
343
- const CORDOVA_TARGETS = new Set([
343
+ const MOBILE_TARGETS = new Set([
344
344
  'android', 'android-dev', 'ios', 'ios-dev', 'ios-sim',
345
345
  ]);
346
346
 
@@ -352,8 +352,8 @@ export function resolveFamilyFromTarget(target) {
352
352
  if (ELECTRON_TARGETS.has(target)) {
353
353
  return 'electron';
354
354
  }
355
- if (CORDOVA_TARGETS.has(target)) {
356
- return 'cordova';
355
+ if (MOBILE_TARGETS.has(target)) {
356
+ return 'capacitor';
357
357
  }
358
358
  if (target === 'pwa') {
359
359
  return 'pwa';
@@ -379,9 +379,12 @@ export function resolveFamily(targets, packager) {
379
379
  if (packager === 'electron') {
380
380
  return 'electron';
381
381
  }
382
- if (packager === 'cordova' || packager === 'capacitor') {
382
+ if (packager === 'cordova') {
383
383
  return 'cordova';
384
384
  }
385
+ if (packager === 'capacitor') {
386
+ return 'capacitor';
387
+ }
385
388
 
386
389
  const families = [...new Set(targets.map((target) => resolveFamilyFromTarget(target)).filter(Boolean))];
387
390
 
@@ -396,20 +399,35 @@ export function resolveFamily(targets, packager) {
396
399
  throw new Error(`Cannot resolve builder family for targets [${targets.join(', ')}]`);
397
400
  }
398
401
 
402
+ /**
403
+ * @param {object | undefined} platform
404
+ * @returns {boolean}
405
+ */
406
+ function platformUsesSocialAuth(platform) {
407
+ if (!platform?.registrations) {
408
+ return false;
409
+ }
410
+ return Object.values(platform.registrations).includes('social-auth');
411
+ }
412
+
399
413
  /**
400
414
  * @param {string} platformKey
401
- * @param {{ config?: { platforms?: Record<string, { builder?: string | string[], packager?: string, steamworks?: boolean, deployments?: string[] }> } }} pkg
415
+ * @param {{ config?: { platforms?: Record<string, { builder?: string | string[], packager?: string, steamworks?: boolean, socialLogin?: boolean | object, registrations?: Record<string, string>, deployments?: string[] }> } }} pkg
402
416
  */
403
417
  export function resolveBuildSpec(platformKey, pkg) {
404
418
  const platform = pkg.config?.platforms?.[platformKey];
405
419
  const builder = platform?.builder ?? (platformKey === 'web' ? 'webapp' : platformKey);
406
420
  const targets = Array.isArray(builder) ? [...builder] : [builder];
421
+ const socialLogin = platform?.socialLogin === true
422
+ || (typeof platform?.socialLogin === 'object' && platform.socialLogin != null)
423
+ || platformUsesSocialAuth(platform);
407
424
 
408
425
  return {
409
426
  family: resolveFamily(targets, platform?.packager),
410
427
  targets,
411
428
  platformKey,
412
429
  steamworks: platform?.steamworks === true,
430
+ socialLogin,
413
431
  deployments: resolveDeploymentsForPlatform(platformKey, pkg),
414
432
  };
415
433
  }
@@ -0,0 +1,313 @@
1
+ /* global window */
2
+
3
+ /**
4
+ * Social auth via @capgo/capacitor-social-login.
5
+ * Adaptfully prebuild writes social-login-config.js and Wrapfully installs the Capgo plugin
6
+ * when packager is "capacitor" and registrations.auth is "social-auth".
7
+ */
8
+ (function registerSocialAuth(ns) {
9
+ const { configValue, getStorage } = ns.auth.helpers;
10
+
11
+ const DEFAULT_AUTO_LOGIN_KEY = 'lastLoggedIn';
12
+ const READY_POLL_MS = 50;
13
+ const READY_TIMEOUT_MS = 10000;
14
+
15
+ function readSocialLoginConfig() {
16
+ return window.__ADAPTFULLY_SOCIAL_LOGIN__ || {};
17
+ }
18
+
19
+ function resolveSocialLoginPlugin() {
20
+ const plugins = window.Capacitor?.Plugins;
21
+ if (plugins?.SocialLogin) {
22
+ return plugins.SocialLogin;
23
+ }
24
+ if (window.SocialLogin) {
25
+ return window.SocialLogin;
26
+ }
27
+ return null;
28
+ }
29
+
30
+ function mapLoginResult(result) {
31
+ const resultResult = result?.result ?? result;
32
+ const profile = resultResult?.profile
33
+ || resultResult?.user
34
+ || resultResult
35
+ || {};
36
+ const id = String(
37
+ profile.id
38
+ || profile.user
39
+ || profile.sub
40
+ || resultResult?.accessToken?.userId
41
+ || resultResult?.idToken
42
+ || '',
43
+ );
44
+ const email = String(profile.email || '');
45
+
46
+ if (!id && !email) {
47
+ return null;
48
+ }
49
+
50
+ return {
51
+ id: id || email,
52
+ email,
53
+ displayName: profile.name || profile.givenName || '',
54
+ };
55
+ }
56
+
57
+ class SocialAuthPlugin {
58
+ constructor() {
59
+ this.name = 'social';
60
+ this.user = null;
61
+ this.authenticated = false;
62
+ this.online = false;
63
+ this.#plugin = null;
64
+ this.#provider = null;
65
+ }
66
+
67
+ /** @type {object | null} */
68
+ #plugin;
69
+
70
+ /** @type {string | null} */
71
+ #provider;
72
+
73
+ supportsAutoLogin() {
74
+ return true;
75
+ }
76
+
77
+ #autoLoginStorageKey() {
78
+ return configValue('autoLoginStorageKey', DEFAULT_AUTO_LOGIN_KEY);
79
+ }
80
+
81
+ #config() {
82
+ return readSocialLoginConfig();
83
+ }
84
+
85
+ #resolveDefaultProvider() {
86
+ const config = this.#config();
87
+ if (config.defaultProvider) {
88
+ return config.defaultProvider;
89
+ }
90
+ const platform = config.platform || window.gameConfig?.platform || '';
91
+ if (String(platform).startsWith('ios')) {
92
+ return config.providers?.apple === false ? 'google' : 'apple';
93
+ }
94
+ return config.providers?.google === false ? 'apple' : 'google';
95
+ }
96
+
97
+ #buildInitializeOptions() {
98
+ const config = this.#config();
99
+ const options = {};
100
+
101
+ if (config.providers?.google !== false) {
102
+ options.google = {
103
+ webClientId: config.google?.webClientId
104
+ || configValue('googleClientId', ''),
105
+ iOSClientId: config.google?.iOSClientId,
106
+ iOSServerClientId: config.google?.iOSServerClientId
107
+ || config.google?.webClientId,
108
+ mode: config.google?.mode || 'online',
109
+ };
110
+ }
111
+
112
+ if (config.providers?.apple !== false) {
113
+ options.apple = {
114
+ clientId: config.apple?.clientId || '',
115
+ redirectUrl: config.apple?.redirectUrl,
116
+ useProperTokenExchange: config.apple?.useProperTokenExchange !== false,
117
+ useBroadcastChannel: config.apple?.useBroadcastChannel !== false,
118
+ };
119
+ }
120
+
121
+ return options;
122
+ }
123
+
124
+ #persistLogin(user) {
125
+ const storage = getStorage();
126
+ storage?.set(this.#autoLoginStorageKey(), user.id);
127
+ }
128
+
129
+ #applyIdentity(identity) {
130
+ if (!identity?.id) {
131
+ this.user = null;
132
+ this.authenticated = false;
133
+ this.online = false;
134
+ return false;
135
+ }
136
+
137
+ this.user = {
138
+ id: identity.id,
139
+ email: identity.email || '',
140
+ displayName: identity.displayName || '',
141
+ };
142
+ this.authenticated = true;
143
+ this.online = true;
144
+ this.#persistLogin(this.user);
145
+ return true;
146
+ }
147
+
148
+ #complete(callback) {
149
+ callback({
150
+ authenticated: this.authenticated,
151
+ user: this.getUser(),
152
+ });
153
+ }
154
+
155
+ whenReady(done) {
156
+ const started = Date.now();
157
+ const timeoutMs = Number(configValue('socialReadyTimeoutMs', READY_TIMEOUT_MS))
158
+ || READY_TIMEOUT_MS;
159
+
160
+ const finish = async () => {
161
+ const plugin = resolveSocialLoginPlugin();
162
+ if (!plugin) {
163
+ if (Date.now() - started >= timeoutMs) {
164
+ console.warn('[adaptfully social-auth] Capgo SocialLogin plugin not available');
165
+ this.online = false;
166
+ done({ error: 'SocialLogin plugin not available' });
167
+ return;
168
+ }
169
+ window.setTimeout(finish, READY_POLL_MS);
170
+ return;
171
+ }
172
+
173
+ this.#plugin = plugin;
174
+ this.#provider = this.#resolveDefaultProvider();
175
+
176
+ try {
177
+ await plugin.initialize(this.#buildInitializeOptions());
178
+ this.online = true;
179
+ done();
180
+ } catch (err) {
181
+ console.error('[adaptfully social-auth] initialize failed:', err);
182
+ this.online = false;
183
+ done({ error: err?.message || 'SocialLogin initialize failed' });
184
+ }
185
+ };
186
+
187
+ finish();
188
+ }
189
+
190
+ login(callback) {
191
+ const plugin = this.#plugin ?? resolveSocialLoginPlugin();
192
+ const provider = this.#provider ?? this.#resolveDefaultProvider();
193
+
194
+ if (!plugin) {
195
+ console.warn('[adaptfully social-auth] login: plugin missing');
196
+ this.#complete(callback);
197
+ return;
198
+ }
199
+
200
+ plugin.login({
201
+ provider,
202
+ options: {
203
+ scopes: provider === 'google'
204
+ ? ['email', 'profile']
205
+ : ['email', 'name'],
206
+ },
207
+ })
208
+ .then((result) => {
209
+ if (!this.#applyIdentity(mapLoginResult(result))) {
210
+ console.warn('[adaptfully social-auth] login: could not map identity', result);
211
+ }
212
+ this.#complete(callback);
213
+ })
214
+ .catch((err) => {
215
+ console.error('[adaptfully social-auth] login failed:', err);
216
+ this.user = null;
217
+ this.authenticated = false;
218
+ this.online = false;
219
+ this.#complete(callback);
220
+ });
221
+ }
222
+
223
+ autoLogin(callback) {
224
+ const plugin = this.#plugin ?? resolveSocialLoginPlugin();
225
+ const provider = this.#provider ?? this.#resolveDefaultProvider();
226
+ const storage = getStorage();
227
+ const lastId = storage?.get?.(this.#autoLoginStorageKey());
228
+
229
+ if (!plugin || typeof plugin.isLoggedIn !== 'function') {
230
+ if (lastId) {
231
+ this.#applyIdentity({ id: String(lastId), email: '' });
232
+ }
233
+ this.#complete(callback);
234
+ return;
235
+ }
236
+
237
+ plugin.isLoggedIn({ provider })
238
+ .then(async (status) => {
239
+ if (!status?.isLoggedIn) {
240
+ this.user = null;
241
+ this.authenticated = false;
242
+ this.#complete(callback);
243
+ return;
244
+ }
245
+
246
+ if (typeof plugin.getAuthorizationCode === 'function') {
247
+ try {
248
+ const auth = await plugin.getAuthorizationCode({ provider });
249
+ const identity = mapLoginResult(auth) || (lastId
250
+ ? { id: String(lastId), email: '' }
251
+ : null);
252
+ this.#applyIdentity(identity);
253
+ this.#complete(callback);
254
+ return;
255
+ } catch {
256
+ // fall through to lastId
257
+ }
258
+ }
259
+
260
+ if (lastId) {
261
+ this.#applyIdentity({ id: String(lastId), email: '' });
262
+ }
263
+ this.#complete(callback);
264
+ })
265
+ .catch((err) => {
266
+ console.warn('[adaptfully social-auth] autoLogin failed:', err);
267
+ this.#complete(callback);
268
+ });
269
+ }
270
+
271
+ logout(callback) {
272
+ const plugin = this.#plugin ?? resolveSocialLoginPlugin();
273
+ const provider = this.#provider ?? this.#resolveDefaultProvider();
274
+ const storage = getStorage();
275
+
276
+ const clear = () => {
277
+ this.user = null;
278
+ this.authenticated = false;
279
+ this.online = false;
280
+ storage?.remove(this.#autoLoginStorageKey());
281
+ callback();
282
+ };
283
+
284
+ if (!plugin || typeof plugin.logout !== 'function') {
285
+ clear();
286
+ return;
287
+ }
288
+
289
+ plugin.logout({ provider })
290
+ .then(clear)
291
+ .catch((err) => {
292
+ console.warn('[adaptfully social-auth] logout failed:', err);
293
+ clear();
294
+ });
295
+ }
296
+
297
+ getUser() {
298
+ if (!this.authenticated || !this.user) {
299
+ return null;
300
+ }
301
+ return {
302
+ id: this.user.id,
303
+ email: this.user.email || '',
304
+ };
305
+ }
306
+
307
+ isAuthenticated() {
308
+ return !!this.authenticated;
309
+ }
310
+ }
311
+
312
+ ns.auth.Social = () => new SocialAuthPlugin();
313
+ }(window.adaptfully));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/adaptfully",
3
- "version": "3.9.0",
3
+ "version": "3.10.0",
4
4
  "description": "Platform abstraction and Wrapfully deploy client for Makefully games",
5
5
  "type": "module",
6
6
  "main": "./lib/node/index.js",