@makefully/adaptfully 3.0.3 → 3.1.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 CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 3.1.0 — 2026-06-18
6
+
7
+ ### Added
8
+
9
+ - Built-in storage plugins: `localStorage` (sync) and `indexedDB` (async).
10
+ - Register with `"storage": "localStorage"` or `"storage": "indexedDB"` in `config.platforms.<platform>.registrations`.
11
+
12
+ ### Changed
13
+
14
+ - Auth helpers use registered `storage` only (no implicit fallbacks).
15
+
5
16
  ## 3.0.3 — 2026-06-18
6
17
 
7
18
  ### Added
package/README.md CHANGED
@@ -45,6 +45,21 @@ const platform = adaptfully.get('auth');
45
45
  platform.login(function (result) { /* ... */ });
46
46
  ```
47
47
 
48
+ ### Storage plugins
49
+
50
+ | Plugin key | Registration | Runtime |
51
+ |------------|--------------|---------|
52
+ | `localStorage` | `adaptfully.register('storage', adaptfully.storage.LocalStorage())` | Sync key/value storage in the browser |
53
+ | `indexedDB` | `adaptfully.register('storage', adaptfully.storage.IndexedDB())` | Async key/value storage for larger payloads |
54
+
55
+ In-game:
56
+
57
+ ```javascript
58
+ const storage = adaptfully.get('storage');
59
+ storage.set('playerName', 'Ada');
60
+ storage.getObject('currentGame');
61
+ ```
62
+
48
63
  ### Auth plugins
49
64
 
50
65
  | Plugin key | Registration | Runtime |
@@ -61,18 +76,19 @@ Use plugin keys in `config.platforms.<platform>.registrations`. Custom deploy sc
61
76
  "platforms": {
62
77
  "steam": {
63
78
  "registrations": {
64
- "auth": "steam-auth",
65
- "storage": "/javascript/custom-storage-solution.js"
79
+ "storage": "localStorage",
80
+ "auth": "steam-auth"
66
81
  }
67
82
  },
68
83
  "web": {
69
84
  "registrations": {
70
- "auth": "google-auth",
71
- "storage": "javascript/adaptfully-bridge.js"
85
+ "storage": "localStorage",
86
+ "auth": "google-auth"
72
87
  }
73
88
  },
74
89
  "dev": {
75
90
  "registrations": {
91
+ "storage": "localStorage",
76
92
  "auth": "dev-auth"
77
93
  }
78
94
  }
@@ -25,6 +25,14 @@ export const STANDARD_PLUGINS = {
25
25
  scripts: ['core.js', 'platform.js', 'auth/_helpers.js', 'auth/dev-auth.js'],
26
26
  registration: (key) => `adaptfully.register('${key}', adaptfully.auth.Dev);`,
27
27
  },
28
+ localStorage: {
29
+ scripts: ['core.js', 'storage/_helpers.js', 'storage/local-storage.js'],
30
+ registration: (key) => `adaptfully.register('${key}', adaptfully.storage.LocalStorage());`,
31
+ },
32
+ indexedDB: {
33
+ scripts: ['core.js', 'storage/_helpers.js', 'storage/indexed-db.js'],
34
+ registration: (key) => `adaptfully.register('${key}', adaptfully.storage.IndexedDB());`,
35
+ },
28
36
  };
29
37
 
30
38
  /** Default Wrapfully builder → config.platforms key */
@@ -1,4 +1,4 @@
1
- /* global localStorage, window */
1
+ /* global window */
2
2
 
3
3
  /**
4
4
  * @typedef {{ id: string, email: string }} AuthUser
@@ -17,27 +17,6 @@
17
17
  */
18
18
 
19
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
-
41
20
  const helpers = {
42
21
  configValue(key, fallback) {
43
22
  if (!ns.has('config')) {
@@ -51,13 +30,7 @@
51
30
  },
52
31
 
53
32
  getStorage() {
54
- if (ns.has('storage')) {
55
- return ns.get('storage');
56
- }
57
- if (defaultStorage === undefined) {
58
- defaultStorage = createDefaultStorage();
59
- }
60
- return defaultStorage;
33
+ return ns.has('storage') ? ns.get('storage') : null;
61
34
  },
62
35
  };
63
36
 
@@ -0,0 +1,5 @@
1
+ /* global window */
2
+
3
+ (function registerStorageHelpers(ns) {
4
+ ns.storage = ns.storage || {};
5
+ }(window.adaptfully));
@@ -0,0 +1,87 @@
1
+ /* global indexedDB, window */
2
+
3
+ (function registerIndexedDBStorage(ns) {
4
+ function configValue(key, fallback) {
5
+ if (!ns.has('config')) {
6
+ return fallback;
7
+ }
8
+ const config = ns.get('config');
9
+ return config?.[key] != null ? config[key] : fallback;
10
+ }
11
+
12
+ ns.storage.IndexedDB = function indexedDBFactory() {
13
+ const dbName = configValue('indexedDBName', 'adaptfully');
14
+ const storeName = configValue('indexedDBStoreName', 'storage');
15
+ /** @type {Promise<IDBDatabase> | null} */
16
+ let dbPromise = null;
17
+
18
+ function openDb() {
19
+ if (!dbPromise) {
20
+ dbPromise = new Promise((resolve, reject) => {
21
+ const request = indexedDB.open(dbName, 1);
22
+ request.onupgradeneeded = () => {
23
+ request.result.createObjectStore(storeName);
24
+ };
25
+ request.onsuccess = () => resolve(request.result);
26
+ request.onerror = () => reject(request.error);
27
+ });
28
+ }
29
+ return dbPromise;
30
+ }
31
+
32
+ function withStore(mode, fn) {
33
+ return openDb().then((db) => new Promise((resolve, reject) => {
34
+ const tx = db.transaction(storeName, mode);
35
+ const store = tx.objectStore(storeName);
36
+ const request = fn(store);
37
+ request.onsuccess = () => resolve(request.result);
38
+ request.onerror = () => reject(request.error);
39
+ }));
40
+ }
41
+
42
+ return {
43
+ name: 'indexedDB',
44
+ async get(field) {
45
+ try {
46
+ const value = await withStore('readonly', (store) => store.get(field));
47
+ return value === undefined ? false : value;
48
+ } catch {
49
+ return false;
50
+ }
51
+ },
52
+ async set(field, value) {
53
+ await this.remove(field);
54
+ try {
55
+ await withStore('readwrite', (store) => store.put(value, field));
56
+ } catch {
57
+ // ignore
58
+ }
59
+ },
60
+ async remove(field) {
61
+ try {
62
+ await withStore('readwrite', (store) => store.delete(field));
63
+ } catch {
64
+ // ignore
65
+ }
66
+ },
67
+ async getObject(field) {
68
+ const raw = await this.get(field);
69
+ if (!raw) {
70
+ return false;
71
+ }
72
+ try {
73
+ return JSON.parse(raw);
74
+ } catch {
75
+ return false;
76
+ }
77
+ },
78
+ async setObject(field, obj) {
79
+ try {
80
+ await this.set(field, JSON.stringify(obj));
81
+ } catch {
82
+ // ignore
83
+ }
84
+ },
85
+ };
86
+ };
87
+ }(window.adaptfully));
@@ -0,0 +1,50 @@
1
+ /* global localStorage, window */
2
+
3
+ (function registerLocalStorage(ns) {
4
+ ns.storage.LocalStorage = function localStorageFactory() {
5
+ return {
6
+ name: 'localStorage',
7
+ get(field) {
8
+ try {
9
+ const value = localStorage.getItem(field);
10
+ return value === null ? false : value;
11
+ } catch {
12
+ return false;
13
+ }
14
+ },
15
+ set(field, value) {
16
+ this.remove(field);
17
+ try {
18
+ localStorage.setItem(field, value);
19
+ } catch {
20
+ // quota or private browsing
21
+ }
22
+ },
23
+ remove(field) {
24
+ try {
25
+ localStorage.removeItem(field);
26
+ } catch {
27
+ // ignore
28
+ }
29
+ },
30
+ getObject(field) {
31
+ const raw = this.get(field);
32
+ if (!raw) {
33
+ return false;
34
+ }
35
+ try {
36
+ return JSON.parse(raw);
37
+ } catch {
38
+ return false;
39
+ }
40
+ },
41
+ setObject(field, obj) {
42
+ try {
43
+ this.set(field, JSON.stringify(obj));
44
+ } catch {
45
+ // ignore
46
+ }
47
+ },
48
+ };
49
+ };
50
+ }(window.adaptfully));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makefully/adaptfully",
3
- "version": "3.0.3",
3
+ "version": "3.1.0",
4
4
  "description": "Platform abstraction and Wrapfully deploy client for Makefully games",
5
5
  "type": "module",
6
6
  "main": "./lib/node/index.js",