@makefully/adaptfully 3.15.0 → 3.16.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,24 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 3.16.0 — 2026-09-02
6
+
7
+ ### Added
8
+
9
+ - **Apple-only iOS `social-auth`** — `providers.google: false` + `providers.apple: true` no longer requires `google.webClientId`. On iOS, missing `apple.clientId` defaults to the platform `packageName`; runtime also falls back to `window.gameConfig.packageName`.
10
+ - **Publish gitignore** — ignore `assets/meta/deployments/**/apple/` (`.p12` / `.mobileprovision`).
11
+
12
+ ### Docs
13
+
14
+ - Registrations are owned by prebuild HTML markers (do not double-inline at compile time).
15
+ - Capacitor iOS signing is file-based under `apple/` (not Cordova-era `build.json` ios fields).
16
+
17
+ ## 3.15.1 — 2026-09-01
18
+
19
+ ### Fixed
20
+
21
+ - **Capacitor/Cordova HTML injection** — packager `<meta>` tags (CSP, viewport) are injected into `<head>`; script tags stay in the body `<!-- adaptfully -->` slot. Fixes browsers ignoring CSP delivered outside `<head>`.
22
+
5
23
  ## 3.15.0 — 2026-08-24
6
24
 
7
25
  ### Added
package/README.md CHANGED
@@ -63,7 +63,7 @@ A **platform** (`config.platforms.<key>`) describes *how* to build — registrat
63
63
 
64
64
  > **Back-compat:** projects with no `assets/meta/deployments/` directory keep the legacy behavior — the entire `assets/meta/` tree (including `assets/meta/publish/`) is shipped as `meta/`, and a single set of credentials is read from `meta/publish/`.
65
65
 
66
- Place `<!-- adaptfully -->` / `<!-- /adaptfully -->` markers in your HTML templates where registrations should be injected (typically between split bundle scripts, before `account.js` runs).
66
+ Place `<!-- adaptfully -->` / `<!-- /adaptfully -->` markers in your HTML templates where registrations should be injected (typically between split bundle scripts, before `account.js` runs). **Do not also inline Adaptfully auth scripts at game compile time** — prebuild/release owns the marker block for each platform.
67
67
 
68
68
  ```javascript
69
69
  // Injected into deploy/index.html for the target platform (before game code):
@@ -224,7 +224,11 @@ The renderer `steam-auth` plugin reads the Steam ID from that bridge. When Steam
224
224
 
225
225
  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).
226
226
 
227
- 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:
227
+ 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`).
228
+
229
+ **Apple-only iOS** (`providers.google: false`, `providers.apple: true`) is supported — Google `webClientId` is not required. On iOS, if `apple.clientId` is omitted, Adaptfully defaults it to the platform `packageName` (Capgo uses this as a plugin label on native iOS, not an Apple Services ID). On Android, Apple Sign-In still requires `apple.clientId`. At runtime, an empty Apple `clientId` also falls back to `window.gameConfig.packageName`.
230
+
231
+ Optional config keys:
228
232
 
229
233
  | Key | Default | Purpose |
230
234
  |-----|---------|---------|
@@ -604,29 +608,21 @@ The file looks like:
604
608
 
605
609
  #### Apple (`ios`, `ios-dev`, `ios-sim`, `mac`)
606
610
 
607
- Include `assets/meta/deployments/<deployment>/build.json` with iOS signing settings:
611
+ **Capacitor iOS** signing is file-based under the deployment folder (not the Cordova-style `build.json` `ios.*` identity fields):
608
612
 
