@makefully/adaptfully 3.7.1 → 3.8.1

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.8.1 — 2026-07-14
6
+
7
+ ### Fixed
8
+
9
+ - **Extract mode** clears stale `output/artifacts/`, `wrapfully-build.json`, and `wrapfully-status.json` before unpacking a Wrapfully response so nested `artifacts/artifacts` leftovers cannot accumulate across runs.
10
+
11
+ ## 3.8.0 — 2026-07-10
12
+
13
+ ### Changed
14
+
15
+ - **`steam-auth` Steamworks app ID** moves from top-level `config.steamId` to **`platforms.<name>.steamId`**. Steam **upload** app IDs belong in each deployment’s `manifest.json` (Wrapfully), not in Adaptfully client config.
16
+
5
17
  ## 3.7.0 — 2026-07-09
6
18
 
7
19
  ### Added
package/README.md CHANGED
@@ -32,8 +32,8 @@ adaptfully steam-auth # one-time: log in with steamcmd
32
32
  | Stage | What it does |
33
33
  |-------|----------------|
34
34
  | `prebuild` | Copy `deploy/` to `output/<platform>-prebuild/` and inject registrations into `config.htmlInjections` |
35
- | `build` | Prebuild, then POST the result to Wrapfully (the platform's default/first deployment only) |
36
- | `deploy` | Prebuild, zip, POST to Wrapfully once per deployment; platform release when credentials are in the deployment folder |
35
+ | `build` | Prebuild, then POST the result to Wrapfully (artifact zip only) |
36
+ | `deploy` | POST the prior build artifact from `./output/` to each configured deployment (or `--deployment`) |
37
37
  | `steam-auth` | One-time local setup: install steamcmd, interactive login, write the `steam` deployment's `steam.json` |
38
38
 
39
39
  `wrapfully-deploy` is a compatibility alias for `adaptfully deploy` when invoked with a Wrapfully builder name (`steam`, `win`, `android`, etc.).
@@ -135,12 +135,11 @@ Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or `
135
135
  | `cordova` | `cordova.js` stub, `game-config.js`, CSP/viewport HTML extras |
136
136
  | `capacitor` | `game-config.js` (more Capacitor-specific output planned) |
137
137
 
138
- **`steam-auth` requires `packager: "electron"`** and `config.steamId`.
138
+ **`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
139
 
140
140
  ```json
141
141
  {
142
142
  "config": {
143
- "steamId": 719140,
144
143
  "platforms": {
145
144
  "web": {
146
145
  "packager": "web",
@@ -148,6 +147,7 @@ Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or `
148
147
  },
149
148
  "steam": {
150
149
  "packager": "electron",
150
+ "steamId": 1234567,
151
151
  "registrations": { "auth": "steam-auth" }
152
152
  }
153
153
  }
@@ -160,7 +160,7 @@ Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or `
160
160
  When `steam-auth` is registered on an **`electron`** platform, Adaptfully prebuild writes:
161
161
 
162
162
  - **`main.js`** — Electron shell with Steam overlay enabled and a preload script wired in
163
- - **`preload.js`** — initializes [steamworks.js](https://github.com/ceifa/steamworks.js) with `config.steamId` and exposes `window.__ADAPTFULLY_STEAMWORKS__`
163
+ - **`preload.js`** — initializes [steamworks.js](https://github.com/ceifa/steamworks.js) with `platforms.<name>.steamId` and exposes `window.__ADAPTFULLY_STEAMWORKS__`
164
164
 
165
165
  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:
166
166
 
@@ -371,7 +371,6 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
371
371
  "scope": "https://example.com/games/",
372
372
  "themeColor": "#1a1a2e",
373
373
  "twitterId": "@examplegames",
374
- "steamId": 1234567,
375
374
  "deployFolder": "deploy",
376
375
  "platforms": {
377
376
  "web": {
@@ -382,6 +381,7 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
382
381
  },
383
382
  "steam": {
384
383
  "packager": "electron",
384
+ "steamId": 1234567,
385
385
  "registrations": {
386
386
  "auth": "steam-auth",
387
387
  "storage": "javascript/adaptfully-bridge.js"
@@ -407,7 +407,6 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
407
407
  | `scope` | Web/PWA | Base URL scope for the web app |
408
408
  | `themeColor` | Cordova, Electron, UWP, web | Loading screen / theme color |
409
409
  | `twitterId` | Web | Twitter handle for meta tags |
410
- | `steamId` | Steam | Steam app ID |
411
410
  | `deployFolder` | Client | Neutral deploy directory staged before prebuild (default: `deploy`) |
412
411
  | `htmlInjections` | Prebuild | Deploy-relative HTML paths to inject (default: `["index.html"]`) |
413
412
  | `outputFolder` | Client | Prebuild output root (default: `output`) |
@@ -415,6 +414,7 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
415
414
  | `platforms.<name>.builder` | Build/deploy | Override Wrapfully builder for a platform (default: `web` → `webapp`, others match platform key) |
416
415
  | `platforms.<name>.builders` | wrapfully-deploy | Map additional Wrapfully builder names to a platform |
417
416
  | `platforms.<name>.packager` | Prebuild | `web` (default), `electron`, `cordova`, or `capacitor` — selects the packager class that adds platform-specific files during prebuild |
417
+ | `platforms.<name>.steamId` | Electron + steam-auth | Steamworks app ID baked into `preload.js` (client). Upload app IDs belong in the Steam deployment `manifest.json` |
418
418
  | `properties` | Cordova | Cordova config.xml entries (plugins, allow-navigation, etc.) |
419
419
 
420
420
  ### `wrapfully.json`
@@ -568,7 +568,7 @@ Accounts **without** Steam Guard only need `username`, `password`, and `configVd
568
568
 
