@haven-team/helix-sdk 1.0.12 → 1.0.13
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/helix-sdk.js +62 -0
- package/package.json +1 -1
package/helix-sdk.js
CHANGED
|
@@ -37,6 +37,16 @@
|
|
|
37
37
|
* localhost unless `force: true`. Failures never break the app; the SDK
|
|
38
38
|
* logs a warning and `enableLogRocket()` resolves to null. See
|
|
39
39
|
* docs/logrocket.md for the full manual.
|
|
40
|
+
*
|
|
41
|
+
* Store scoping (which stores the user may access):
|
|
42
|
+
* const scope = await helix.getStoreScope();
|
|
43
|
+
* // { unrestricted: false, store_ids: [3, 7], source: 'grant' }
|
|
44
|
+
* if (await helix.canAccessStore(storeId)) { ... }
|
|
45
|
+
*
|
|
46
|
+
* Resolved from GET /api/v1/auth/authenticate (cached). Admins / district
|
|
47
|
+
* managers come back `unrestricted: true`; everyone else is limited to
|
|
48
|
+
* `store_ids`. Fails CLOSED on error. This shapes the UI; the API still
|
|
49
|
+
* enforces scope server-side. See docs/store-scoping.md.
|
|
40
50
|
*/
|
|
41
51
|
(function (root, factory) {
|
|
42
52
|
if (typeof module !== 'undefined' && module.exports) {
|
|
@@ -52,6 +62,19 @@
|
|
|
52
62
|
const DEFAULT_LOGROCKET_APP_ID = 'lto7os/helix-frontend';
|
|
53
63
|
const LOGROCKET_CDN = 'https://cdn.lr-ingest.com/LogRocket.min.js';
|
|
54
64
|
|
|
65
|
+
// Coerce whatever the identity endpoint returns into a well-formed store
|
|
66
|
+
// scope. Missing/garbage input fails CLOSED (no access) rather than open.
|
|
67
|
+
function normalizeStoreScope(s) {
|
|
68
|
+
if (!s || typeof s !== 'object') return { unrestricted: false, store_ids: [], source: 'error' };
|
|
69
|
+
return {
|
|
70
|
+
unrestricted: s.unrestricted === true,
|
|
71
|
+
store_ids: Array.isArray(s.store_ids)
|
|
72
|
+
? s.store_ids.filter((v) => v != null && v !== '' && Number.isFinite(Number(v))).map(Number)
|
|
73
|
+
: [],
|
|
74
|
+
source: typeof s.source === 'string' ? s.source : 'unknown'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
55
78
|
class HelixSDK {
|
|
56
79
|
constructor(options = {}) {
|
|
57
80
|
this._ready = false;
|
|
@@ -66,6 +89,8 @@
|
|
|
66
89
|
this._logrocket = null;
|
|
67
90
|
this._lrShouldRecord = true;
|
|
68
91
|
this._lrPromise = null;
|
|
92
|
+
this._storeScope = null;
|
|
93
|
+
this._storeScopePromise = null;
|
|
69
94
|
|
|
70
95
|
if (options.logrocket) {
|
|
71
96
|
this.enableLogRocket(options.logrocket === true ? {} : options.logrocket);
|
|
@@ -268,6 +293,43 @@
|
|
|
268
293
|
return this._context?.token || null;
|
|
269
294
|
}
|
|
270
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Resolve the acting user's store scope — which stores they may access.
|
|
298
|
+
* Fetches GET /api/v1/auth/authenticate once and caches the result.
|
|
299
|
+
*
|
|
300
|
+
* Returns { unrestricted: boolean, store_ids: number[], source: string }.
|
|
301
|
+
* On any failure it returns a CLOSED default ({ unrestricted: false,
|
|
302
|
+
* store_ids: [], source: 'error' }) — fail closed, never accidentally
|
|
303
|
+
* grant access. Apps should still treat the server as the source of truth;
|
|
304
|
+
* this is for shaping the UI, not for security.
|
|
305
|
+
*/
|
|
306
|
+
async getStoreScope() {
|
|
307
|
+
if (this._storeScope) return this._storeScope;
|
|
308
|
+
if (this._storeScopePromise) return this._storeScopePromise;
|
|
309
|
+
this._storeScopePromise = this.apiGet('/api/v1/auth/authenticate')
|
|
310
|
+
.then((res) => {
|
|
311
|
+
const s = res && res.user && res.user.store_scope;
|
|
312
|
+
this._storeScope = normalizeStoreScope(s);
|
|
313
|
+
return this._storeScope;
|
|
314
|
+
})
|
|
315
|
+
.catch((e) => {
|
|
316
|
+
console.warn('HelixSDK: getStoreScope failed, defaulting to no access', e);
|
|
317
|
+
this._storeScope = { unrestricted: false, store_ids: [], source: 'error' };
|
|
318
|
+
return this._storeScope;
|
|
319
|
+
});
|
|
320
|
+
return this._storeScopePromise;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* True if the acting user may access the given store id. Resolves the
|
|
325
|
+
* store scope (cached) and checks it. Unrestricted users pass for any id.
|
|
326
|
+
*/
|
|
327
|
+
async canAccessStore(storeId) {
|
|
328
|
+
const scope = await this.getStoreScope();
|
|
329
|
+
if (scope.unrestricted) return true;
|
|
330
|
+
return scope.store_ids.indexOf(Number(storeId)) !== -1;
|
|
331
|
+
}
|
|
332
|
+
|
|
271
333
|
/** Make an authenticated GET request to the Helix API. */
|
|
272
334
|
async apiGet(path) {
|
|
273
335
|
return this._apiFetch('GET', path);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haven-team/helix-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "Client-side SDK for Helix Apps embedded via iframe — auth context, API helpers, URL/title sync, LogRocket session replay.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "helix-sdk.js",
|