609
- ```json
610
- {
611
- "ios": {
612
- "debug": {
613
- "codeSignIdentity": "iPhone Development",
614
- "provisioningProfile": "(your development provisioning profile id)",
615
- "developmentTeam": "(your team id)",
616
- "packageType": "development",
617
- "automaticProvisioning": false
618
- },
619
- "release": {
620
- "codeSignIdentity": "iPhone Distribution",
621
- "provisioningProfile": "(your distribution provisioning profile id)",
622
- "developmentTeam": "(your team id)",
623
- "packageType": "app-store",
624
- "automaticProvisioning": false
625
- }
626
- }
627
- }
613
+ ```
614
+ assets/meta/deployments/<deployment>/
615
+ apple.json
616
+ apple/distribution.p12 # release
617
+ apple/app-store.mobileprovision # release
618
+ apple/development.p12 # ios-dev device
619
+ apple/development.mobileprovision # ios-dev device
628
620
  ```
629
621
 
622
+ The `.p12` passphrase must match `MAC_KEYCHAIN_PASSWORD` on the Mac Wrapfully host. Team ID and profile UUID are read from the `.mobileprovision`. Enable **Sign in with Apple** on the App ID when using Capgo Apple auth.
623
+
624
+ Optional `build.json` may still be present for tooling compatibility; Capacitor does not require its `ios.*` code-sign fields.
625
+
630
626
  To deploy to the App Store, include `assets/meta/deployments/<deployment>/apple.json`. The easiest path is:
631
627
 
632
628
  ```bash
@@ -638,13 +634,14 @@ The file looks like:
638
634
 
639
635
  ```json
640
636
  {
641
- "category": "(your app's category)",
642
- "identity": "(your team identity)",
643
- "username": "(your username)",
644
- "password": "(your password)"
637
+ "category": "public.app-category.games",
638
+ "identity": "(your team id)",
639
+ "username": "(Apple ID email)",
640
+ "password": "(app-specific password)"
645
641
  }
646
642
  ```
647
643
 
644
+ iOS IPA upload uses `username` + `password` via `xcrun altool --upload-app`. `category` / `identity` matter more for macOS Electron signing/notarization.
648
645
  #### Cordova
649
646
 
650
647
  Set `packager: "cordova"` on an `android` or `ios` platform to use Cordova instead of Capacitor. Requires the Android and Apple package requirements above.
@@ -11,6 +11,8 @@ export const VALID_PACKAGERS = ['web', 'electron', 'cordova', 'capacitor'];
11
11
 
12
12
  const PACKAGER_MARKER = '<!-- adaptfully-packager -->';
13
13
  const PACKAGER_END_MARKER = '<!-- /adaptfully-packager -->';
14
+ const HEAD_PACKAGER_MARKER = '<!-- adaptfully-packager-head -->';
15
+ const HEAD_PACKAGER_END_MARKER = '<!-- /adaptfully-packager-head -->';
14
16
 
15
17
  /**
16
18
  * @typedef {Object} PackagerOptions
@@ -58,21 +60,64 @@ function writeGameConfig(dest, platformKey, pkg, log) {
58
60
 
59
61
  /**
60
62
  * @param {string} html
63
+ * @param {string} marker
64
+ * @param {string} endMarker
61
65
  * @param {string} injection
62
66
  */
