@makefully/adaptfully 3.0.2 → 3.0.3

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,23 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 3.0.3 — 2026-06-18
6
+
7
+ ### Added
8
+
9
+ - `platform.autoLogin()` and `platform.supportsAutoLogin()` for session restore without UI.
10
+ - Google auth reads `config.autoLoginStorageKey` (default: `lastLoggedIn`) when deciding whether to attempt silent OAuth.
11
+
12
+ ### Changed
13
+
14
+ - Auth plugins expose `autoLogin()` only (removed `silentLogin()`).
15
+
16
+ ### Fixed
17
+
18
+ - Google auth reads OAuth config and stored tokens lazily so bridge-registered `config`/`storage` are applied before auto-login runs.
19
+ - Prebuild injects deploy bridge scripts after `core.js` and before auth plugin runtime scripts.
20
+ - Auth storage helper falls back to `localStorage` when `storage` is not registered via Adaptfully.
21
+
5
22
  ## 3.0.2 — 2026-06-18
6
23
 
7
24
  ### Fixed
package/README.md CHANGED
@@ -107,6 +107,8 @@ import {
107
107
  - **`prebuildPlatform(deployFolder, platformKey, pkg)`** — copy `deploy/` to `output/<platform>-prebuild/` and inject registrations into `config.htmlInjections` (default: `index.html`).
108
108
  - **`resolveRegistrationAssets(registrations)`** — resolve runtime script paths, inline registration JS, and external script tags for a registration map (useful for Vite dev servers).
109
109
  - **`runAdaptfullyStage('prebuild' | 'build' | 'deploy', platformKey, options)`** — run a pipeline stage programmatically.
110
+ - **`platform.autoLogin(callback)`** — restore a prior session without UI when the auth plugin supports it (Google uses `lastLoggedIn` in storage and a cached OAuth token).
111
+ - **`platform.supportsAutoLogin()`** — whether the active auth plugin can attempt automatic sign-in.
110
112
 
111
113
  ---
112
114
 
@@ -201,7 +201,11 @@ export function buildAdaptfullyInjection(registrations, options = {}) {
201
201
  block += `${extScript}\n`;
202
202
  }
203
203
 
204
- for (const script of parts.runtimeScripts) {
204
+ const coreScript = 'core.js';
205
+ const bootstrapScripts = parts.runtimeScripts.filter((script) => script === coreScript);
206
+ const pluginScripts = parts.runtimeScripts.filter((script) => script !== coreScript);
207
+
208
+ for (const script of bootstrapScripts) {
205
209
  block += `<script>\n${readRuntimeScript(script)}\n</script>\n`;
206
210
  }
207
211
 
@@ -209,6 +213,10 @@ export function buildAdaptfullyInjection(registrations, options = {}) {
209
213
  block += `<script src="${src}"></script>\n`;
210
214
  }
211
215
 
216
+ for (const script of pluginScripts) {
217
+ block += `<script>\n${readRuntimeScript(script)}\n</script>\n`;
218
+ }
219
+
212
220
  if (parts.inlineRegistrations.length > 0) {
213
221
  block += `<script>\n${parts.inlineRegistrations.join('\n')}\n</script>\n`;
214
222
  }
@@ -1,4 +1,4 @@
1
- /* global window */
1
+ /* global localStorage, window */
2
2
 
3
3
  /**
4
4
  * @typedef {{ id: string, email: string }} AuthUser
@@ -9,13 +9,35 @@
9
9
  * @property {string} name
10
10
  * @property {(done: ReadyCallback) => void} whenReady
11
11
  * @property {(callback: AuthCallback) => void} login
12
- * @property {(callback: AuthCallback) => void} silentLogin
12
+ * @property {(callback: AuthCallback) => void} autoLogin
13
+ * @property {() => boolean} [supportsAutoLogin]
13
14
  * @property {(callback: () => void) => void} logout
14
15
  * @property {() => AuthUser | null} getUser
15
16
  * @property {() => boolean} isAuthenticated
16
17
  */
17
18
 
18
19
  (function registerAuthHelpers(ns) {
20
+ /** @type {ReturnType<typeof createDefaultStorage> | undefined} */
21
+ let defaultStorage;
22
+
23
+ function createDefaultStorage() {
24
+ if (typeof localStorage !== 'undefined') {
25
+ return {
26
+ get(key) {
27
+ const value = localStorage.getItem(key);
28
+ return value === null ? undefined : value;
29
+ },
30
+ set(key, value) {
31
+ localStorage.setItem(key, value == null ? '' : String(value));
32
+ },
33
+ remove(key) {
34
+ localStorage.removeItem(key);
35
+ },
36
+ };
37
+ }
38
+ return null;
39
+ }
40
+
19
41
  const helpers = {
20
42
  configValue(key, fallback) {
21
43
  if (!ns.has('config')) {
@@ -29,7 +51,13 @@
29
51
  },
30
52
 
31
53
  getStorage() {
32
- return ns.has('storage') ? ns.get('storage') : null;
54
+ if (ns.has('storage')) {
55
+ return ns.get('storage');
56
+ }
57
+ if (defaultStorage === undefined) {
58
+ defaultStorage = createDefaultStorage();
59
+ }
60
+ return defaultStorage;
33
61
  },
34
62
  };
35
63
 
@@ -50,10 +50,14 @@
50
50
  });
51
51
  }
52
52
 
53
- silentLogin(callback) {
53
+ autoLogin(callback) {
54
54
  callback({ authenticated: this.authenticated, user: this.getUser() });
55
55
  }
56
56
 
57
+ supportsAutoLogin() {
58
+ return true;
59
+ }
60
+
57
61
  logout(callback) {
58
62
  const storage = getStorage();
59
63
  this.authenticated = false;
@@ -6,24 +6,42 @@
6
6
  const DEFAULT_CLIENT_ID = '225754014403.apps.googleusercontent.com';
7
7
  const DEFAULT_SCOPES = 'openid email profile';
8
8
  const DEFAULT_TOKEN_KEY = 'adaptfully_google_token';
9
+ const DEFAULT_AUTO_LOGIN_KEY = 'lastLoggedIn';
9
10
 
10
11
  class GoogleAuthPlugin {
11
12
  constructor() {
12
13
  this.name = 'google';
13
14
  this.tokenClient = null;
14
- this.tokenKey = configValue('googleTokenKey', DEFAULT_TOKEN_KEY);
15
- this.clientId = configValue('googleClientId', DEFAULT_CLIENT_ID);
16
- this.scopes = configValue('googleScopes', DEFAULT_SCOPES);
17
- this.accessToken = sessionStorage.getItem(this.tokenKey) || '';
15
+ this.accessToken = '';
18
16
  this.user = null;
19
17
  this.authenticated = false;
20
18
  }
21
19
 
20
+ supportsAutoLogin() {
21
+ return true;
22
+ }
23
+
24
+ #tokenKey() {
25
+ return configValue('googleTokenKey', DEFAULT_TOKEN_KEY);
26
+ }
27
+
28
+ #clientId() {
29
+ return configValue('googleClientId', DEFAULT_CLIENT_ID);
30
+ }
31
+
32
+ #scopes() {
33
+ return configValue('googleScopes', DEFAULT_SCOPES);
34
+ }
35
+
36
+ #autoLoginStorageKey() {
37
+ return configValue('autoLoginStorageKey', DEFAULT_AUTO_LOGIN_KEY);
38
+ }
39
+
22
40
  whenReady(done) {
23
41
  const setup = () => {
24
42
  this.tokenClient = google.accounts.oauth2.initTokenClient({
25
- client_id: this.clientId,
26
- scope: this.scopes,
43
+ client_id: this.#clientId(),
44
+ scope: this.#scopes(),
27
45
  callback: () => {},
28
46
  });
29
47
  done();
@@ -60,12 +78,19 @@
60
78
  this.accessToken = '';
61
79
  this.user = null;
62
80
  this.authenticated = false;
63
- sessionStorage.removeItem(this.tokenKey);
81
+ sessionStorage.removeItem(this.#tokenKey());
82
+ }
83
+
84
+ #loadStoredToken() {
85
+ if (!this.accessToken) {
86
+ this.accessToken = sessionStorage.getItem(this.#tokenKey()) || '';
87
+ }
64
88
  }
65
89
 
66
90
  #hasPersistedLogin() {
67
91
  const storage = getStorage();
68
- return !!(storage?.get('lastLoggedIn'));
92
+ const key = this.#autoLoginStorageKey();
93
+ return !!(storage?.get(key));
69
94
  }
70
95
 
71
96
  #fetchUserInfo(token, callback) {
@@ -80,7 +105,7 @@
80
105
  })
