@makefully/adaptfully 5.0.5 → 5.0.7

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,19 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 5.0.7 — 2026-10-07
6
+
7
+ ### Added
8
+
9
+ - Gitignore patterns for `assets/meta/deployments/**/s3.json` and `**/showfully.json`.
10
+ - Docs for web deploy via SFTP (atomic `cleanRemote`), AWS S3, and Showfully folder (credential shapes consumed by Wrapfully).
11
+
12
+ ## 5.0.6 — 2026-10-07
13
+
14
+ ### Changed
15
+
16
+ - **`google-auth`** persists the GIS access token in **`localStorage`** by default (was `sessionStorage`), so web sign-in survives browser restarts until explicit logout or Google silent reauth fails. Opt back into tab-only tokens with `config.googleTokenStorage: 'sessionStorage'`. Existing `sessionStorage` tokens are migrated once on read.
17
+
5
18
  ## 5.0.5 — 2026-10-06
6
19
 
7
20
  ### Changed
package/README.md CHANGED
@@ -236,7 +236,8 @@ Optional config keys:
236
236
  | Key | Default | Purpose |
237
237
  |-----|---------|---------|
238
238
  | `googleClientId` | *(required)* | Browser GIS OAuth client id |
239
- | `googleTokenKey` | `adaptfully_google_token` | `sessionStorage` key for the access token |
239
+ | `googleTokenKey` | `adaptfully_google_token` | Storage key for the GIS access token |
240
+ | `googleTokenStorage` | `localStorage` | `localStorage` (survives browser restart) or `sessionStorage` (tab-only) |
240
241
  | `googleScopes` | `openid email profile` | OAuth scopes |
241
242
  | `autoLoginStorageKey` | `lastLoggedIn` | Storage key written with the user id on login |
242
243
 
@@ -436,11 +437,17 @@ mygame/
436
437
  │ └── sftp.json
437
438
  ├── web-prod/
438
439
  │ └── sftp.json
440
+ ├── web-s3-prod/
441
+ │ ├── manifest.json # { "type": "s3" }
442
+ │ └── s3.json
443
+ ├── web-showfully/
444
+ │ ├── manifest.json # { "type": "showfully" }
445
+ │ └── showfully.json
439
446
  └── steam/ # default deployment for the `steam` platform
440
447
  └── steam.json
441
448
  ```
442
449
 
443
- The selected deployment's folder is shipped as `meta/publish/` in the zip, so per-platform credential file names (`build.json`, `sftp.json`, `steam.json`, `apple.json`, `google.json`, `ms.json`, `android/`, `ms/`) live inside `assets/meta/deployments/<key>/`.
450
+ The selected deployment's folder is shipped as `meta/publish/` in the zip, so per-platform credential file names (`build.json`, `sftp.json`, `s3.json`, `showfully.json`, `steam.json`, `apple.json`, `google.json`, `ms.json`, `android/`, `ms/`) live inside `assets/meta/deployments/<key>/`.
444
451
 
445
452
  Icons (`icon-foreground.png`, `icon-background.png`) are required for mobile, desktop, and Steam builds. If either file is missing from `assets/meta/`, Adaptfully **warns** and ships packaged placeholder icons for that build so Wrapfully can still run.
446
453
 
@@ -717,7 +724,9 @@ Release `win` builds can be signed with `assets/meta/deployments/<deployment>/ms
717
724
 
718
725
  #### Web (`web` / `webapp`)
719
726
 
720
- `adaptfully deploy web` prebuilds and POSTs to the Wrapfully **`webapp`** builder (same conduit as Steam). Put an `sftp.json` in each web deployment folder (e.g. `assets/meta/deployments/web-prod/sftp.json`); the selected deployment is zipped as `meta/publish/sftp.json` for Wrapfully to deploy via SFTP. The inner key stays the builder name (`webapp`).
727
+ `adaptfully deploy web` prebuilds and POSTs to the Wrapfully **`webapp`** builder (same conduit as Steam). Each web deployment folder under `assets/meta/deployments/<key>/` is zipped as `meta/publish/` for Wrapfully. Set `manifest.json` `"type"` to `sftp`, `s3`, or `showfully` (or rely on the credential filename). The inner settings key stays the builder name (`webapp`).
728
+
729
+ **SFTP** — `sftp.json`:
721
730
 