63
- function injectPackagerExtras(html, injection) {
64
- if (!injection) {
65
- return html;
66
- }
67
-
67
+ function replaceMarkedBlock(html, marker, endMarker, injection) {
68
68
  const markerPattern = new RegExp(
69
- `${escapeRegExp(PACKAGER_MARKER)}[\\s\\S]*?${escapeRegExp(PACKAGER_END_MARKER)}\\n?`,
69
+ `${escapeRegExp(marker)}[\\s\\S]*?${escapeRegExp(endMarker)}\\n?`,
70
70
  );
71
71
 
72
72
  if (markerPattern.test(html)) {
73
73
  return html.replace(markerPattern, injection);
74
74
  }
75
75
 
76
+ if (html.includes(marker)) {
77
+ return html.replace(marker, `${injection}${marker}`);
78
+ }
79
+
80
+ return html;
81
+ }
82
+
83
+ /**
84
+ * @param {string} html
85
+ * @param {string} injection
86
+ */
87
+ function injectHeadPackagerExtras(html, injection) {
88
+ if (!injection) {
89
+ return html;
90
+ }
91
+
92
+ let updated = replaceMarkedBlock(html, HEAD_PACKAGER_MARKER, HEAD_PACKAGER_END_MARKER, injection);
93
+
94
+ if (updated === html && html.includes('</head>')) {
95
+ updated = html.replace('</head>', `${injection}</head>`);
96
+ } else if (updated === html) {
97
+ throw new Error(
98
+ 'Cannot inject packager head extras: HTML needs '
99
+ + `${HEAD_PACKAGER_MARKER}…${HEAD_PACKAGER_END_MARKER} or </head>`,
100
+ );
101
+ }
102
+
103
+ return updated;
104
+ }
105
+
106
+ /**
107
+ * @param {string} html
108
+ * @param {string} injection
109
+ */
110
+ function injectBodyPackagerExtras(html, injection) {
111
+ if (!injection) {
112
+ return html;
113
+ }
114
+
115
+ let updated = replaceMarkedBlock(html, PACKAGER_MARKER, PACKAGER_END_MARKER, injection);
116
+
117
+ if (updated !== html) {
118
+ return updated;
119
+ }
120
+
76
121
  if (html.includes(PACKAGER_MARKER)) {
77
122
  return html.replace(PACKAGER_MARKER, `${injection}${PACKAGER_MARKER}`);
78
123
  }
@@ -85,17 +130,36 @@ function injectPackagerExtras(html, injection) {
85
130
  return html.replace('<!-- scripts -->', `${injection}<!-- scripts -->`);
86
131
  }
87
132
 
88
- if (html.includes('</head>')) {
89
- return html.replace('</head>', `${injection}</head>`);
133
+ if (html.includes('</body>')) {
134
+ return html.replace('</body>', `${injection}</body>`);
90
135
  }
91
136
 
92
137
  throw new Error(
93
- 'Cannot inject packager extras: HTML needs '
138
+ 'Cannot inject packager body extras: HTML needs '
94
139
  + '<!-- adaptfully-packager -->…<!-- /adaptfully-packager -->, '
95
- + '<!-- adaptfully -->, <!-- scripts -->, or </head>',
140
+ + '<!-- adaptfully -->, <!-- scripts -->, or </body>',
96
141
  );
97
142
  }
98
143
 
144
+ /**
145
+ * @param {string} html
146
+ * @param {{ head?: string, body?: string }} injection
147
+ */
148
+ function injectPackagerExtras(html, injection) {
149
+ if (!injection?.head && !injection?.body) {
150
+ return html;
151
+ }
152
+
153
+ let updated = html;
154
+ if (injection.head) {
155
+ updated = injectHeadPackagerExtras(updated, injection.head);
156
+ }
157
+ if (injection.body) {
158
+ updated = injectBodyPackagerExtras(updated, injection.body);
159
+ }
160
+ return updated;
161
+ }
162
+
99
163
  function escapeRegExp(value) {
100
164
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
101
165
  }
@@ -243,22 +307,28 @@ export class Packager {
243
307
  /**
244
308
  * @param {string[]} headExtras
245
309
  * @param {string[]} bodyScripts
246
- * @returns {string}
310
+ * @returns {{ head: string, body: string }}
247
311
  */
248
312
  formatHtmlInjection(headExtras, bodyScripts) {
249
- if (headExtras.length === 0 && bodyScripts.length === 0) {
250
- return '';
313
+ let head = '';
314
+ if (headExtras.length > 0) {
315
+ head = `${HEAD_PACKAGER_MARKER}\n`;
316
+ for (const extra of headExtras) {
317
+ head += `${extra}\n`;
318
+ }
319
+ head += `${HEAD_PACKAGER_END_MARKER}\n`;
251
320
  }
252
321
 
253
- let block = `${PACKAGER_MARKER}\n`;
254
- for (const extra of headExtras) {
255
- block += `${extra}\n`;
256
- }
257
- for (const script of bodyScripts) {
258
- block += `${script}\n`;
322
+ let body = '';
323
+ if (bodyScripts.length > 0) {
324
+ body = `${PACKAGER_MARKER}\n`;
325
+ for (const script of bodyScripts) {
326
+ body += `${script}\n`;
327
+ }
328
+ body += `${PACKAGER_END_MARKER}\n`;
259
329
  }
260
- block += `${PACKAGER_END_MARKER}\n`;
261
- return block;
330
+
331
+ return { head, body };
262
332
  }
263
333
 
264
334
  /** @param {string} dest */
@@ -268,7 +338,7 @@ export class Packager {
268
338
  }
269
339
  }
270
340
 
271
- /** @returns {string} */
341
+ /** @returns {{ head: string, body: string }} */
272
342
  buildHtmlInjection() {
273
343
  const bodyScripts = [];
274
344
  if (this.needsGameConfig()) {
@@ -283,7 +353,7 @@ export class Packager {
283
353
  */
284
354
  applyHtmlExtras(dest, htmlPaths) {
285
355
  const injection = this.buildHtmlInjection();
286
- if (!injection) {
356
+ if (!injection.head && !injection.body) {
287
357
  return;
288
358
  }
289
359
 
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { Packager } from './base.js';
4
+ import { resolvePlatformConfig } from '../platform-config.js';
4
5
 
5
6
  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
 
@@ -17,24 +18,40 @@ export function resolvePlatformSocialLogin(platformKey, pkg) {
17
18
  return pkg.config?.platforms?.[platformKey]?.socialLogin;
18
19
  }
19
20
 
21
+ /**
22
+ * @param {string} platformKey
23
+ */
24
+ function isIosPlatformKey(platformKey) {
25
+ return platformKey === 'ios'
26
+ || platformKey === 'ios-dev'
27
+ || platformKey === 'ios-sim'
28
+ || String(platformKey).startsWith('ios');
29
+ }
30
+
20
31
  /**
21
32
  * @param {object} socialLogin
22
33
  * @param {string} platformKey
34
+ * @param {{ packageName?: string }} [options]
23
35
  */
24
- function normalizeSocialLoginConfig(socialLogin = {}, platformKey) {
36
+ export function normalizeSocialLoginConfig(socialLogin = {}, platformKey, options = {}) {
25
37
  const providers = socialLogin.providers ?? { google: true, apple: true };
26
38
  const googleEnabled = providers.google !== false;
27
39
  const appleEnabled = providers.apple !== false;
28
40
 
29
41
  let defaultProvider = socialLogin.defaultProvider;
30
42
  if (!defaultProvider) {
31
- if (platformKey === 'ios' || platformKey === 'ios-dev' || platformKey === 'ios-sim') {
43
+ if (isIosPlatformKey(platformKey)) {
32
44
  defaultProvider = appleEnabled ? 'apple' : 'google';
33
45
  } else {
34
46
  defaultProvider = googleEnabled ? 'google' : 'apple';
35
47
  }
36
48
  }
37
49
 
50
+ const apple = { ...(socialLogin.apple ?? {}) };
51
+ if (appleEnabled && !apple.clientId && options.packageName && isIosPlatformKey(platformKey)) {
52
+ apple.clientId = options.packageName;
53
+ }
54
+
38
55
  return {
39
56
  providers: {
40
57
  google: googleEnabled,
@@ -43,7 +60,7 @@ function normalizeSocialLoginConfig(socialLogin = {}, platformKey) {
43
60
  twitter: providers.twitter === true,
44
61
  },
45
62
  google: socialLogin.google ?? {},
46
- apple: socialLogin.apple ?? {},
63
+ apple,
47
64
  defaultProvider,
48
65
  platform: platformKey,
49
66
  };
@@ -52,7 +69,7 @@ function normalizeSocialLoginConfig(socialLogin = {}, platformKey) {
52
69
  /**
53
70
  * @param {object} config
54
71
  */
55
- function validateSocialLoginConfig(config) {
72
+ export function validateSocialLoginConfig(config) {
56
73
  const { providers, google, apple } = config;
57
74
 
58
75
  if (!providers.google && !providers.apple) {
@@ -83,6 +100,10 @@ export class CapacitorPackager extends Packager {
83
100
  /** @type {string[]} */
84
101
  static defaultPlatforms = ['ios', 'android'];
85
102
 
103
+ #socialLoginPackageName() {
104
+ return resolvePlatformConfig(this.pkg, this.platformKey).packageName;
105
+ }
106
+
86
107
  validate() {
87
108
  super.validate();
88
109
 
@@ -98,6 +119,7 @@ export class CapacitorPackager extends Packager {
98
119
  const config = normalizeSocialLoginConfig(
99
120
  socialLogin ?? this.pkg.config?.socialLogin,
100
121
  this.platformKey ?? 'android',
122
+ { packageName: this.#socialLoginPackageName() },
101
123
  );
102
124
  validateSocialLoginConfig(config);
103
125
  }
@@ -113,7 +135,9 @@ export class CapacitorPackager extends Packager {
113
135
  const socialLogin = resolvePlatformSocialLogin(this.platformKey, this.pkg)
114
136
  ?? this.pkg.config?.socialLogin
115
137
  ?? {};
116
- const config = normalizeSocialLoginConfig(socialLogin, this.platformKey);
138
+ const config = normalizeSocialLoginConfig(socialLogin, this.platformKey, {
139
+ packageName: this.#socialLoginPackageName(),
140
+ });
117
141
  const content = `window.__ADAPTFULLY_SOCIAL_LOGIN__ = ${JSON.stringify(config, null, 4)};\n`;
118
142
  fs.writeFileSync(path.join(dest, 'social-login-config.js'), content);
119
143
  this.log('adaptfully: write social-login-config.js (social-auth)');
@@ -4,7 +4,12 @@ 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 { CapacitorPackager, resolvePlatformSocialLogin } from './capacitor.js';
7
+ export {
8
+ CapacitorPackager,
9
+ normalizeSocialLoginConfig,
10
+ resolvePlatformSocialLogin,
11
+ validateSocialLoginConfig,
12
+ } from './capacitor.js';
8
13
  export { CordovaPackager } from './cordova.js';
9
14
  export {
10
15
  ElectronPackager,
@@ -12,6 +12,7 @@ export const PUBLISH_CREDENTIAL_GITIGNORE_PATTERNS = [
12
12
  'assets/meta/deployments/**/apple.json',
13
13
  'assets/meta/deployments/**/ms.json',
14
14
  'assets/meta/deployments/**/android/',
15
+ 'assets/meta/deployments/**/apple/',
15
16
  'assets/meta/deployments/**/ms/',
16
17
  ];
17
18
 
@@ -119,7 +119,9 @@
119
119
 
120
120
  if (config.providers?.apple !== false) {
121
121
  options.apple = {
122
- clientId: config.apple?.clientId || '',
122
+ clientId: config.apple?.clientId
123
+ || window.gameConfig?.packageName
124
+ || '',
123
125
  redirectUrl: config.apple?.redirectUrl,
124
126
  useProperTokenExchange: config.apple?.useProperTokenExchange !== false,
125
127
  useBroadcastChannel: config.apple?.useBroadcastChannel !== false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/adaptfully",
3
- "version": "3.15.0",
3
+ "version": "3.16.0",
4
4
  "description": "Platform abstraction and Wrapfully deploy client for Makefully games",
5
5
  "type": "module",
6
6
  "main": "./lib/node/index.js",