@makefully/adaptfully 3.1.0 → 3.2.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 +16 -0
- package/README.md +37 -1
- package/lib/node/index.js +10 -1
- package/lib/node/packagers.js +157 -0
- package/lib/node/paths.js +5 -0
- package/lib/node/prebuild.js +3 -0
- package/lib/runtime/auth/steam-auth.js +206 -12
- package/lib/templates/electron/main.js +104 -0
- package/lib/templates/electron/preload.js +14 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 3.2.0 — 2026-06-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`config.platforms.<platform>.packager`** — `web` (default), `electron`, `cordova`, or `capacitor`. Controls which template files are added during prebuild.
|
|
10
|
+
- **`lib/templates/`** — packager-specific templates (starting with `electron/main.js` and `electron/preload.js`).
|
|
11
|
+
- Electron prebuild writes **`main.js`** automatically. When **`steam-auth`** is registered, Adaptfully also writes **`preload.js`** with steamworks.js init and overlay setup in `main.js`.
|
|
12
|
+
- **`steam-auth` requires `packager: "electron"`** — prebuild throws if steam-auth is used on a non-electron platform or without `config.steamId`.
|
|
13
|
+
|
|
14
|
+
## 3.1.1 — 2026-06-16
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **steam-auth** integrates with [steamworks.js](https://github.com/ceifa/steamworks.js): reads Steam ID and persona name from shell-provided bridges (`__ADAPTFULLY_STEAMWORKS__`, `steamworks`, `electronAPI`).
|
|
19
|
+
- Steam auth supports `autoLogin()` and `supportsAutoLogin()` when the Steam client is available.
|
|
20
|
+
|
|
5
21
|
## 3.1.0 — 2026-06-18
|
|
6
22
|
|
|
7
23
|
### Added
|
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ storage.getObject('currentGame');
|
|
|
65
65
|
| Plugin key | Registration | Runtime |
|
|
66
66
|
|------------|--------------|---------|
|
|
67
67
|
| `google-auth` | `adaptfully.register('auth', adaptfully.auth.Google)` | Web, Android, iOS |
|
|
68
|
-
| `steam-auth` | `adaptfully.register('auth', adaptfully.auth.Steam)` | Steam / Electron |
|
|
68
|
+
| `steam-auth` | `adaptfully.register('auth', adaptfully.auth.Steam)` | Steam / Electron (via [steamworks.js](https://github.com/ceifa/steamworks.js)) |
|
|
69
69
|
| `dev-auth` | `adaptfully.register('auth', adaptfully.auth.Dev)` | Local dev (test user) |
|
|
70
70
|
|
|
71
71
|
Use plugin keys in `config.platforms.<platform>.registrations`. Custom deploy scripts use a path relative to the deploy folder instead:
|
|
@@ -101,6 +101,40 @@ Standard plugin keys load bundled Adaptfully runtime scripts and emit an inline
|
|
|
101
101
|
|
|
102
102
|
Wrapfully builders (`steam`, `win`, `mac`, `android`, etc.) map to platform keys via defaults (`win` → `steam`) or an explicit `builders` array on the platform config.
|
|
103
103
|
|
|
104
|
+
Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or eventually `capacitor`). Defaults to **`web`** — copy deploy and inject HTML only. **`electron`** adds `main.js` from `lib/templates/electron/` during prebuild. **`steam-auth` requires `packager: "electron"`** and `config.steamId`.
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"config": {
|
|
109
|
+
"steamId": 719140,
|
|
110
|
+
"platforms": {
|
|
111
|
+
"web": {
|
|
112
|
+
"packager": "web",
|
|
113
|
+
"registrations": { "auth": "google-auth" }
|
|
114
|
+
},
|
|
115
|
+
"steam": {
|
|
116
|
+
"packager": "electron",
|
|
117
|
+
"registrations": { "auth": "steam-auth" }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### Steam auth (`steam-auth`)
|
|
125
|
+
|
|
126
|
+
When `steam-auth` is registered on an **`electron`** platform, Adaptfully prebuild writes:
|
|
127
|
+
|
|
128
|
+
- **`main.js`** — Electron shell with Steam overlay enabled and a preload script wired in
|
|
129
|
+
- **`preload.js`** — initializes [steamworks.js](https://github.com/ceifa/steamworks.js) with `config.steamId` and exposes `window.__ADAPTFULLY_STEAMWORKS__`
|
|
130
|
+
|
|
131
|
+
The renderer `steam-auth` plugin reads the Steam ID from that bridge. When Steam is available, `autoLogin()` succeeds immediately with `{ id: steamId64, email: '' }`. Optional config keys:
|
|
132
|
+
|
|
133
|
+
| Key | Default | Purpose |
|
|
134
|
+
|-----|---------|---------|
|
|
135
|
+
| `autoLoginStorageKey` | `lastLoggedIn` | Storage key written with the Steam ID on login |
|
|
136
|
+
| `steamReadyTimeoutMs` | `10000` | Max wait when a bridge exists but identity is not yet ready |
|
|
137
|
+
|
|
104
138
|
### Node API
|
|
105
139
|
|
|
106
140
|
```javascript
|
|
@@ -306,6 +340,7 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
|
|
|
306
340
|
}
|
|
307
341
|
},
|
|
308
342
|
"steam": {
|
|
343
|
+
"packager": "electron",
|
|
309
344
|
"registrations": {
|
|
310
345
|
"auth": "steam-auth",
|
|
311
346
|
"storage": "javascript/adaptfully-bridge.js"
|
|
@@ -338,6 +373,7 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
|
|
|
338
373
|
| `platforms` | Prebuild | Per-platform registration maps (see [Adaptfully runtime](#adaptfully-runtime)) |
|
|
339
374
|
| `platforms.<name>.builder` | Build/deploy | Override Wrapfully builder for a platform (default: `web` → `webapp`, others match platform key) |
|
|
340
375
|
| `platforms.<name>.builders` | wrapfully-deploy | Map additional Wrapfully builder names to a platform |
|
|
376
|
+
| `platforms.<name>.packager` | Prebuild | `web` (default), `electron`, `cordova`, or `capacitor` — controls template files added during prebuild |
|
|
341
377
|
| `properties` | Cordova | Cordova config.xml entries (plugins, allow-navigation, etc.) |
|
|
342
378
|
|
|
343
379
|
### `wrapfully.json`
|
package/lib/node/index.js
CHANGED
|
@@ -3,7 +3,16 @@ export { loadProjectConfig, resolveServerUrl } from './config.js';
|
|
|
3
3
|
export { send } from './deploy.js';
|
|
4
4
|
export { adaptfullyFromCli, runAdaptfullyStage } from './pipeline.js';
|
|
5
5
|
export { prebuildPlatform, prebuildOutputDir, resolveHtmlInjections } from './prebuild.js';
|
|
6
|
-
export { getPackageRoot, getRuntimeDir, resolveRuntimeScript } from './paths.js';
|
|
6
|
+
export { getPackageRoot, getRuntimeDir, getTemplatesDir, resolveRuntimeScript } from './paths.js';
|
|
7
|
+
export {
|
|
8
|
+
VALID_PACKAGERS,
|
|
9
|
+
applyPackagerTemplates,
|
|
10
|
+
applyTemplateMarker,
|
|
11
|
+
resolvePlatformPackager,
|
|
12
|
+
resolvePackagerTemplateDir,
|
|
13
|
+
usesSteamAuth,
|
|
14
|
+
validatePlatformPackager,
|
|
15
|
+
} from './packagers.js';
|
|
7
16
|
export {
|
|
8
17
|
STANDARD_PLUGINS,
|
|
9
18
|
DEFAULT_BUILDER_PLATFORMS,
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getTemplatesDir } from './paths.js';
|
|
4
|
+
|
|
5
|
+
/** @typedef {'web' | 'electron' | 'cordova' | 'capacitor'} PackagerName */
|
|
6
|
+
|
|
7
|
+
/** @type {PackagerName[]} */
|
|
8
|
+
export const VALID_PACKAGERS = ['web', 'electron', 'cordova', 'capacitor'];
|
|
9
|
+
|
|
10
|
+
const STEAM_INIT = `const path = require('path');
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
require('steamworks.js').electronEnableSteamOverlay();
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.error('Steam overlay unavailable:', err);
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const STEAM_PRELOAD = `,
|
|
20
|
+
preload: path.join(__dirname, 'preload.js')`;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} platformKey
|
|
24
|
+
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string> }>, steamId?: number } }} pkg
|
|
25
|
+
* @returns {PackagerName}
|
|
26
|
+
*/
|
|
27
|
+
export function resolvePlatformPackager(platformKey, pkg) {
|
|
28
|
+
const platform = pkg.config?.platforms?.[platformKey];
|
|
29
|
+
const packager = platform?.packager ?? 'web';
|
|
30
|
+
return /** @type {PackagerName} */ (packager);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Record<string, string>} registrations
|
|
35
|
+
*/
|
|
36
|
+
export function usesSteamAuth(registrations) {
|
|
37
|
+
return Object.values(registrations).includes('steam-auth');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} platformKey
|
|
42
|
+
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string> }>, steamId?: number } }} pkg
|
|
43
|
+
*/
|
|
44
|
+
export function validatePlatformPackager(platformKey, pkg) {
|
|
45
|
+
const platform = pkg.config?.platforms?.[platformKey];
|
|
46
|
+
const packager = resolvePlatformPackager(platformKey, pkg);
|
|
47
|
+
|
|
48
|
+
if (!VALID_PACKAGERS.includes(packager)) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Invalid packager "${packager}" for platform "${platformKey}". `
|
|
51
|
+
+ `Expected one of: ${VALID_PACKAGERS.join(', ')}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const registrations = platform?.registrations ?? {};
|
|
56
|
+
if (usesSteamAuth(registrations) && packager !== 'electron') {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Platform "${platformKey}" uses steam-auth but packager is "${packager}". `
|
|
59
|
+
+ 'Set config.platforms.' + platformKey + '.packager to "electron".',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (usesSteamAuth(registrations) && !pkg.config?.steamId) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Platform "${platformKey}" uses steam-auth but config.steamId is not set.`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} content
|
|
72
|
+
* @param {string} markerName
|
|
73
|
+
* @param {string} replacement
|
|
74
|
+
*/
|
|
75
|
+
export function applyTemplateMarker(content, markerName, replacement) {
|
|
76
|
+
const pattern = new RegExp(
|
|
77
|
+
`/\\* adaptfully-${markerName} \\*/[\\s\\S]*?/\\* /adaptfully-${markerName} \\*/`,
|
|
78
|
+
'g',
|
|
79
|
+
);
|
|
80
|
+
return content.replace(pattern, replacement);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {PackagerName} packager
|
|
85
|
+
*/
|
|
86
|
+
export function resolvePackagerTemplateDir(packager) {
|
|
87
|
+
return path.join(getTemplatesDir(), packager);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {string} dest Prebuild output directory
|
|
92
|
+
* @param {string} platformKey
|
|
93
|
+
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string> }>, steamId?: number } }} pkg
|
|
94
|
+
* @param {{ log?: (message: string) => void }} [options]
|
|
95
|
+
*/
|
|
96
|
+
export function applyPackagerTemplates(dest, platformKey, pkg, options = {}) {
|
|
97
|
+
const log = options.log ?? (() => {});
|
|
98
|
+
const packager = resolvePlatformPackager(platformKey, pkg);
|
|
99
|
+
|
|
100
|
+
validatePlatformPackager(platformKey, pkg);
|
|
101
|
+
|
|
102
|
+
if (packager === 'web') {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const templateDir = resolvePackagerTemplateDir(packager);
|
|
107
|
+
if (!fs.existsSync(templateDir)) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Packager "${packager}" has no templates yet (missing ${templateDir}).`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (packager === 'electron') {
|
|
114
|
+
applyElectronTemplates(dest, platformKey, pkg, templateDir, log);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} dest
|
|
120
|
+
* @param {string} platformKey
|
|
121
|
+
* @param {{ config?: { platforms?: Record<string, { registrations?: Record<string, string> }>, steamId?: number } }} pkg
|
|
122
|
+
* @param {string} templateDir
|
|
123
|
+
* @param {(message: string) => void} log
|
|
124
|
+
*/
|
|
125
|
+
function applyElectronTemplates(dest, platformKey, pkg, templateDir, log) {
|
|
126
|
+
const registrations = pkg.config?.platforms?.[platformKey]?.registrations ?? {};
|
|
127
|
+
const withSteam = usesSteamAuth(registrations);
|
|
128
|
+
const mainTemplatePath = path.join(templateDir, 'main.js');
|
|
129
|
+
|
|
130
|
+
if (!fs.existsSync(mainTemplatePath)) {
|
|
131
|
+
throw new Error(`Electron packager template missing: ${mainTemplatePath}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let mainContent = fs.readFileSync(mainTemplatePath, 'utf8');
|
|
135
|
+
|
|
136
|
+
if (withSteam) {
|
|
137
|
+
mainContent = applyTemplateMarker(mainContent, 'steam-init', STEAM_INIT);
|
|
138
|
+
mainContent = applyTemplateMarker(mainContent, 'steam-preload', STEAM_PRELOAD);
|
|
139
|
+
|
|
140
|
+
const preloadTemplatePath = path.join(templateDir, 'preload.js');
|
|
141
|
+
if (!fs.existsSync(preloadTemplatePath)) {
|
|
142
|
+
throw new Error(`Electron steam-auth template missing: ${preloadTemplatePath}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const preloadContent = fs.readFileSync(preloadTemplatePath, 'utf8')
|
|
146
|
+
.replace(/\{\{STEAM_APP_ID\}\}/g, String(pkg.config.steamId));
|
|
147
|
+
|
|
148
|
+
fs.writeFileSync(path.join(dest, 'preload.js'), preloadContent);
|
|
149
|
+
log('adaptfully: write preload.js (steam-auth)');
|
|
150
|
+
} else {
|
|
151
|
+
mainContent = applyTemplateMarker(mainContent, 'steam-init', '');
|
|
152
|
+
mainContent = applyTemplateMarker(mainContent, 'steam-preload', '');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
fs.writeFileSync(path.join(dest, 'main.js'), mainContent);
|
|
156
|
+
log('adaptfully: write main.js (electron)');
|
|
157
|
+
}
|
package/lib/node/paths.js
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
5
|
const PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
|
|
6
6
|
const RUNTIME_DIR = path.join(PACKAGE_ROOT, 'lib', 'runtime');
|
|
7
|
+
const TEMPLATES_DIR = path.join(PACKAGE_ROOT, 'lib', 'templates');
|
|
7
8
|
|
|
8
9
|
export function getPackageRoot() {
|
|
9
10
|
return PACKAGE_ROOT;
|
|
@@ -13,6 +14,10 @@ export function getRuntimeDir() {
|
|
|
13
14
|
return RUNTIME_DIR;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
export function getTemplatesDir() {
|
|
18
|
+
return TEMPLATES_DIR;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
export function resolveRuntimeScript(relativePath) {
|
|
17
22
|
return path.join(RUNTIME_DIR, relativePath);
|
|
18
23
|
}
|
package/lib/node/prebuild.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
adaptfullyInjectionForPlatform,
|
|
5
5
|
injectAdaptfullyRegistrations,
|
|
6
6
|
} from './registrations.js';
|
|
7
|
+
import { applyPackagerTemplates } from './packagers.js';
|
|
7
8
|
import { copyRecursiveSync, emptyDirSync } from './fs-utils.js';
|
|
8
9
|
|
|
9
10
|
/** @typedef {'prebuild' | 'build' | 'deploy'} AdaptfullyStage */
|
|
@@ -73,6 +74,8 @@ export function prebuildPlatform(deployFolder, platformKey, pkg, options = {}) {
|
|
|
73
74
|
emptyDirSync(dest);
|
|
74
75
|
copyRecursiveSync(source, dest);
|
|
75
76
|
|
|
77
|
+
applyPackagerTemplates(dest, platformKey, pkg, { log });
|
|
78
|
+
|
|
76
79
|
const injection = adaptfullyInjectionForPlatform(platformKey, pkg, { log });
|
|
77
80
|
if (injection) {
|
|
78
81
|
for (const relativePath of resolveHtmlInjections(pkg)) {
|
|
@@ -1,40 +1,226 @@
|
|
|
1
1
|
/* global window */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Steam auth
|
|
5
|
-
*
|
|
4
|
+
* Steam auth via steamworks.js. Adaptfully prebuild writes Electron main.js and
|
|
5
|
+
* preload.js (when packager is "electron") to expose __ADAPTFULLY_STEAMWORKS__.
|
|
6
|
+
* Fallback bridges: window.steamworks, window.electronAPI, etc.
|
|
6
7
|
*/
|
|
7
8
|
(function registerSteamAuth(ns) {
|
|
8
|
-
const { getStorage } = ns.auth.helpers;
|
|
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 normalizeSteamId(value) {
|
|
16
|
+
if (value == null || value === '') {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === 'bigint') {
|
|
20
|
+
return value.toString();
|
|
21
|
+
}
|
|
22
|
+
return String(value);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function readSyncIdentity(client) {
|
|
26
|
+
if (!client?.localplayer) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const steamId = client.localplayer.getSteamId?.();
|
|
32
|
+
const id = normalizeSteamId(steamId?.steamId64 ?? steamId);
|
|
33
|
+
if (!id) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const name = client.localplayer.getName?.() || '';
|
|
38
|
+
return {
|
|
39
|
+
id,
|
|
40
|
+
email: '',
|
|
41
|
+
displayName: name,
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function readAsyncIdentity(bridge) {
|
|
49
|
+
if (!bridge) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
let steamIdRaw = bridge.getSteamId?.();
|
|
55
|
+
if (steamIdRaw && typeof steamIdRaw.then === 'function') {
|
|
56
|
+
steamIdRaw = await steamIdRaw;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const id = normalizeSteamId(steamIdRaw?.steamId64 ?? steamIdRaw);
|
|
60
|
+
if (!id) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let name = '';
|
|
65
|
+
if (typeof bridge.getSteamName === 'function') {
|
|
66
|
+
name = await bridge.getSteamName();
|
|
67
|
+
} else if (typeof bridge.getName === 'function') {
|
|
68
|
+
const resolved = bridge.getName();
|
|
69
|
+
name = resolved && typeof resolved.then === 'function' ? await resolved : resolved;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
id,
|
|
74
|
+
email: '',
|
|
75
|
+
displayName: name || '',
|
|
76
|
+
};
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveSteamworksClient() {
|
|
83
|
+
if (typeof window.__ADAPTFULLY_STEAMWORKS__ !== 'undefined') {
|
|
84
|
+
return window.__ADAPTFULLY_STEAMWORKS__;
|
|
85
|
+
}
|
|
86
|
+
if (window.steamworks?.localplayer) {
|
|
87
|
+
return window.steamworks;
|
|
88
|
+
}
|
|
89
|
+
if (window.steamworksClient?.localplayer) {
|
|
90
|
+
return window.steamworksClient;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function resolveAsyncBridge() {
|
|
96
|
+
if (window.electronAPI?.getSteamId) {
|
|
97
|
+
return window.electronAPI;
|
|
98
|
+
}
|
|
99
|
+
if (window.adaptfullySteam?.getSteamId) {
|
|
100
|
+
return window.adaptfullySteam;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
9
104
|
|
|
10
105
|
class SteamAuthPlugin {
|
|
11
106
|
constructor() {
|
|
12
107
|
this.name = 'steam';
|
|
13
108
|
this.user = null;
|
|
14
109
|
this.authenticated = false;
|
|
110
|
+
this.online = false;
|
|
111
|
+
this.#client = null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @type {object | null} */
|
|
115
|
+
#client;
|
|
116
|
+
|
|
117
|
+
supportsAutoLogin() {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#autoLoginStorageKey() {
|
|
122
|
+
return configValue('autoLoginStorageKey', DEFAULT_AUTO_LOGIN_KEY);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#persistLogin(user) {
|
|
126
|
+
const storage = getStorage();
|
|
127
|
+
storage?.set(this.#autoLoginStorageKey(), user.id);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#applyIdentity(identity) {
|
|
131
|
+
if (!identity?.id) {
|
|
132
|
+
this.user = null;
|
|
133
|
+
this.authenticated = false;
|
|
134
|
+
this.online = false;
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.user = {
|
|
139
|
+
id: identity.id,
|
|
140
|
+
email: identity.email || '',
|
|
141
|
+
displayName: identity.displayName || '',
|
|
142
|
+
};
|
|
143
|
+
this.authenticated = true;
|
|
144
|
+
this.online = true;
|
|
145
|
+
this.#persistLogin(this.user);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async #resolveIdentity() {
|
|
150
|
+
const client = this.#client ?? resolveSteamworksClient();
|
|
151
|
+
if (client) {
|
|
152
|
+
this.#client = client;
|
|
153
|
+
return readSyncIdentity(client);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return readAsyncIdentity(resolveAsyncBridge());
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#hasSteamBridge() {
|
|
160
|
+
return !!(resolveSteamworksClient() || resolveAsyncBridge());
|
|
15
161
|
}
|
|
16
162
|
|
|
17
163
|
whenReady(done) {
|
|
18
|
-
|
|
164
|
+
if (!this.#hasSteamBridge()) {
|
|
165
|
+
this.online = false;
|
|
166
|
+
done({ error: 'Steamworks client not available' });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const started = Date.now();
|
|
171
|
+
const timeoutMs = Number(configValue('steamReadyTimeoutMs', READY_TIMEOUT_MS)) || READY_TIMEOUT_MS;
|
|
172
|
+
|
|
173
|
+
const finish = async () => {
|
|
174
|
+
const identity = await this.#resolveIdentity();
|
|
175
|
+
if (identity) {
|
|
176
|
+
this.#applyIdentity(identity);
|
|
177
|
+
done();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (Date.now() - started >= timeoutMs) {
|
|
182
|
+
this.online = false;
|
|
183
|
+
done({ error: 'Steamworks client not available' });
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
window.setTimeout(finish, READY_POLL_MS);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
finish();
|
|
19
191
|
}
|
|
20
192
|
|
|
21
|
-
|
|
22
|
-
callback({
|
|
193
|
+
#complete(callback) {
|
|
194
|
+
callback({
|
|
195
|
+
authenticated: this.authenticated,
|
|
196
|
+
user: this.getUser(),
|
|
197
|
+
});
|
|
23
198
|
}
|
|
24
199
|
|
|
25
|
-
|
|
26
|
-
|
|
200
|
+
login(callback) {
|
|
201
|
+
this.#resolveIdentity()
|
|
202
|
+
.then((identity) => {
|
|
203
|
+
this.#applyIdentity(identity);
|
|
204
|
+
this.#complete(callback);
|
|
205
|
+
})
|
|
206
|
+
.catch(() => {
|
|
207
|
+
this.user = null;
|
|
208
|
+
this.authenticated = false;
|
|
209
|
+
this.online = false;
|
|
210
|
+
this.#complete(callback);
|
|
211
|
+
});
|
|
27
212
|
}
|
|
28
213
|
|
|
29
|
-
|
|
30
|
-
|
|
214
|
+
autoLogin(callback) {
|
|
215
|
+
this.login(callback);
|
|
31
216
|
}
|
|
32
217
|
|
|
33
218
|
logout(callback) {
|
|
34
219
|
const storage = getStorage();
|
|
35
220
|
this.user = null;
|
|
36
221
|
this.authenticated = false;
|
|
37
|
-
|
|
222
|
+
this.online = false;
|
|
223
|
+
storage?.remove(this.#autoLoginStorageKey());
|
|
38
224
|
callback();
|
|
39
225
|
}
|
|
40
226
|
|
|
@@ -42,12 +228,20 @@
|
|
|
42
228
|
if (!this.authenticated || !this.user) {
|
|
43
229
|
return null;
|
|
44
230
|
}
|
|
45
|
-
return {
|
|
231
|
+
return {
|
|
232
|
+
id: this.user.id,
|
|
233
|
+
email: this.user.email || '',
|
|
234
|
+
};
|
|
46
235
|
}
|
|
47
236
|
|
|
48
237
|
isAuthenticated() {
|
|
49
238
|
return !!this.authenticated;
|
|
50
239
|
}
|
|
240
|
+
|
|
241
|
+
/** @returns {object | null} Initialized steamworks.js client, when exposed synchronously */
|
|
242
|
+
getSteamworksClient() {
|
|
243
|
+
return this.#client ?? resolveSteamworksClient();
|
|
244
|
+
}
|
|
51
245
|
}
|
|
52
246
|
|
|
53
247
|
ns.auth.Steam = () => new SteamAuthPlugin();
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* eslint-disable no-sync */
|
|
2
|
+
/* global process, require */
|
|
3
|
+
const
|
|
4
|
+
{app, shell, BrowserWindow, Menu} = require('electron'),
|
|
5
|
+
fs = require('fs');
|
|
6
|
+
|
|
7
|
+
/* adaptfully-steam-init */
|
|
8
|
+
/* /adaptfully-steam-init */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates window.
|
|
12
|
+
*/
|
|
13
|
+
function createWindow () {
|
|
14
|
+
// Create the browser window.
|
|
15
|
+
const
|
|
16
|
+
debugMode = false,
|
|
17
|
+
lastState = (function () {
|
|
18
|
+
let state = null;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
state = JSON.parse(fs.readFileSync(app.getPath('userData') + '/window-state.json', 'utf8'));
|
|
22
|
+
} catch (e) {}
|
|
23
|
+
|
|
24
|
+
return state || {
|
|
25
|
+
bounds: {
|
|
26
|
+
width: 800,
|
|
27
|
+
height: 600
|
|
28
|
+
},
|
|
29
|
+
maximized: false,
|
|
30
|
+
fullscreen: false
|
|
31
|
+
};
|
|
32
|
+
})(),
|
|
33
|
+
win = new BrowserWindow({
|
|
34
|
+
backgroundColor: '#E4378E',
|
|
35
|
+
width: lastState.bounds.width,
|
|
36
|
+
height: lastState.bounds.height,
|
|
37
|
+
minWidth: 200,
|
|
38
|
+
minHeight: 100,
|
|
39
|
+
webPreferences: {
|
|
40
|
+
devTools: debugMode,
|
|
41
|
+
nodeIntegration: false,
|
|
42
|
+
contextIsolation: true
|
|
43
|
+
/* adaptfully-steam-preload */
|
|
44
|
+
/* /adaptfully-steam-preload */
|
|
45
|
+
},
|
|
46
|
+
show: false
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (lastState.maximized) {
|
|
50
|
+
win.maximize();
|
|
51
|
+
}
|
|
52
|
+
if (lastState.fullscreen) {
|
|
53
|
+
win.setFullScreen(true);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!debugMode) {
|
|
57
|
+
Menu.setApplicationMenu(null);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
win.once('ready-to-show', () => { // helps prevent flicker-loading
|
|
61
|
+
win.show();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// and load the index.html of the app.
|
|
65
|
+
win.loadFile('index.html');
|
|
66
|
+
|
|
67
|
+
win.webContents.on('will-navigate', (event, url) => {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
shell.openExternal(url);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
//win.webContents.openDevTools();
|
|
74
|
+
|
|
75
|
+
win.on('close', () => {
|
|
76
|
+
fs.writeFileSync(app.getPath('userData') + '/window-state.json', JSON.stringify({
|
|
77
|
+
bounds: win.getNormalBounds(),
|
|
78
|
+
maximized: win.isMaximized(),
|
|
79
|
+
fullscreen: win.isFullScreen()
|
|
80
|
+
}));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// This method will be called when Electron has finished
|
|
85
|
+
// initialization and is ready to create browser windows.
|
|
86
|
+
// Some APIs can only be used after this event occurs.
|
|
87
|
+
app.whenReady().then(createWindow);
|
|
88
|
+
|
|
89
|
+
// Quit when all windows are closed.
|
|
90
|
+
app.on('window-all-closed', () => {
|
|
91
|
+
// On macOS it is common for applications and their menu bar
|
|
92
|
+
// to stay active until the user quits explicitly with Cmd + Q
|
|
93
|
+
if (process.platform !== 'darwin') {
|
|
94
|
+
app.quit();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
app.on('activate', () => {
|
|
99
|
+
// On macOS it's common to re-create a window in the app when the
|
|
100
|
+
// dock icon is clicked and there are no other windows open.
|
|
101
|
+
if (BrowserWindow.getAllWindows().length === 0) {
|
|
102
|
+
createWindow();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* global require */
|
|
2
|
+
const { contextBridge } = require('electron');
|
|
3
|
+
|
|
4
|
+
let steamClient = null;
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
steamClient = require('steamworks.js').init({{STEAM_APP_ID}});
|
|
8
|
+
} catch (err) {
|
|
9
|
+
console.error('Steamworks init failed:', err);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (steamClient) {
|
|
13
|
+
contextBridge.exposeInMainWorld('__ADAPTFULLY_STEAMWORKS__', steamClient);
|
|
14
|
+
}
|