722
731
  ```json
723
732
  {
@@ -735,7 +744,42 @@ Release `win` builds can be signed with `assets/meta/deployments/<deployment>/ms
735
744
  }
736
745
  ```
737
746
 
738
- See [Wrapfully README](https://github.com/Makefully-Studios/wrapfully-client) for `uploadMode`, `serviceWorker`, `metaWeb`, and `cleanRemote`.
747
+ `path` is the remote site root or a longer subfolder path. With `uploadMode: "direct"` and `cleanRemote: true`, Wrapfully uploads to `[path]-temp`, then deletes the live directory and renames temp → `path` (atomic replace). `cleanRemote` is ignored for `versioned` uploads.
748
+
749
+ **AWS S3** — `s3.json` + `manifest.json` `{ "type": "s3" }`:
750
+
751
+ ```json
752
+ {
753
+ "webapp": {
754
+ "region": "us-east-1",
755
+ "bucket": "my-games-cdn",
756
+ "prefix": "",
757
+ "uploadMode": "direct",
758
+ "cleanRemote": false,
759
+ "accessKeyId": "(iam access key)",
760
+ "secretAccessKey": "(iam secret)"
761
+ }
762
+ }
763
+ ```
764
+
765
+ `prefix: ""` deploys to the bucket root; `"games/mygame"` deploys under that key prefix. `cleanRemote: true` uses an atomic `prefix-temp` swap (refuses empty prefix unless `allowBucketRootWipe: true`).
766
+
767
+ **Showfully folder** — `showfully.json` + `manifest.json` `{ "type": "showfully" }`:
768
+
769
+ ```json
770
+ {
771
+ "webapp": {
772
+ "server": "https://make.makefullystudios.com/",
773
+ "folder": "MyGame",
774
+ "path": "",
775
+ "accessToken": "sfpat_…"
776
+ }
777
+ }
778
+ ```
779
+
780
+ `folder` is a top-level Showfully folder name. `path: ""` writes at that folder’s root; `"web"` (or nested) prefixes zip entries under a subfolder. `accessToken` is required (Wrapfully workers cannot reuse the Yap submit PAT).
781
+
782
+ See [Wrapfully README](https://github.com/Makefully-Studios/wrapfully-client) for `uploadMode`, `serviceWorker`, `metaWeb`, and deployer details.
739
783
 
740
784
  #### Windows (`win`, `win-dev`, `uwp`)
741
785
 
@@ -6,6 +6,8 @@ import { stdin as input, stdout as output } from 'node:process';
6
6
  /** Patterns that should never be committed from deployment credential folders. */
7
7
  export const PUBLISH_CREDENTIAL_GITIGNORE_PATTERNS = [
8
8
  'assets/meta/deployments/**/sftp.json',
9
+ 'assets/meta/deployments/**/s3.json',
10
+ 'assets/meta/deployments/**/showfully.json',
9
11
  'assets/meta/deployments/**/steam.json',
10
12
  'assets/meta/deployments/**/build.json',
11
13
  'assets/meta/deployments/**/google.json',
@@ -1,4 +1,4 @@
1
- /* global google, sessionStorage, window */
1
+ /* global google, localStorage, sessionStorage, window */
2
2
 
3
3
  (function registerGoogleAuth(ns) {
4
4
  const { configValue, getStorage } = ns.auth.helpers;
@@ -6,6 +6,7 @@
6
6
  const DEFAULT_SCOPES = 'openid email profile';
7
7
  const DEFAULT_TOKEN_KEY = 'adaptfully_google_token';
8
8
  const DEFAULT_AUTO_LOGIN_KEY = 'lastLoggedIn';
9
+ const DEFAULT_TOKEN_STORAGE = 'localStorage';
9
10
 
10
11
  class GoogleAuthPlugin {
11
12
  constructor() {
@@ -32,6 +33,27 @@
32
33
  return configValue('googleTokenKey', DEFAULT_TOKEN_KEY);
33
34
  }
34
35
 
36
+ /**
37
+ * Where the GIS access token is persisted.
38
+ * Default `localStorage` so sign-in survives browser restarts until logout
39
+ * (or Google silent reauth fails). Use `sessionStorage` for tab-only tokens.
40
+ */
41
+ #tokenStorageName() {
42
+ const raw = configValue('googleTokenStorage', DEFAULT_TOKEN_STORAGE);
43
+ return raw === 'sessionStorage' ? 'sessionStorage' : 'localStorage';
44
+ }
45
+
46
+ #tokenStore() {
47
+ try {
48
+ if (this.#tokenStorageName() === 'sessionStorage') {
49
+ return typeof sessionStorage !== 'undefined' ? sessionStorage : null;
50
+ }
51
+ return typeof localStorage !== 'undefined' ? localStorage : null;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+
35
57
  /**
36
58
  * Browser GIS OAuth client id only (`config.googleClientId`).
37
59
  * Capacitor / Capgo builds use `platforms.<name>.socialLogin.google.webClientId` instead.
@@ -95,16 +117,91 @@
95
117
  this.authenticated = !!(this.user.id && this.user.email);
96
118
  }
97
119
 
120
+ #readStoredToken() {
121
+ const key = this.#tokenKey(),
122
+ primary = this.#tokenStore();
123
+ let token = '';
124
+
125
+ try {
126
+ token = (primary && primary.getItem(key)) || '';
127
+ } catch {
128
+ token = '';
129
+ }
130
+
131
+ // One-time migrate: older builds kept the token in sessionStorage.
132
+ if (!token && this.#tokenStorageName() === 'localStorage') {
133
+ try {
134
+ token = (typeof sessionStorage !== 'undefined' && sessionStorage.getItem(key)) || '';
135
+ if (token && primary) {
136
+ primary.setItem(key, token);
137
+ sessionStorage.removeItem(key);
138
+ }
139
+ } catch {
140
+ /* ignore quota / private-mode */
141
+ }
142
+ }
143
+
144
+ return token;
145
+ }
146
+
147
+ #writeStoredToken(token) {
148
+ const key = this.#tokenKey(),
149
+ primary = this.#tokenStore();
150
+
151
+ try {
152
+ if (primary) {
153
+ primary.setItem(key, token);
154
+ }
155
+ } catch {
156
+ /* ignore */
157
+ }
158
+
159
+ // Drop the legacy session copy so tab-close cannot leave a stale twin.
160
+ if (this.#tokenStorageName() === 'localStorage') {
161
+ try {
162
+ if (typeof sessionStorage !== 'undefined') {
163
+ sessionStorage.removeItem(key);
164
+ }
165
+ } catch {
166
+ /* ignore */
167
+ }
168
+ }
169
+ }
170
+
171
+ #clearStoredToken() {
172
+ const key = this.#tokenKey();
173
+
174
+ try {
175
+ this.#tokenStore()?.removeItem(key);
176
+ } catch {
177
+ /* ignore */
178
+ }
179
+ try {
180
+ if (typeof sessionStorage !== 'undefined') {
181
+ sessionStorage.removeItem(key);
182
+ }
183
+ } catch {
184
+ /* ignore */
185
+ }
186
+ try {
187
+ if (typeof localStorage !== 'undefined') {
188
+ localStorage.removeItem(key);
189
+ }
190
+ } catch {
191
+ /* ignore */
192
+ }
193
+ }
194
+
98
195
  #clearSession() {
99
196
  this.accessToken = '';
100
197
  this.user = null;
101
198
  this.authenticated = false;
102
- sessionStorage.removeItem(this.#tokenKey());
199
+ this.#clearStoredToken();
103
200
  }
104
201
 
105
202
  #loadStoredToken() {
106
203
  if (!this.accessToken) {
107
- this.accessToken = sessionStorage.getItem(this.#tokenKey()) || '';
204
+ this.accessToken = this.#readStoredToken();
108
205
  }
109
206
  }
110
207
 
@@ -126,7 +223,7 @@
126
223
  })
127
224
  .then((data) => {
128
225
  this.accessToken = token;
129
- sessionStorage.setItem(this.#tokenKey(), token);
226
+ this.#writeStoredToken(token);
130
227
  this.#applyUserInfo(data);
131
228
  callback();
132
229
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/adaptfully",
3
- "version": "5.0.5",
3
+ "version": "5.0.7",
4
4
  "description": "Platform abstraction and Wrapfully deploy client for Makefully games",
5
5
  "type": "module",
6
6
  "main": "./lib/node/index.js",