569
569
  Re-run `adaptfully steam-auth` if Steam invalidates the token (new machine, password change, or expired guard).
570
570
 
571
- Also set `steamId` in your `config` block.
571
+ For Steamworks / `steam-auth` client builds, set `platforms.<name>.steamId`. For Steam **uploads**, set `steamId` on the deployment’s `manifest.json` (see Wrapfully). Those are independent: you can deploy without steam-auth, or use steam-auth without uploading.
572
572
 
573
573
  Steam builds can run on either the Windows or Mac server. The server that receives the request builds its own platforms and requests the rest from the other server (Windows builds `win` and requests `mac`/`linux`; Mac builds `mac`/`linux` and requests `win`). Install the Steamworks SDK ContentBuilder on any server that will upload to Steam.
574
574
 
@@ -0,0 +1,78 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const MANIFEST_NAME = 'wrapfully-build.json';
5
+ const STATUS_NAME = 'wrapfully-status.json';
6
+
7
+ /**
8
+ * Directory where build responses are extracted (contains wrapfully-build.json).
9
+ *
10
+ * @param {{ config?: { outputFolder?: string } }} pkg
11
+ * @param {string} [outputRoot='output']
12
+ */
13
+ export function buildOutputDir(pkg, outputRoot = 'output') {
14
+ const outputFolder = pkg.config?.outputFolder || outputRoot;
15
+ return path.resolve(outputFolder);
16
+ }
17
+
18
+ /**
19
+ * Remove prior Wrapfully extract artifacts so zip responses do not stack
20
+ * nested `artifacts/` directories across runs.
21
+ *
22
+ * @param {string} [outputRoot='output']
23
+ */
24
+ export function clearStaleBuildExtract(outputRoot = 'output') {
25
+ const root = path.resolve(outputRoot);
26
+ const stalePaths = [
27
+ path.join(root, 'artifacts'),
28
+ path.join(root, MANIFEST_NAME),
29
+ path.join(root, STATUS_NAME),
30
+ ];
31
+
32
+ for (const stalePath of stalePaths) {
33
+ fs.rmSync(stalePath, { recursive: true, force: true });
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Resolve the artifact directory from a prior `adaptfully build` (or compatible zip extract).
39
+ *
40
+ * @param {{ name: string, version: string, config?: { outputFolder?: string } }} pkg
41
+ * @param {{ outputRoot?: string, platformKey?: string }} [options]
42
+ * @returns {string}
43
+ */
44
+ export function resolveBuildArtifactDir(pkg, options = {}) {
45
+ const artifactDir = buildOutputDir(pkg, options.outputRoot);
46
+ const manifestPath = path.join(artifactDir, MANIFEST_NAME);
47
+
48
+ if (!fs.existsSync(manifestPath)) {
49
+ throw new Error(
50
+ 'No prior build artifact found in '
51
+ + `${artifactDir}. Run \`adaptfully build <platform>\` first or pass --artifact <path>.`,
52
+ );
53
+ }
54
+
55
+ let manifest;
56
+ try {
57
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
58
+ } catch {
59
+ throw new Error(`Unable to read ${manifestPath}`);
60
+ }
61
+
62
+ const gameId = `${pkg.name}-${pkg.version}`;
63
+ if (manifest.gameId && manifest.gameId !== gameId) {
64
+ throw new Error(
65
+ `Build artifact is for "${manifest.gameId}" but package.json is ${gameId}. `
66
+ + 'Run a fresh build or pass --artifact <path>.',
67
+ );
68
+ }
69
+
70
+ if (options.platformKey && manifest.platformKey && manifest.platformKey !== options.platformKey) {
71
+ throw new Error(
72
+ `Build artifact is for platform "${manifest.platformKey}" but deploy targets "${options.platformKey}". `
73
+ + 'Run a fresh build or pass --artifact <path>.',
74
+ );
75
+ }
76
+
77
+ return artifactDir;
78
+ }
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import { pipeline } from 'node:stream/promises';
5
5
  import unzipper from 'unzip-stream';
6
6
  import { createDeployArchive, createReleaseArchive, createSourceArchive } from './archive.js';
7
+ import { clearStaleBuildExtract } from './artifacts.js';
7
8
  import { listHtmlFilesRecursive } from './fs-utils.js';
8
9
  import { printBuildReport } from './report.js';
9
10
 
@@ -47,9 +48,15 @@ function resolveWrapfullyUrl(server, stage, family, gameId, platformKey, deploym
47
48
  export async function send(gameId, contents, server, stage, family, deployFolder, pkg, mode = 'extract', options = {}) {
48
49
  const log = options.log ?? console.log;
49
50
  const platformKey = options.platformKey ?? family;
51
+ const outputRoot = pkg.config?.outputFolder || 'output';
52
+
53
+ if (mode === 'extract') {
54
+ clearStaleBuildExtract(outputRoot);
55
+ }
56
+
50
57
  const destination = mode === 'extract'
51
- ? unzipper.Extract({ path: './output/', concurrency: 1 })
52
- : fs.createWriteStream(`./output/${pkg.name}-${pkg.version}-${stage}-${family}.zip`);
58
+ ? unzipper.Extract({ path: `${outputRoot}/`, concurrency: 1 })
59
+ : fs.createWriteStream(`${outputRoot}/${pkg.name}-${pkg.version}-${stage}-${family}.zip`);
53
60
 
54
61
  let archiveStream;
55
62
 
package/lib/node/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export { buildOutputDir, clearStaleBuildExtract, resolveBuildArtifactDir } from './artifacts.js';
1
2
  export { createArchive, createDeployArchive, createReleaseArchive, createSourceArchive } from './archive.js';
2
3
  export { loadProjectConfig, resolveServerUrl } from './config.js';
3
4
  export { send } from './deploy.js';
@@ -178,6 +178,19 @@ export function buildElectronPreload(steamAppId) {
178
178
  return ELECTRON_PRELOAD_HEAD + steamAppId + ELECTRON_PRELOAD_TAIL;
179
179
  }
180
180
 
181
+ /**
182
+ * Steamworks app ID for steam-auth preload, from the active platform config.
183
+ * @param {string | null | undefined} platformKey
184
+ * @param {{ config?: { platforms?: Record<string, { steamId?: number | string }> } }} pkg
185
+ * @returns {number | string | undefined}
186
+ */
187
+ export function resolvePlatformSteamId(platformKey, pkg) {
188
+ if (!platformKey) {
189
+ return undefined;
190
+ }
191
+ return pkg.config?.platforms?.[platformKey]?.steamId;
192
+ }
193
+
181
194
  export class ElectronPackager extends Packager {
182
195
  /** @type {'electron'} */
183
196
  static id = 'electron';
@@ -188,10 +201,10 @@ export class ElectronPackager extends Packager {
188
201
  validate() {
189
202
  super.validate();
190
203
 
191
- if (this.usesPlugin('steam-auth') && !this.pkg.config?.steamId) {
204
+ if (this.usesPlugin('steam-auth') && !resolvePlatformSteamId(this.platformKey, this.pkg)) {
192
205
  const platformLabel = this.platformKey ?? this.platforms.join(', ');
193
206
  throw new Error(
194
- `Platform "${platformLabel}" uses steam-auth but config.steamId is not set.`,
207
+ `Platform "${platformLabel}" uses steam-auth but platforms.${platformLabel}.steamId is not set.`,
195
208
  );
196
209
  }
197
210
  }
@@ -212,9 +225,10 @@ export class ElectronPackager extends Packager {
212
225
  const mainContent = buildElectronMain(withSteam, this.pkg.config?.themeColor);
213
226
 
214
227
  if (withSteam) {
228
+ const steamId = resolvePlatformSteamId(this.platformKey, this.pkg);
215
229
  fs.writeFileSync(
216
230
  path.join(dest, 'preload.js'),
217
- buildElectronPreload(this.pkg.config.steamId),
231
+ buildElectronPreload(steamId),
218
232
  );
219
233
  this.log('adaptfully: write preload.js (steam-auth)');
220
234
  }
@@ -6,7 +6,12 @@ import { WebPackager } from './web.js';
6
6
 
7
7
  export { Packager, VALID_PACKAGERS } from './base.js';
8
8
  export { WebPackager } from './web.js';
9
- export { ElectronPackager, buildElectronMain, buildElectronPreload } from './electron.js';
9
+ export {
10
+ ElectronPackager,
11
+ buildElectronMain,
12
+ buildElectronPreload,
13
+ resolvePlatformSteamId,
14
+ } from './electron.js';
10
15
  export { CordovaPackager } from './cordova.js';
11
16
  export { CapacitorPackager } from './capacitor.js';
12
17
 
@@ -22,7 +27,7 @@ const PACKAGER_REGISTRY = {
22
27
 
23
28
  /**
24
29
  * @param {string} platformKey
25
- * @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string> }>, steamId?: number } }} pkg
30
+ * @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string>, steamId?: number | string }> } }} pkg
26
31
  * @param {{ log?: (message: string) => void, platforms?: string[] }} [options]
27
32
  * @returns {Packager}
28
33
  */
@@ -54,7 +59,7 @@ export function resolvePlatformPackager(platformKey, pkg) {
54
59
 
55
60
  /**
56
61
  * @param {string} platformKey
57
- * @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string> }>, steamId?: number } }} pkg
62
+ * @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string>, steamId?: number | string }> } }} pkg
58
63
  */
59
64
  export function validatePlatformPackager(platformKey, pkg) {
60
65
  createPackagerForPlatform(platformKey, pkg).validate();
@@ -70,7 +75,7 @@ export function usesSteamAuth(registrations) {
70
75
  /**
71
76
  * @param {string} dest
72
77
  * @param {string} platformKey
73
- * @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string> }>, steamId?: number } }} pkg
78
+ * @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string>, steamId?: number | string }> } }} pkg
74
79
  * @param {{ log?: (message: string) => void, platforms?: string[] }} [options]
75
80
  */
76
81
  export function applyPackagerTemplates(dest, platformKey, pkg, options = {}) {
@@ -1,5 +1,6 @@
1
1
  import { loadProjectConfig, resolveServerUrl } from './config.js';
2
2
  import { send } from './deploy.js';
3
+ import { resolveBuildArtifactDir } from './artifacts.js';
3
4
  import { prebuildPlatform } from './prebuild.js';
4
5
  import {
5
6
  resolveBuildSpec,
@@ -43,8 +44,16 @@ export async function runAdaptfullyStage(stage, platformKey, options) {
43
44
  deployments = deployments.filter((key) => key !== 'zip');
44
45
  }
45
46
 
46
- const prebuiltDir = stage === 'deploy' && options.artifactPath
47
- ? options.artifactPath
47
+ let artifactPath = options.artifactPath;
48
+ if (stage === 'deploy' && !artifactPath) {
49
+ artifactPath = resolveBuildArtifactDir(pkg, {
50
+ outputRoot: options.outputRoot,
51
+ platformKey,
52
+ });
53
+ }
54
+
55
+ const prebuiltDir = stage === 'deploy'
56
+ ? artifactPath
48
57
  : prebuildPlatform(deployFolder, platformKey, pkg, {
49
58
  log,
50
59
  outputRoot: options.outputRoot,
@@ -81,7 +90,7 @@ export async function runAdaptfullyStage(stage, platformKey, options) {
81
90
  );
82
91
 
83
92
  sent.push({ deploymentKey: 'release', stage, family: buildSpec.family });
84
- return { prebuiltDir, platformKey, buildSpec, deployments: sent };
93
+ return { prebuiltDir, platformKey, buildSpec, deployments: sent, artifactPath };
85
94
  }
86
95
 
87
96
  for (const deploymentKey of deployments) {
@@ -108,14 +117,14 @@ export async function runAdaptfullyStage(stage, platformKey, options) {
108
117
  publishDir,
109
118
  platformKey,
110
119
  deploymentKey: wrapStage === 'deploy' ? deploymentKey : undefined,
111
- artifactPath: options.artifactPath,
120
+ artifactPath,
112
121
  },
113
122
  );
114
123
 
115
124
  sent.push({ deploymentKey, stage: wrapStage, family: buildSpec.family });
116
125
  }
117
126
 
118
- return { prebuiltDir, platformKey, buildSpec, deployments: sent };
127
+ return { prebuiltDir, platformKey, buildSpec, deployments: sent, artifactPath };
119
128
  }
120
129
 
121
130
  /**
@@ -147,7 +156,7 @@ export async function adaptfullyFromCli(argv = process.argv) {
147
156
  + ' release prebuild + POST /{family}/release (build + configured deployments)\n'
148
157
  + '\n'
149
158
  + ' --deployment <key> Target a single named deployment.\n'
150
- + ' --artifact <path> Prior build artifact directory (deploy stage).\n'
159
+ + ' --artifact <path> Prior build artifact directory (deploy only; default: ./output/ after build).\n'
151
160
  + '\n'
152
161
  + ' steam-auth Install steamcmd if needed, log in interactively, write steam deployment steam.json',
153
162
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/adaptfully",
3
- "version": "3.7.1",
3
+ "version": "3.8.1",
4
4
  "description": "Platform abstraction and Wrapfully deploy client for Makefully games",
5
5
  "type": "module",
6
6
  "main": "./lib/node/index.js",