@makefully/adaptfully 3.0.1 → 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 +23 -0
- package/README.md +6 -4
- package/lib/node/registrations.js +22 -4
- package/lib/runtime/auth/_helpers.js +31 -3
- package/lib/runtime/auth/dev-auth.js +5 -1
- package/lib/runtime/auth/google-auth.js +38 -11
- package/lib/runtime/auth/steam-auth.js +5 -1
- package/lib/runtime/platform.js +12 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
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
|
+
|
|
22
|
+
## 3.0.2 — 2026-06-18
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Deploy script paths without a leading `/` are emitted as page-relative `script src` values (for subdirectory hosting).
|
|
27
|
+
|
|
5
28
|
## 3.0.1 — 2026-06-17
|
|
6
29
|
|
|
7
30
|
### Added
|
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ Use plugin keys in `config.platforms.<platform>.registrations`. Custom deploy sc
|
|
|
68
68
|
"web": {
|
|
69
69
|
"registrations": {
|
|
70
70
|
"auth": "google-auth",
|
|
71
|
-
"storage": "
|
|
71
|
+
"storage": "javascript/adaptfully-bridge.js"
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
"dev": {
|
|
@@ -81,7 +81,7 @@ Use plugin keys in `config.platforms.<platform>.registrations`. Custom deploy sc
|
|
|
81
81
|
}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Standard plugin keys load bundled Adaptfully runtime scripts and emit an inline `adaptfully.register()` call. Path values add a `<script src="...">` tag — the script is expected to call `adaptfully.register()` itself (for example a bridge that wires `storage` and `config`).
|
|
84
|
+
Standard plugin keys load bundled Adaptfully runtime scripts and emit an inline `adaptfully.register()` call. Path values add a `<script src="...">` tag — the script is expected to call `adaptfully.register()` itself (for example a bridge that wires `storage` and `config`). Use a leading `/` for site-root URLs; omit it for paths relative to each HTML page (required when the game is hosted in a subdirectory).
|
|
85
85
|
|
|
86
86
|
Wrapfully builders (`steam`, `win`, `mac`, `android`, etc.) map to platform keys via defaults (`win` → `steam`) or an explicit `builders` array on the platform config.
|
|
87
87
|
|
|
@@ -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
|
|
|
@@ -284,13 +286,13 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
|
|
|
284
286
|
"web": {
|
|
285
287
|
"registrations": {
|
|
286
288
|
"auth": "google-auth",
|
|
287
|
-
"storage": "
|
|
289
|
+
"storage": "javascript/adaptfully-bridge.js"
|
|
288
290
|
}
|
|
289
291
|
},
|
|
290
292
|
"steam": {
|
|
291
293
|
"registrations": {
|
|
292
294
|
"auth": "steam-auth",
|
|
293
|
-
"storage": "
|
|
295
|
+
"storage": "javascript/adaptfully-bridge.js"
|
|
294
296
|
}
|
|
295
297
|
}
|
|
296
298
|
},
|
|
@@ -83,7 +83,7 @@ export function collectRegistrationParts(registrations) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
if (isDeployPath(value)) {
|
|
86
|
-
const src =
|
|
86
|
+
const src = normalizeDeployScriptSrc(value);
|
|
87
87
|
deployScripts.push({ key: registerKey, src });
|
|
88
88
|
continue;
|
|
89
89
|
}
|
|
@@ -130,6 +130,17 @@ function isDeployPath(value) {
|
|
|
130
130
|
return value.startsWith('/') || value.startsWith('./') || value.includes('/');
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} value Deploy-relative script path from config
|
|
135
|
+
* @returns {string} Value for HTML script src (leading `/` = site root; otherwise page-relative)
|
|
136
|
+
*/
|
|
137
|
+
function normalizeDeployScriptSrc(value) {
|
|
138
|
+
if (value.startsWith('/')) {
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
return value.replace(/^\.\//, '');
|
|
142
|
+
}
|
|
143
|
+
|
|
133
144
|
/**
|
|
134
145
|
* @param {string} builder
|
|
135
146
|
* @param {Record<string, { builders?: string[], registrations?: RegistrationMap }>} [platforms]
|
|
@@ -178,8 +189,7 @@ export function buildAdaptfullyInjection(registrations, options = {}) {
|
|
|
178
189
|
|
|
179
190
|
for (const [registerKey, value] of Object.entries(registrations)) {
|
|
180
191
|
if (isDeployPath(value)) {
|
|
181
|
-
|
|
182
|
-
log(`adaptfully: registering ${registerKey} ← ${src}`);
|
|
192
|
+
log(`adaptfully: registering ${registerKey} ← ${normalizeDeployScriptSrc(value)}`);
|
|
183
193
|
} else {
|
|
184
194
|
log(`adaptfully: registering ${registerKey} ← ${value}`);
|
|
185
195
|
}
|
|
@@ -191,7 +201,11 @@ export function buildAdaptfullyInjection(registrations, options = {}) {
|
|
|
191
201
|
block += `${extScript}\n`;
|
|
192
202
|
}
|
|
193
203
|
|
|
194
|
-
|
|
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) {
|
|
195
209
|
block += `<script>\n${readRuntimeScript(script)}\n</script>\n`;
|
|
196
210
|
}
|
|
197
211
|
|
|
@@ -199,6 +213,10 @@ export function buildAdaptfullyInjection(registrations, options = {}) {
|
|
|
199
213
|
block += `<script src="${src}"></script>\n`;
|
|
200
214
|
}
|
|
201
215
|
|
|
216
|
+
for (const script of pluginScripts) {
|
|
217
|
+
block += `<script>\n${readRuntimeScript(script)}\n</script>\n`;
|
|
218
|
+
}
|
|
219
|
+
|
|
202
220
|
if (parts.inlineRegistrations.length > 0) {
|
|
203
221
|
block += `<script>\n${parts.inlineRegistrations.join('\n')}\n</script>\n`;
|
|
204
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}
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
26
|
-
scope: this
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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;
|
package/lib/runtime/platform.js
CHANGED
|
@@ -53,13 +53,17 @@
|
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
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.
|
|
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(() => {
|