81
106
  .then((data) => {
82
107
  this.accessToken = token;
83
- sessionStorage.setItem(this.tokenKey, token);
108
+ sessionStorage.setItem(this.#tokenKey(), token);
84
109
  this.#applyUserInfo(data);
85
110
  callback();
86
111
  })
@@ -103,6 +128,8 @@
103
128
  }
104
129
 
105
130
  #restoreSession(callback) {
131
+ this.#loadStoredToken();
132
+
106
133
  const trySilentGoogleLogin = () => {
107
134
  if (this.#hasPersistedLogin()) {
108
135
  this.#requestAccessToken('none', callback);
@@ -135,7 +162,7 @@
135
162
  });
136
163
  }
137
164
 
138
- silentLogin(callback) {
165
+ autoLogin(callback) {
139
166
  if (this.authenticated && this.user?.id) {
140
167
  callback({ authenticated: true, user: this.getUser() });
141
168
  return;
@@ -149,7 +176,7 @@
149
176
  const storage = getStorage();
150
177
  const finish = () => {
151
178
  this.#clearSession();
152
- storage?.remove('lastLoggedIn');
179
+ storage?.remove(this.#autoLoginStorageKey());
153
180
  callback();
154
181
  };
155
182
 
@@ -22,10 +22,14 @@
22
22
  callback({ authenticated: this.authenticated, user: this.getUser() });
23
23
  }
24
24
 
25
- silentLogin(callback) {
25
+ autoLogin(callback) {
26
26
  callback({ authenticated: this.authenticated, user: this.getUser() });
27
27
  }
28
28
 
29
+ supportsAutoLogin() {
30
+ return false;
31
+ }
32
+
29
33
  logout(callback) {
30
34
  const storage = getStorage();
31
35
  this.user = null;
@@ -53,13 +53,17 @@
53
53
  });
54
54
  }
55
55
 
56
- silentLogin(callback) {
56
+ autoLogin(callback) {
57
57
  this.whenReady(() => {
58
58
  if (!this.online) {
59
59
  callback({ authenticated: false });
60
60
  return;
61
61
  }
62
- this.auth.silentLogin((result) => {
62
+ if (typeof this.auth.autoLogin !== 'function') {
63
+ callback({ authenticated: false });
64
+ return;
65
+ }
66
+ this.auth.autoLogin((result) => {
63
67
  callback(result || {
64
68
  authenticated: this.auth.isAuthenticated(),
65
69
  user: this.auth.getUser(),
@@ -68,6 +72,12 @@
68
72
  });
69
73
  }
70
74
 
75
+ supportsAutoLogin() {
76
+ return typeof this.auth.supportsAutoLogin === 'function'
77
+ ? this.auth.supportsAutoLogin()
78
+ : typeof this.auth.autoLogin === 'function';
79
+ }
80
+
71
81
  logout(callback) {
72
82
  this.whenReady(() => {
73
83
  this.auth.logout(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/adaptfully",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "Platform abstraction and Wrapfully deploy client for Makefully games",
5
5
  "type": "module",
6
6
  "main": "./lib/node/index.js",