@agnostack/next-shopify 4.0.0-canary.7 → 4.0.0-canary.8
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/dist/esm/lib/index.js +1 -0
- package/dist/esm/lib/utils/index.js +0 -1
- package/dist/esm/lib/utils/session.js +45 -13
- package/dist/esm/storage/firestore/index.js +77 -0
- package/dist/esm/storage/index.js +54 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/utils/index.d.ts +0 -1
- package/dist/lib/utils/index.js +0 -1
- package/dist/lib/utils/session.d.ts +3 -1
- package/dist/lib/utils/session.js +45 -13
- package/dist/storage/firestore/index.d.ts +4 -0
- package/dist/storage/firestore/index.js +81 -0
- package/dist/storage/index.d.ts +61 -0
- package/dist/storage/index.js +58 -0
- package/package.json +26 -1
- package/dist/esm/lib/utils/firebase.js +0 -265
- package/dist/lib/utils/firebase.d.ts +0 -31
- package/dist/lib/utils/firebase.js +0 -277
package/dist/esm/lib/index.js
CHANGED
|
@@ -12,10 +12,10 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { Base64 } from 'js-base64';
|
|
13
13
|
import { HttpResponseError, RequestedTokenType } from '@shopify/shopify-api';
|
|
14
14
|
import { isTrue, isString, isNumericOnly, ensureString, ensureArray, cleanObject, objectEmpty, stringEmpty, ensureObject, isTokenExpired, stringNotEmpty, objectNotEmpty, stringEmptyOnly, removeLeadingSlash, ShopifyAuthenticationError, ShopifySessionAuthenticationError, ShopifySessionMissingError, ShopifySessionInvalidError, SESSION_TOKEN_PARAM, APP_QUERY, TRANSFORM, } from '../../shared/index.js';
|
|
15
|
-
import {
|
|
15
|
+
import { ensureStorage } from '../../storage/index.js';
|
|
16
16
|
import { ensureBilling } from './billing.js';
|
|
17
|
+
import { replaceHeaders } from './headers.js';
|
|
17
18
|
import { getInitializedShopify } from './shopify.js';
|
|
18
|
-
import { loadState, storeState, deleteState, loadCallback, updateCallback, findSessionIdsByShop, deleteSessionsCallback, FirebaseSessionHandler, } from './firebase.js';
|
|
19
19
|
import { CryptoHandler } from './crypto.js';
|
|
20
20
|
// TODO!!!: keep in sync between next-shopify and lib-core
|
|
21
21
|
/** @param {any} [shopifyData] */
|
|
@@ -61,7 +61,39 @@ export const normalizeShopifyId = (shopifyData) => {
|
|
|
61
61
|
* @param {any} [config]
|
|
62
62
|
* @returns {Promise<import('./types.js').ShopifyHelpers>}
|
|
63
63
|
*/
|
|
64
|
-
|
|
64
|
+
// What may be persisted, decided HERE rather than in an implementation. Both write callbacks in
|
|
65
|
+
// the retired Firestore handler carried this rule, so every third-party store would have had to
|
|
66
|
+
// reimplement it - and any that missed it would quietly persist customer data.
|
|
67
|
+
//
|
|
68
|
+
// ⚠️ `associated_user` is destructured out for DPA compliance. The JSON round-trip strips
|
|
69
|
+
// prototypes and undefined values that a store may not accept.
|
|
70
|
+
/** @param {any} session */
|
|
71
|
+
const prepareSessionPayload = (session) => {
|
|
72
|
+
const _a = JSON.parse(JSON.stringify(session)), { onlineAccessInfo: _onlineAccessInfo } = _a, sessionData = __rest(_a, ["onlineAccessInfo"]);
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
74
|
+
const _b = _onlineAccessInfo !== null && _onlineAccessInfo !== void 0 ? _onlineAccessInfo : {}, { associated_user } = _b, onlineAccessInfo = __rest(_b, ["associated_user"]);
|
|
75
|
+
return Object.assign({ onlineAccessInfo }, sessionData);
|
|
76
|
+
};
|
|
77
|
+
// NOTE: imported lazily so an app that supplies its own storage never loads firebase-admin,
|
|
78
|
+
// even though it is still the default. PR 4 removes this branch and the peer with it.
|
|
79
|
+
/**
|
|
80
|
+
* @param {any} firebaseConfig
|
|
81
|
+
* @param {any} options
|
|
82
|
+
*/
|
|
83
|
+
const createDefaultStorage = async (firebaseConfig, options) => {
|
|
84
|
+
const { createFirestoreStorage } = await import('../../storage/firestore/index.js');
|
|
85
|
+
return createFirestoreStorage(firebaseConfig, options);
|
|
86
|
+
};
|
|
87
|
+
// TODO: move to curry??
|
|
88
|
+
/**
|
|
89
|
+
* @param {any} [serverRuntimeConfig]
|
|
90
|
+
* @param {any} [config]
|
|
91
|
+
* @param {{ storage?: import('../../storage/index.js').SessionStorage }} [options]
|
|
92
|
+
* `storage` defaults to the Firestore implementation, which is the only reason this package
|
|
93
|
+
* still declares firebase-admin.
|
|
94
|
+
* @returns {Promise<import('./types.js').ShopifyHelpers>}
|
|
95
|
+
*/
|
|
96
|
+
export const getShopifyHelpers = async (serverRuntimeConfig, config, { storage: _storage } = {}) => {
|
|
65
97
|
var _a, _b;
|
|
66
98
|
const shopify = getInitializedShopify(serverRuntimeConfig);
|
|
67
99
|
const { APP_ID, FB_CONFIG, FB_ROOT_COLLECTION, SHOPIFY_RUN_MODE, SESSION_CONFIG, PAYMENT_CONFIG, } = serverRuntimeConfig;
|
|
@@ -71,10 +103,10 @@ export const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
71
103
|
// TODO: should this use our helper instead (and/or as a fallback)??
|
|
72
104
|
const shopId = (_b = (_a = shopify === null || shopify === void 0 ? void 0 : shopify.utils) === null || _a === void 0 ? void 0 : _a.sanitizeShop) === null || _b === void 0 ? void 0 : _b.call(_a, shop);
|
|
73
105
|
const cryptoHandler = CryptoHandler.getInstance(SESSION_CONFIG);
|
|
74
|
-
const
|
|
106
|
+
const storage = ensureStorage(_storage !== null && _storage !== void 0 ? _storage : await createDefaultStorage(FB_CONFIG, {
|
|
75
107
|
collection: FB_ROOT_COLLECTION,
|
|
76
108
|
configurationId: APP_ID,
|
|
77
|
-
});
|
|
109
|
+
}));
|
|
78
110
|
// NOTE: this should handle running in next to test non-shopify functionality
|
|
79
111
|
if (!shopify) {
|
|
80
112
|
return {
|
|
@@ -174,7 +206,7 @@ export const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
174
206
|
/** @param {any} [session] */
|
|
175
207
|
const encryptUpdateSession = async (session) => {
|
|
176
208
|
if (stringNotEmpty(session === null || session === void 0 ? void 0 : session.id)) {
|
|
177
|
-
return
|
|
209
|
+
return storage.updateSession(shopId, prepareSessionPayload(ensureEncryptedSession(session)));
|
|
178
210
|
}
|
|
179
211
|
throw new ShopifySessionInvalidError('Invalid session');
|
|
180
212
|
};
|
|
@@ -201,7 +233,7 @@ export const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
201
233
|
rawResponse: res,
|
|
202
234
|
});
|
|
203
235
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
204
|
-
const _c = (_b = (await
|
|
236
|
+
const _c = (_b = (await storage.loadSession(shopId, sessionId))) !== null && _b !== void 0 ? _b : {}, { accessToken: encryptedOnlineAccessToken } = _c, storedOnlineSession = __rest(_c, ["accessToken"]);
|
|
205
237
|
let session = storedOnlineSession;
|
|
206
238
|
let accessToken = encryptedOnlineAccessToken;
|
|
207
239
|
let isEncrypted = true; // TODO: explore removing (isEncryptyed)
|
|
@@ -227,7 +259,7 @@ export const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
227
259
|
const verifyOfflineSession = async () => {
|
|
228
260
|
var _a;
|
|
229
261
|
const sessionId = shopify.session.getOfflineId(shop);
|
|
230
|
-
const _b = (_a = (await
|
|
262
|
+
const _b = (_a = (await storage.loadSession(shopId, sessionId))) !== null && _a !== void 0 ? _a : {}, { accessToken: encryptedOfflineAccessToken } = _b, offlineSession = __rest(_b, ["accessToken"]);
|
|
231
263
|
return {
|
|
232
264
|
sessionId,
|
|
233
265
|
session: offlineSession,
|
|
@@ -378,19 +410,19 @@ export const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
378
410
|
loadSession: decryptVerifySession,
|
|
379
411
|
storeSession: encryptStoreSession,
|
|
380
412
|
ensureBilling: handleEnsureBilling,
|
|
381
|
-
loadState: async () => loadState(
|
|
413
|
+
loadState: async () => storage.loadState(shop),
|
|
382
414
|
/** @param {any} state */
|
|
383
|
-
storeState: async (state) => storeState(
|
|
384
|
-
deleteState: async () => deleteState(
|
|
415
|
+
storeState: async (state) => storage.storeState(shop, state),
|
|
416
|
+
deleteState: async () => storage.deleteState(shop),
|
|
385
417
|
/**
|
|
386
418
|
* @param {any} sessionId
|
|
387
419
|
* @param {any} data
|
|
388
420
|
*/
|
|
389
421
|
updateSession: async (sessionId, data) => encryptUpdateSession(Object.assign(Object.assign({}, data), { id: sessionId })),
|
|
390
422
|
/** @param {any} sessionIds */
|
|
391
|
-
deleteSessions: async (sessionIds) =>
|
|
423
|
+
deleteSessions: async (sessionIds) => storage.deleteSessions(shopId, sessionIds),
|
|
392
424
|
/** @param {any} _shop */
|
|
393
|
-
findSessionIds: async (_shop) => { var _a, _b; return
|
|
425
|
+
findSessionIds: async (_shop) => { var _a, _b; return storage.findSessionIds((_b = (_a = shopify === null || shopify === void 0 ? void 0 : shopify.utils) === null || _a === void 0 ? void 0 : _a.sanitizeShop) === null || _b === void 0 ? void 0 : _b.call(_a, _shop)); },
|
|
394
426
|
};
|
|
395
427
|
};
|
|
396
428
|
/**
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { cert, getApp, getApps, initializeApp } from 'firebase-admin/app';
|
|
2
|
+
import { getFirestore } from 'firebase-admin/firestore';
|
|
3
|
+
import { ensureArray, stringEmpty, objectNotEmpty, COLLECTION_ROOT, STATE_PREFIX, } from '../../shared/index.js';
|
|
4
|
+
// firebase-admin allows one app per process, so an existing one is reused rather than replaced.
|
|
5
|
+
// NOTE: the retired handler also passed `databaseURL`, which configures the Realtime Database.
|
|
6
|
+
// Nothing here has ever used it - no `getDatabase`, no `firebase-admin/database` import.
|
|
7
|
+
/** @param {any} firebaseConfig */
|
|
8
|
+
const initializeFirebaseApp = (firebaseConfig) => (getApps().length
|
|
9
|
+
? getApp()
|
|
10
|
+
: initializeApp({ credential: cert(firebaseConfig) }));
|
|
11
|
+
/**
|
|
12
|
+
* The Firestore implementation of the session-storage contract, kept here so the core never
|
|
13
|
+
* imports firebase-admin. Sessions and state share one collection per shop; state is a single
|
|
14
|
+
* document per shop, prefixed so it cannot collide with a session id.
|
|
15
|
+
*
|
|
16
|
+
* @param {any} firebaseConfig a firebase service-account config
|
|
17
|
+
* @param {{ collection?: string, configurationId: string }} options
|
|
18
|
+
* @returns {import('../index.js').SessionStorage}
|
|
19
|
+
*/
|
|
20
|
+
export const createFirestoreStorage = (firebaseConfig, { collection = COLLECTION_ROOT, configurationId }) => {
|
|
21
|
+
if (!objectNotEmpty(firebaseConfig)) {
|
|
22
|
+
throw new Error('createFirestoreStorage requires a firebase service-account config');
|
|
23
|
+
}
|
|
24
|
+
const firestore = getFirestore(initializeFirebaseApp(firebaseConfig));
|
|
25
|
+
/** @param {any} shop */
|
|
26
|
+
const shopCollection = (shop) => (firestore
|
|
27
|
+
.collection(collection)
|
|
28
|
+
.doc(shop)
|
|
29
|
+
.collection(configurationId));
|
|
30
|
+
/** @param {any} shop */
|
|
31
|
+
const stateRef = (shop) => shopCollection(shop).doc(`${STATE_PREFIX}${shop}`);
|
|
32
|
+
/** @param {any} docRef */
|
|
33
|
+
const readDocument = async (docRef) => {
|
|
34
|
+
const document = await docRef.get();
|
|
35
|
+
return document.exists ? document.data() : undefined;
|
|
36
|
+
};
|
|
37
|
+
/** @type {import('../index.js').SessionStorage} */
|
|
38
|
+
const storage = {
|
|
39
|
+
loadSession: async (shopId, sessionId) => (stringEmpty(sessionId)
|
|
40
|
+
? undefined
|
|
41
|
+
: readDocument(shopCollection(shopId).doc(`${sessionId}`))),
|
|
42
|
+
// NOTE: merge rather than replace. The core writes partial sessions - a billing status
|
|
43
|
+
// update carries no access token - and a replacing write would drop the rest.
|
|
44
|
+
updateSession: async (shopId, session) => {
|
|
45
|
+
const docRef = shopCollection(shopId).doc(session.id);
|
|
46
|
+
await docRef.set(session, { merge: true });
|
|
47
|
+
return readDocument(docRef);
|
|
48
|
+
},
|
|
49
|
+
deleteSessions: async (shopId, sessionIds) => {
|
|
50
|
+
const collectionRef = shopCollection(shopId);
|
|
51
|
+
return Promise.all(ensureArray(sessionIds).map((sessionId) => collectionRef.doc(sessionId).delete()));
|
|
52
|
+
},
|
|
53
|
+
findSessionIds: async (shopId) => {
|
|
54
|
+
// NOTE: empty rather than an error - this is the state on an initial install
|
|
55
|
+
if (stringEmpty(shopId)) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
const documents = await shopCollection(shopId).get();
|
|
59
|
+
return documents.docs.map((/** @type {any} */ { id }) => id);
|
|
60
|
+
},
|
|
61
|
+
loadState: async (shop) => (stringEmpty(shop)
|
|
62
|
+
? undefined
|
|
63
|
+
: readDocument(stateRef(shop))),
|
|
64
|
+
storeState: async (shop, state) => {
|
|
65
|
+
if (stringEmpty(shop)) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const docRef = stateRef(shop);
|
|
69
|
+
await docRef.set(state);
|
|
70
|
+
return readDocument(docRef);
|
|
71
|
+
},
|
|
72
|
+
deleteState: async (shop) => (stringEmpty(shop)
|
|
73
|
+
? undefined
|
|
74
|
+
: stateRef(shop).delete()),
|
|
75
|
+
};
|
|
76
|
+
return storage;
|
|
77
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ensureArray } from '../shared/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shop and session ids are nullable: `sanitizeShop` returns null for anything it does not
|
|
4
|
+
* recognise, and the core forwards that rather than guarding.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {string | null | undefined} ShopId
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* What the session layer needs from a store. Two entities, sessions and state, both scoped to a
|
|
10
|
+
* shop. Nothing here is Firestore-specific: `@agnostack/next-shopify/firestore` is one
|
|
11
|
+
* implementation, and an app can supply its own.
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ Values arrive ALREADY ENCRYPTED and already stripped of anything that must not be persisted.
|
|
14
|
+
* An implementation stores and returns what it is handed - it must not inspect, reshape or
|
|
15
|
+
* re-encrypt, or the core's crypto and DPA guarantees stop holding.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ `findSessionIds` returns `[]` rather than erroring for an unknown shop - that is what an
|
|
18
|
+
* initial install looks like.
|
|
19
|
+
*
|
|
20
|
+
* @typedef {object} SessionStorage
|
|
21
|
+
* @property {(shopId: ShopId, sessionId: ShopId) => Promise<any>} loadSession
|
|
22
|
+
* @property {(shopId: ShopId, session: any) => Promise<any>} updateSession
|
|
23
|
+
* MERGES into the stored session. The core writes partial sessions, so a replacing write
|
|
24
|
+
* would silently drop fields it did not send.
|
|
25
|
+
* @property {(shopId: ShopId, sessionIds: any) => Promise<any>} deleteSessions
|
|
26
|
+
* @property {(shopId: ShopId) => Promise<string[]>} findSessionIds
|
|
27
|
+
* @property {(shop: ShopId) => Promise<any>} loadState
|
|
28
|
+
* @property {(shop: ShopId, state: any) => Promise<any>} storeState
|
|
29
|
+
* @property {(shop: ShopId) => Promise<any>} deleteState
|
|
30
|
+
*/
|
|
31
|
+
export const STORAGE_METHODS = [
|
|
32
|
+
'loadState',
|
|
33
|
+
'storeState',
|
|
34
|
+
'deleteState',
|
|
35
|
+
'loadSession',
|
|
36
|
+
'updateSession',
|
|
37
|
+
'deleteSessions',
|
|
38
|
+
'findSessionIds'
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Fails at composition rather than at the first OAuth round-trip. Nothing automated covers the
|
|
42
|
+
* Shopify auth flow, so a missing method would otherwise surface as a runtime TypeError in a
|
|
43
|
+
* callback that needs a real dev store to reach.
|
|
44
|
+
*
|
|
45
|
+
* @param {any} storage
|
|
46
|
+
* @returns {SessionStorage}
|
|
47
|
+
*/
|
|
48
|
+
export const ensureStorage = (storage) => {
|
|
49
|
+
const missing = ensureArray(STORAGE_METHODS).filter((method) => (typeof (storage === null || storage === void 0 ? void 0 : storage[method]) !== 'function'));
|
|
50
|
+
if (missing.length) {
|
|
51
|
+
throw new Error(`Invalid session storage, missing: ${missing.join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
return storage;
|
|
54
|
+
};
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.js
CHANGED
|
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
exports.prepareHeaders = exports.getShopifyHelpers = void 0;
|
|
18
18
|
__exportStar(require("../shared/index.js"), exports);
|
|
19
19
|
__exportStar(require("./handlers/index.js"), exports);
|
|
20
|
+
__exportStar(require("../storage/index.js"), exports);
|
|
20
21
|
var index_js_1 = require("./utils/index.js");
|
|
21
22
|
Object.defineProperty(exports, "getShopifyHelpers", { enumerable: true, get: function () { return index_js_1.getShopifyHelpers; } });
|
|
22
23
|
Object.defineProperty(exports, "prepareHeaders", { enumerable: true, get: function () { return index_js_1.prepareHeaders; } });
|
package/dist/lib/utils/index.js
CHANGED
|
@@ -16,7 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./billing.js"), exports);
|
|
18
18
|
__exportStar(require("./crypto.js"), exports);
|
|
19
|
-
__exportStar(require("./firebase.js"), exports);
|
|
20
19
|
__exportStar(require("./headers.js"), exports);
|
|
21
20
|
__exportStar(require("./json.js"), exports);
|
|
22
21
|
__exportStar(require("./session.js"), exports);
|
|
@@ -13,5 +13,7 @@ export function normalizeShopifyId(shopifyData?: any): {
|
|
|
13
13
|
entity_type?: string | undefined;
|
|
14
14
|
id?: string | undefined;
|
|
15
15
|
};
|
|
16
|
-
export function getShopifyHelpers(serverRuntimeConfig?: any, config?: any
|
|
16
|
+
export function getShopifyHelpers(serverRuntimeConfig?: any, config?: any, { storage: _storage }?: {
|
|
17
|
+
storage?: import("../../storage/index.js").SessionStorage;
|
|
18
|
+
}): Promise<import("./types.js").ShopifyHelpers>;
|
|
17
19
|
export function prepareHeaders(serverRuntimeConfig?: any): ({ generateReplacements, appData, shop, appId, headers: _headers, }?: any) => Promise<any>;
|
|
@@ -15,10 +15,10 @@ exports.prepareHeaders = exports.getShopifyHelpers = exports.normalizeShopifyId
|
|
|
15
15
|
const js_base64_1 = require("js-base64");
|
|
16
16
|
const shopify_api_1 = require("@shopify/shopify-api");
|
|
17
17
|
const index_js_1 = require("../../shared/index.js");
|
|
18
|
-
const
|
|
18
|
+
const index_js_2 = require("../../storage/index.js");
|
|
19
19
|
const billing_js_1 = require("./billing.js");
|
|
20
|
+
const headers_js_1 = require("./headers.js");
|
|
20
21
|
const shopify_js_1 = require("./shopify.js");
|
|
21
|
-
const firebase_js_1 = require("./firebase.js");
|
|
22
22
|
const crypto_js_1 = require("./crypto.js");
|
|
23
23
|
// TODO!!!: keep in sync between next-shopify and lib-core
|
|
24
24
|
/** @param {any} [shopifyData] */
|
|
@@ -65,7 +65,39 @@ exports.normalizeShopifyId = normalizeShopifyId;
|
|
|
65
65
|
* @param {any} [config]
|
|
66
66
|
* @returns {Promise<import('./types.js').ShopifyHelpers>}
|
|
67
67
|
*/
|
|
68
|
-
|
|
68
|
+
// What may be persisted, decided HERE rather than in an implementation. Both write callbacks in
|
|
69
|
+
// the retired Firestore handler carried this rule, so every third-party store would have had to
|
|
70
|
+
// reimplement it - and any that missed it would quietly persist customer data.
|
|
71
|
+
//
|
|
72
|
+
// ⚠️ `associated_user` is destructured out for DPA compliance. The JSON round-trip strips
|
|
73
|
+
// prototypes and undefined values that a store may not accept.
|
|
74
|
+
/** @param {any} session */
|
|
75
|
+
const prepareSessionPayload = (session) => {
|
|
76
|
+
const _a = JSON.parse(JSON.stringify(session)), { onlineAccessInfo: _onlineAccessInfo } = _a, sessionData = __rest(_a, ["onlineAccessInfo"]);
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
78
|
+
const _b = _onlineAccessInfo !== null && _onlineAccessInfo !== void 0 ? _onlineAccessInfo : {}, { associated_user } = _b, onlineAccessInfo = __rest(_b, ["associated_user"]);
|
|
79
|
+
return Object.assign({ onlineAccessInfo }, sessionData);
|
|
80
|
+
};
|
|
81
|
+
// NOTE: imported lazily so an app that supplies its own storage never loads firebase-admin,
|
|
82
|
+
// even though it is still the default. PR 4 removes this branch and the peer with it.
|
|
83
|
+
/**
|
|
84
|
+
* @param {any} firebaseConfig
|
|
85
|
+
* @param {any} options
|
|
86
|
+
*/
|
|
87
|
+
const createDefaultStorage = async (firebaseConfig, options) => {
|
|
88
|
+
const { createFirestoreStorage } = await import('../../storage/firestore/index.js');
|
|
89
|
+
return createFirestoreStorage(firebaseConfig, options);
|
|
90
|
+
};
|
|
91
|
+
// TODO: move to curry??
|
|
92
|
+
/**
|
|
93
|
+
* @param {any} [serverRuntimeConfig]
|
|
94
|
+
* @param {any} [config]
|
|
95
|
+
* @param {{ storage?: import('../../storage/index.js').SessionStorage }} [options]
|
|
96
|
+
* `storage` defaults to the Firestore implementation, which is the only reason this package
|
|
97
|
+
* still declares firebase-admin.
|
|
98
|
+
* @returns {Promise<import('./types.js').ShopifyHelpers>}
|
|
99
|
+
*/
|
|
100
|
+
const getShopifyHelpers = async (serverRuntimeConfig, config, { storage: _storage } = {}) => {
|
|
69
101
|
var _a, _b;
|
|
70
102
|
const shopify = (0, shopify_js_1.getInitializedShopify)(serverRuntimeConfig);
|
|
71
103
|
const { APP_ID, FB_CONFIG, FB_ROOT_COLLECTION, SHOPIFY_RUN_MODE, SESSION_CONFIG, PAYMENT_CONFIG, } = serverRuntimeConfig;
|
|
@@ -75,10 +107,10 @@ const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
75
107
|
// TODO: should this use our helper instead (and/or as a fallback)??
|
|
76
108
|
const shopId = (_b = (_a = shopify === null || shopify === void 0 ? void 0 : shopify.utils) === null || _a === void 0 ? void 0 : _a.sanitizeShop) === null || _b === void 0 ? void 0 : _b.call(_a, shop);
|
|
77
109
|
const cryptoHandler = crypto_js_1.CryptoHandler.getInstance(SESSION_CONFIG);
|
|
78
|
-
const
|
|
110
|
+
const storage = (0, index_js_2.ensureStorage)(_storage !== null && _storage !== void 0 ? _storage : await createDefaultStorage(FB_CONFIG, {
|
|
79
111
|
collection: FB_ROOT_COLLECTION,
|
|
80
112
|
configurationId: APP_ID,
|
|
81
|
-
});
|
|
113
|
+
}));
|
|
82
114
|
// NOTE: this should handle running in next to test non-shopify functionality
|
|
83
115
|
if (!shopify) {
|
|
84
116
|
return {
|
|
@@ -178,7 +210,7 @@ const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
178
210
|
/** @param {any} [session] */
|
|
179
211
|
const encryptUpdateSession = async (session) => {
|
|
180
212
|
if ((0, index_js_1.stringNotEmpty)(session === null || session === void 0 ? void 0 : session.id)) {
|
|
181
|
-
return
|
|
213
|
+
return storage.updateSession(shopId, prepareSessionPayload(ensureEncryptedSession(session)));
|
|
182
214
|
}
|
|
183
215
|
throw new index_js_1.ShopifySessionInvalidError('Invalid session');
|
|
184
216
|
};
|
|
@@ -205,7 +237,7 @@ const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
205
237
|
rawResponse: res,
|
|
206
238
|
});
|
|
207
239
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
208
|
-
const _c = (_b = (await
|
|
240
|
+
const _c = (_b = (await storage.loadSession(shopId, sessionId))) !== null && _b !== void 0 ? _b : {}, { accessToken: encryptedOnlineAccessToken } = _c, storedOnlineSession = __rest(_c, ["accessToken"]);
|
|
209
241
|
let session = storedOnlineSession;
|
|
210
242
|
let accessToken = encryptedOnlineAccessToken;
|
|
211
243
|
let isEncrypted = true; // TODO: explore removing (isEncryptyed)
|
|
@@ -231,7 +263,7 @@ const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
231
263
|
const verifyOfflineSession = async () => {
|
|
232
264
|
var _a;
|
|
233
265
|
const sessionId = shopify.session.getOfflineId(shop);
|
|
234
|
-
const _b = (_a = (await
|
|
266
|
+
const _b = (_a = (await storage.loadSession(shopId, sessionId))) !== null && _a !== void 0 ? _a : {}, { accessToken: encryptedOfflineAccessToken } = _b, offlineSession = __rest(_b, ["accessToken"]);
|
|
235
267
|
return {
|
|
236
268
|
sessionId,
|
|
237
269
|
session: offlineSession,
|
|
@@ -382,19 +414,19 @@ const getShopifyHelpers = async (serverRuntimeConfig, config) => {
|
|
|
382
414
|
loadSession: decryptVerifySession,
|
|
383
415
|
storeSession: encryptStoreSession,
|
|
384
416
|
ensureBilling: handleEnsureBilling,
|
|
385
|
-
loadState: async () =>
|
|
417
|
+
loadState: async () => storage.loadState(shop),
|
|
386
418
|
/** @param {any} state */
|
|
387
|
-
storeState: async (state) =>
|
|
388
|
-
deleteState: async () =>
|
|
419
|
+
storeState: async (state) => storage.storeState(shop, state),
|
|
420
|
+
deleteState: async () => storage.deleteState(shop),
|
|
389
421
|
/**
|
|
390
422
|
* @param {any} sessionId
|
|
391
423
|
* @param {any} data
|
|
392
424
|
*/
|
|
393
425
|
updateSession: async (sessionId, data) => encryptUpdateSession(Object.assign(Object.assign({}, data), { id: sessionId })),
|
|
394
426
|
/** @param {any} sessionIds */
|
|
395
|
-
deleteSessions: async (sessionIds) =>
|
|
427
|
+
deleteSessions: async (sessionIds) => storage.deleteSessions(shopId, sessionIds),
|
|
396
428
|
/** @param {any} _shop */
|
|
397
|
-
findSessionIds: async (_shop) => { var _a, _b; return
|
|
429
|
+
findSessionIds: async (_shop) => { var _a, _b; return storage.findSessionIds((_b = (_a = shopify === null || shopify === void 0 ? void 0 : shopify.utils) === null || _a === void 0 ? void 0 : _a.sanitizeShop) === null || _b === void 0 ? void 0 : _b.call(_a, _shop)); },
|
|
398
430
|
};
|
|
399
431
|
};
|
|
400
432
|
exports.getShopifyHelpers = getShopifyHelpers;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFirestoreStorage = void 0;
|
|
4
|
+
const app_1 = require("firebase-admin/app");
|
|
5
|
+
const firestore_1 = require("firebase-admin/firestore");
|
|
6
|
+
const index_js_1 = require("../../shared/index.js");
|
|
7
|
+
// firebase-admin allows one app per process, so an existing one is reused rather than replaced.
|
|
8
|
+
// NOTE: the retired handler also passed `databaseURL`, which configures the Realtime Database.
|
|
9
|
+
// Nothing here has ever used it - no `getDatabase`, no `firebase-admin/database` import.
|
|
10
|
+
/** @param {any} firebaseConfig */
|
|
11
|
+
const initializeFirebaseApp = (firebaseConfig) => ((0, app_1.getApps)().length
|
|
12
|
+
? (0, app_1.getApp)()
|
|
13
|
+
: (0, app_1.initializeApp)({ credential: (0, app_1.cert)(firebaseConfig) }));
|
|
14
|
+
/**
|
|
15
|
+
* The Firestore implementation of the session-storage contract, kept here so the core never
|
|
16
|
+
* imports firebase-admin. Sessions and state share one collection per shop; state is a single
|
|
17
|
+
* document per shop, prefixed so it cannot collide with a session id.
|
|
18
|
+
*
|
|
19
|
+
* @param {any} firebaseConfig a firebase service-account config
|
|
20
|
+
* @param {{ collection?: string, configurationId: string }} options
|
|
21
|
+
* @returns {import('../index.js').SessionStorage}
|
|
22
|
+
*/
|
|
23
|
+
const createFirestoreStorage = (firebaseConfig, { collection = index_js_1.COLLECTION_ROOT, configurationId }) => {
|
|
24
|
+
if (!(0, index_js_1.objectNotEmpty)(firebaseConfig)) {
|
|
25
|
+
throw new Error('createFirestoreStorage requires a firebase service-account config');
|
|
26
|
+
}
|
|
27
|
+
const firestore = (0, firestore_1.getFirestore)(initializeFirebaseApp(firebaseConfig));
|
|
28
|
+
/** @param {any} shop */
|
|
29
|
+
const shopCollection = (shop) => (firestore
|
|
30
|
+
.collection(collection)
|
|
31
|
+
.doc(shop)
|
|
32
|
+
.collection(configurationId));
|
|
33
|
+
/** @param {any} shop */
|
|
34
|
+
const stateRef = (shop) => shopCollection(shop).doc(`${index_js_1.STATE_PREFIX}${shop}`);
|
|
35
|
+
/** @param {any} docRef */
|
|
36
|
+
const readDocument = async (docRef) => {
|
|
37
|
+
const document = await docRef.get();
|
|
38
|
+
return document.exists ? document.data() : undefined;
|
|
39
|
+
};
|
|
40
|
+
/** @type {import('../index.js').SessionStorage} */
|
|
41
|
+
const storage = {
|
|
42
|
+
loadSession: async (shopId, sessionId) => ((0, index_js_1.stringEmpty)(sessionId)
|
|
43
|
+
? undefined
|
|
44
|
+
: readDocument(shopCollection(shopId).doc(`${sessionId}`))),
|
|
45
|
+
// NOTE: merge rather than replace. The core writes partial sessions - a billing status
|
|
46
|
+
// update carries no access token - and a replacing write would drop the rest.
|
|
47
|
+
updateSession: async (shopId, session) => {
|
|
48
|
+
const docRef = shopCollection(shopId).doc(session.id);
|
|
49
|
+
await docRef.set(session, { merge: true });
|
|
50
|
+
return readDocument(docRef);
|
|
51
|
+
},
|
|
52
|
+
deleteSessions: async (shopId, sessionIds) => {
|
|
53
|
+
const collectionRef = shopCollection(shopId);
|
|
54
|
+
return Promise.all((0, index_js_1.ensureArray)(sessionIds).map((sessionId) => collectionRef.doc(sessionId).delete()));
|
|
55
|
+
},
|
|
56
|
+
findSessionIds: async (shopId) => {
|
|
57
|
+
// NOTE: empty rather than an error - this is the state on an initial install
|
|
58
|
+
if ((0, index_js_1.stringEmpty)(shopId)) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const documents = await shopCollection(shopId).get();
|
|
62
|
+
return documents.docs.map((/** @type {any} */ { id }) => id);
|
|
63
|
+
},
|
|
64
|
+
loadState: async (shop) => ((0, index_js_1.stringEmpty)(shop)
|
|
65
|
+
? undefined
|
|
66
|
+
: readDocument(stateRef(shop))),
|
|
67
|
+
storeState: async (shop, state) => {
|
|
68
|
+
if ((0, index_js_1.stringEmpty)(shop)) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
const docRef = stateRef(shop);
|
|
72
|
+
await docRef.set(state);
|
|
73
|
+
return readDocument(docRef);
|
|
74
|
+
},
|
|
75
|
+
deleteState: async (shop) => ((0, index_js_1.stringEmpty)(shop)
|
|
76
|
+
? undefined
|
|
77
|
+
: stateRef(shop).delete()),
|
|
78
|
+
};
|
|
79
|
+
return storage;
|
|
80
|
+
};
|
|
81
|
+
exports.createFirestoreStorage = createFirestoreStorage;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shop and session ids are nullable: `sanitizeShop` returns null for anything it does not
|
|
3
|
+
* recognise, and the core forwards that rather than guarding.
|
|
4
|
+
*
|
|
5
|
+
* @typedef {string | null | undefined} ShopId
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* What the session layer needs from a store. Two entities, sessions and state, both scoped to a
|
|
9
|
+
* shop. Nothing here is Firestore-specific: `@agnostack/next-shopify/firestore` is one
|
|
10
|
+
* implementation, and an app can supply its own.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ Values arrive ALREADY ENCRYPTED and already stripped of anything that must not be persisted.
|
|
13
|
+
* An implementation stores and returns what it is handed - it must not inspect, reshape or
|
|
14
|
+
* re-encrypt, or the core's crypto and DPA guarantees stop holding.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ `findSessionIds` returns `[]` rather than erroring for an unknown shop - that is what an
|
|
17
|
+
* initial install looks like.
|
|
18
|
+
*
|
|
19
|
+
* @typedef {object} SessionStorage
|
|
20
|
+
* @property {(shopId: ShopId, sessionId: ShopId) => Promise<any>} loadSession
|
|
21
|
+
* @property {(shopId: ShopId, session: any) => Promise<any>} updateSession
|
|
22
|
+
* MERGES into the stored session. The core writes partial sessions, so a replacing write
|
|
23
|
+
* would silently drop fields it did not send.
|
|
24
|
+
* @property {(shopId: ShopId, sessionIds: any) => Promise<any>} deleteSessions
|
|
25
|
+
* @property {(shopId: ShopId) => Promise<string[]>} findSessionIds
|
|
26
|
+
* @property {(shop: ShopId) => Promise<any>} loadState
|
|
27
|
+
* @property {(shop: ShopId, state: any) => Promise<any>} storeState
|
|
28
|
+
* @property {(shop: ShopId) => Promise<any>} deleteState
|
|
29
|
+
*/
|
|
30
|
+
export const STORAGE_METHODS: string[];
|
|
31
|
+
export function ensureStorage(storage: any): SessionStorage;
|
|
32
|
+
/**
|
|
33
|
+
* Shop and session ids are nullable: `sanitizeShop` returns null for anything it does not
|
|
34
|
+
* recognise, and the core forwards that rather than guarding.
|
|
35
|
+
*/
|
|
36
|
+
export type ShopId = string | null | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* What the session layer needs from a store. Two entities, sessions and state, both scoped to a
|
|
39
|
+
* shop. Nothing here is Firestore-specific: `@agnostack/next-shopify/firestore` is one
|
|
40
|
+
* implementation, and an app can supply its own.
|
|
41
|
+
*
|
|
42
|
+
* ⚠️ Values arrive ALREADY ENCRYPTED and already stripped of anything that must not be persisted.
|
|
43
|
+
* An implementation stores and returns what it is handed - it must not inspect, reshape or
|
|
44
|
+
* re-encrypt, or the core's crypto and DPA guarantees stop holding.
|
|
45
|
+
*
|
|
46
|
+
* ⚠️ `findSessionIds` returns `[]` rather than erroring for an unknown shop - that is what an
|
|
47
|
+
* initial install looks like.
|
|
48
|
+
*/
|
|
49
|
+
export type SessionStorage = {
|
|
50
|
+
loadSession: (shopId: ShopId, sessionId: ShopId) => Promise<any>;
|
|
51
|
+
/**
|
|
52
|
+
* MERGES into the stored session. The core writes partial sessions, so a replacing write
|
|
53
|
+
* would silently drop fields it did not send.
|
|
54
|
+
*/
|
|
55
|
+
updateSession: (shopId: ShopId, session: any) => Promise<any>;
|
|
56
|
+
deleteSessions: (shopId: ShopId, sessionIds: any) => Promise<any>;
|
|
57
|
+
findSessionIds: (shopId: ShopId) => Promise<string[]>;
|
|
58
|
+
loadState: (shop: ShopId) => Promise<any>;
|
|
59
|
+
storeState: (shop: ShopId, state: any) => Promise<any>;
|
|
60
|
+
deleteState: (shop: ShopId) => Promise<any>;
|
|
61
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureStorage = exports.STORAGE_METHODS = void 0;
|
|
4
|
+
const index_js_1 = require("../shared/index.js");
|
|
5
|
+
/**
|
|
6
|
+
* Shop and session ids are nullable: `sanitizeShop` returns null for anything it does not
|
|
7
|
+
* recognise, and the core forwards that rather than guarding.
|
|
8
|
+
*
|
|
9
|
+
* @typedef {string | null | undefined} ShopId
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* What the session layer needs from a store. Two entities, sessions and state, both scoped to a
|
|
13
|
+
* shop. Nothing here is Firestore-specific: `@agnostack/next-shopify/firestore` is one
|
|
14
|
+
* implementation, and an app can supply its own.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ Values arrive ALREADY ENCRYPTED and already stripped of anything that must not be persisted.
|
|
17
|
+
* An implementation stores and returns what it is handed - it must not inspect, reshape or
|
|
18
|
+
* re-encrypt, or the core's crypto and DPA guarantees stop holding.
|
|
19
|
+
*
|
|
20
|
+
* ⚠️ `findSessionIds` returns `[]` rather than erroring for an unknown shop - that is what an
|
|
21
|
+
* initial install looks like.
|
|
22
|
+
*
|
|
23
|
+
* @typedef {object} SessionStorage
|
|
24
|
+
* @property {(shopId: ShopId, sessionId: ShopId) => Promise<any>} loadSession
|
|
25
|
+
* @property {(shopId: ShopId, session: any) => Promise<any>} updateSession
|
|
26
|
+
* MERGES into the stored session. The core writes partial sessions, so a replacing write
|
|
27
|
+
* would silently drop fields it did not send.
|
|
28
|
+
* @property {(shopId: ShopId, sessionIds: any) => Promise<any>} deleteSessions
|
|
29
|
+
* @property {(shopId: ShopId) => Promise<string[]>} findSessionIds
|
|
30
|
+
* @property {(shop: ShopId) => Promise<any>} loadState
|
|
31
|
+
* @property {(shop: ShopId, state: any) => Promise<any>} storeState
|
|
32
|
+
* @property {(shop: ShopId) => Promise<any>} deleteState
|
|
33
|
+
*/
|
|
34
|
+
exports.STORAGE_METHODS = [
|
|
35
|
+
'loadState',
|
|
36
|
+
'storeState',
|
|
37
|
+
'deleteState',
|
|
38
|
+
'loadSession',
|
|
39
|
+
'updateSession',
|
|
40
|
+
'deleteSessions',
|
|
41
|
+
'findSessionIds'
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* Fails at composition rather than at the first OAuth round-trip. Nothing automated covers the
|
|
45
|
+
* Shopify auth flow, so a missing method would otherwise surface as a runtime TypeError in a
|
|
46
|
+
* callback that needs a real dev store to reach.
|
|
47
|
+
*
|
|
48
|
+
* @param {any} storage
|
|
49
|
+
* @returns {SessionStorage}
|
|
50
|
+
*/
|
|
51
|
+
const ensureStorage = (storage) => {
|
|
52
|
+
const missing = (0, index_js_1.ensureArray)(exports.STORAGE_METHODS).filter((method) => (typeof (storage === null || storage === void 0 ? void 0 : storage[method]) !== 'function'));
|
|
53
|
+
if (missing.length) {
|
|
54
|
+
throw new Error(`Invalid session storage, missing: ${missing.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
return storage;
|
|
57
|
+
};
|
|
58
|
+
exports.ensureStorage = ensureStorage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agnostack/next-shopify",
|
|
3
|
-
"version": "4.0.0-canary.
|
|
3
|
+
"version": "4.0.0-canary.8",
|
|
4
4
|
"author": "agnoStack Dev <developers@agnostack.com> (https://agnostack.com)",
|
|
5
5
|
"owner": "agnoStack",
|
|
6
6
|
"description": "Please contact agnoStack via info@agnostack.com for any questions",
|
|
@@ -39,6 +39,28 @@
|
|
|
39
39
|
"import": "./dist/esm/lib/index.js",
|
|
40
40
|
"default": "./dist/esm/lib/index.js"
|
|
41
41
|
},
|
|
42
|
+
"./storage": {
|
|
43
|
+
"types": "./dist/storage/index.d.ts",
|
|
44
|
+
"require": "./dist/storage/index.js",
|
|
45
|
+
"import": "./dist/storage/index.js",
|
|
46
|
+
"default": "./dist/storage/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./storage/esm": {
|
|
49
|
+
"types": "./dist/storage/index.d.ts",
|
|
50
|
+
"import": "./dist/esm/storage/index.js",
|
|
51
|
+
"default": "./dist/esm/storage/index.js"
|
|
52
|
+
},
|
|
53
|
+
"./storage/firestore": {
|
|
54
|
+
"types": "./dist/storage/firestore/index.d.ts",
|
|
55
|
+
"require": "./dist/storage/firestore/index.js",
|
|
56
|
+
"import": "./dist/storage/firestore/index.js",
|
|
57
|
+
"default": "./dist/storage/firestore/index.js"
|
|
58
|
+
},
|
|
59
|
+
"./storage/firestore/esm": {
|
|
60
|
+
"types": "./dist/storage/firestore/index.d.ts",
|
|
61
|
+
"import": "./dist/esm/storage/firestore/index.js",
|
|
62
|
+
"default": "./dist/esm/storage/firestore/index.js"
|
|
63
|
+
},
|
|
42
64
|
"./runtime": {
|
|
43
65
|
"types": "./dist/runtime/index.d.ts",
|
|
44
66
|
"require": "./dist/runtime/index.js",
|
|
@@ -153,6 +175,9 @@
|
|
|
153
175
|
"prompts": ">=2.4.0"
|
|
154
176
|
},
|
|
155
177
|
"peerDependenciesMeta": {
|
|
178
|
+
"firebase-admin": {
|
|
179
|
+
"optional": true
|
|
180
|
+
},
|
|
156
181
|
"next": {
|
|
157
182
|
"optional": true
|
|
158
183
|
},
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
-
var t = {};
|
|
3
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
-
t[p] = s[p];
|
|
5
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
-
t[p[i]] = s[p[i]];
|
|
9
|
-
}
|
|
10
|
-
return t;
|
|
11
|
-
};
|
|
12
|
-
import { cert, getApp, getApps, initializeApp } from 'firebase-admin/app';
|
|
13
|
-
import { getFirestore } from 'firebase-admin/firestore';
|
|
14
|
-
import { ensureArray, stringEmpty, objectNotEmpty, COLLECTION_ROOT, STATE_PREFIX, } from '../../shared/index.js';
|
|
15
|
-
const singleton = Symbol('singleton');
|
|
16
|
-
const singletonEnforcer = Symbol('singletonEnforcer');
|
|
17
|
-
export class FirebaseSessionHandler {
|
|
18
|
-
/**
|
|
19
|
-
* @param {any} enforcer
|
|
20
|
-
* @param {any} _param1
|
|
21
|
-
*/
|
|
22
|
-
constructor(enforcer, { configurationId, collection = COLLECTION_ROOT }) {
|
|
23
|
-
if (enforcer !== singletonEnforcer) {
|
|
24
|
-
throw new Error('Cannot instantiate FirebaseSessionHandler directly, please use getInstance()');
|
|
25
|
-
}
|
|
26
|
-
this.type = 'FirebaseSessionHandler';
|
|
27
|
-
this.collection = collection;
|
|
28
|
-
this.configurationId = configurationId;
|
|
29
|
-
}
|
|
30
|
-
get isInitialized() {
|
|
31
|
-
return (this.firestore != undefined);
|
|
32
|
-
}
|
|
33
|
-
/** @param {any} [firebaseConfig] */
|
|
34
|
-
initializeFireStore(firebaseConfig) {
|
|
35
|
-
if (objectNotEmpty(firebaseConfig)) {
|
|
36
|
-
const project = getApps().length
|
|
37
|
-
? getApp()
|
|
38
|
-
: initializeApp({
|
|
39
|
-
credential: cert(firebaseConfig),
|
|
40
|
-
databaseURL: `https://${firebaseConfig.project_id}.firebaseio.com`,
|
|
41
|
-
});
|
|
42
|
-
this.firestore = getFirestore(project);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* @param {any} firebaseConfig
|
|
47
|
-
* @param {any} _param1
|
|
48
|
-
* @this {any}
|
|
49
|
-
*/
|
|
50
|
-
static getInstance(firebaseConfig, { collection, configurationId }) {
|
|
51
|
-
if (!this[singleton]) {
|
|
52
|
-
this[singleton] = new FirebaseSessionHandler(singletonEnforcer, { collection, configurationId });
|
|
53
|
-
}
|
|
54
|
-
if (!this[singleton].isInitialized) {
|
|
55
|
-
this[singleton].initializeFireStore(firebaseConfig);
|
|
56
|
-
}
|
|
57
|
-
return this[singleton];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* @param {any} [instance]
|
|
62
|
-
* @param {any} [shopId]
|
|
63
|
-
* @param {any} [session]
|
|
64
|
-
*/
|
|
65
|
-
export const storeCallback = async (instance, shopId, session) => {
|
|
66
|
-
try {
|
|
67
|
-
const docRef = instance.firestore
|
|
68
|
-
.collection(instance.collection)
|
|
69
|
-
.doc(shopId)
|
|
70
|
-
.collection(instance.configurationId)
|
|
71
|
-
.doc(session.id);
|
|
72
|
-
// HMMMM why the freeze here?
|
|
73
|
-
// TODO: do we need to remove/can we remove session.id here??
|
|
74
|
-
const _a = JSON.parse(JSON.stringify(session)), { onlineAccessInfo: _onlineAccessInfo } = _a, docData = __rest(_a, ["onlineAccessInfo"]);
|
|
75
|
-
// NOTE: destructuring out associated_user for DPA compliance
|
|
76
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
77
|
-
const _b = _onlineAccessInfo !== null && _onlineAccessInfo !== void 0 ? _onlineAccessInfo : {}, { associated_user } = _b, onlineAccessInfo = __rest(_b, ["associated_user"]);
|
|
78
|
-
const payload = Object.assign({ onlineAccessInfo }, docData);
|
|
79
|
-
await docRef.set(payload);
|
|
80
|
-
return (await docRef.get()).data();
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
console.error('FirebaseSessionHandler.storeCallback error', error);
|
|
84
|
-
throw error;
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
* @param {any} [instance]
|
|
89
|
-
* @param {any} [shopId]
|
|
90
|
-
* @param {any} [session]
|
|
91
|
-
*/
|
|
92
|
-
export const updateCallback = async (instance, shopId, session) => {
|
|
93
|
-
try {
|
|
94
|
-
const docRef = instance.firestore
|
|
95
|
-
.collection(instance.collection)
|
|
96
|
-
.doc(shopId)
|
|
97
|
-
.collection(instance.configurationId)
|
|
98
|
-
.doc(session.id);
|
|
99
|
-
// HMMMM why the freeze here?
|
|
100
|
-
// TODO: do we need to remove/can we remove session.id here??
|
|
101
|
-
const _a = JSON.parse(JSON.stringify(session)), { onlineAccessInfo: _onlineAccessInfo } = _a, docData = __rest(_a, ["onlineAccessInfo"]);
|
|
102
|
-
// NOTE: destructuring out associated_user for DPA compliance
|
|
103
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
104
|
-
const _b = _onlineAccessInfo !== null && _onlineAccessInfo !== void 0 ? _onlineAccessInfo : {}, { associated_user } = _b, onlineAccessInfo = __rest(_b, ["associated_user"]);
|
|
105
|
-
const payload = Object.assign({ onlineAccessInfo }, docData);
|
|
106
|
-
await docRef.set(payload, { merge: true });
|
|
107
|
-
return (await docRef.get()).data();
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
console.error('FirebaseSessionHandler.updateCallback error', error);
|
|
111
|
-
throw error;
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
/**
|
|
115
|
-
* @param {any} [instance]
|
|
116
|
-
* @param {any} [shopId]
|
|
117
|
-
* @param {any} [sessionId]
|
|
118
|
-
*/
|
|
119
|
-
export const loadCallback = async (instance, shopId, sessionId) => {
|
|
120
|
-
if (stringEmpty(sessionId)) {
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
const document = await instance.firestore
|
|
125
|
-
.collection(instance.collection)
|
|
126
|
-
.doc(shopId)
|
|
127
|
-
.collection(instance.configurationId)
|
|
128
|
-
.doc(sessionId)
|
|
129
|
-
.get();
|
|
130
|
-
if (document.exists) {
|
|
131
|
-
return document.data();
|
|
132
|
-
}
|
|
133
|
-
return undefined;
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
console.error('FirebaseSessionHandler.loadCallback error', error);
|
|
137
|
-
throw error;
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
/**
|
|
141
|
-
* @param {any} [instance]
|
|
142
|
-
* @param {any} [shop]
|
|
143
|
-
*/
|
|
144
|
-
export const loadState = async (instance, shop) => {
|
|
145
|
-
if (stringEmpty(shop)) {
|
|
146
|
-
return undefined;
|
|
147
|
-
}
|
|
148
|
-
try {
|
|
149
|
-
const document = await instance.firestore
|
|
150
|
-
.collection(instance.collection)
|
|
151
|
-
.doc(shop)
|
|
152
|
-
.collection(instance.configurationId)
|
|
153
|
-
.doc(`${STATE_PREFIX}${shop}`)
|
|
154
|
-
.get();
|
|
155
|
-
if (document.exists) {
|
|
156
|
-
return document.data();
|
|
157
|
-
}
|
|
158
|
-
return undefined;
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
console.error('FirebaseSessionHandler.loadState error', error);
|
|
162
|
-
throw error;
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
/**
|
|
166
|
-
* @param {any} [instance]
|
|
167
|
-
* @param {any} [state]
|
|
168
|
-
*/
|
|
169
|
-
export const storeState = async (instance, state) => {
|
|
170
|
-
if (stringEmpty(state === null || state === void 0 ? void 0 : state.shop)) {
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
try {
|
|
174
|
-
const docRef = instance.firestore
|
|
175
|
-
.collection(instance.collection)
|
|
176
|
-
.doc(state.shop)
|
|
177
|
-
.collection(instance.configurationId)
|
|
178
|
-
.doc(`${STATE_PREFIX}${state.shop}`);
|
|
179
|
-
await docRef.set(state);
|
|
180
|
-
return (await docRef.get()).data();
|
|
181
|
-
}
|
|
182
|
-
catch (error) {
|
|
183
|
-
console.error('FirebaseSessionHandler.storeState error', error);
|
|
184
|
-
throw error;
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
/**
|
|
188
|
-
* @param {any} [instance]
|
|
189
|
-
* @param {any} [shop]
|
|
190
|
-
*/
|
|
191
|
-
export const deleteState = async (instance, shop) => {
|
|
192
|
-
if (stringEmpty(shop)) {
|
|
193
|
-
return undefined;
|
|
194
|
-
}
|
|
195
|
-
try {
|
|
196
|
-
const docRef = instance.firestore
|
|
197
|
-
.collection(instance.collection)
|
|
198
|
-
.doc(shop)
|
|
199
|
-
.collection(instance.configurationId)
|
|
200
|
-
.doc(`${STATE_PREFIX}${shop}`);
|
|
201
|
-
return docRef.delete();
|
|
202
|
-
}
|
|
203
|
-
catch (error) {
|
|
204
|
-
console.error('FirebaseSessionHandler.deleteState error', error);
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
/**
|
|
209
|
-
* @param {any} [instance]
|
|
210
|
-
* @param {any} [shopId]
|
|
211
|
-
* @param {any} [sessionIds]
|
|
212
|
-
*/
|
|
213
|
-
export const deleteSessionsCallback = async (instance, shopId, sessionIds) => {
|
|
214
|
-
/**
|
|
215
|
-
* @param {any} [_collectionRef]
|
|
216
|
-
* @param {any} [_sessionId]
|
|
217
|
-
*/
|
|
218
|
-
const deleteSessionCallback = async (_collectionRef, _sessionId) => {
|
|
219
|
-
try {
|
|
220
|
-
return _collectionRef.doc(_sessionId).delete();
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
console.error('FirebaseSessionHandler.deleteSessionCallback error', error);
|
|
224
|
-
throw error;
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
try {
|
|
228
|
-
const collectionRef = instance.firestore
|
|
229
|
-
.collection(instance.collection)
|
|
230
|
-
.doc(shopId)
|
|
231
|
-
.collection(instance.configurationId);
|
|
232
|
-
return Promise.all(ensureArray(sessionIds).map((sessionId) => deleteSessionCallback(collectionRef, sessionId)));
|
|
233
|
-
}
|
|
234
|
-
catch (error) {
|
|
235
|
-
console.error('FirebaseSessionHandler.deleteSessionsCallback error', error);
|
|
236
|
-
throw error;
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
/**
|
|
240
|
-
* @param {any} [instance]
|
|
241
|
-
* @param {any} [shopId]
|
|
242
|
-
*/
|
|
243
|
-
export const findSessionIdsByShop = async (instance, shopId) => {
|
|
244
|
-
if (stringEmpty(shopId)) {
|
|
245
|
-
// NOTE: this happens on initial install
|
|
246
|
-
return [];
|
|
247
|
-
}
|
|
248
|
-
try {
|
|
249
|
-
const collection = await instance.firestore
|
|
250
|
-
.collection(instance.collection)
|
|
251
|
-
.doc(shopId)
|
|
252
|
-
.collection(instance.configurationId)
|
|
253
|
-
.get();
|
|
254
|
-
/** @type {string[]} */
|
|
255
|
-
const sessionIds = [];
|
|
256
|
-
collection.forEach((/** @type {any} */ _doc) => {
|
|
257
|
-
sessionIds.push(_doc.id);
|
|
258
|
-
});
|
|
259
|
-
return sessionIds;
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
console.error('FirebaseSessionHandler.findSessionIdsByShop error', error);
|
|
263
|
-
throw error;
|
|
264
|
-
}
|
|
265
|
-
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export class FirebaseSessionHandler {
|
|
2
|
-
/**
|
|
3
|
-
* @param {any} firebaseConfig
|
|
4
|
-
* @param {any} _param1
|
|
5
|
-
* @this {any}
|
|
6
|
-
*/
|
|
7
|
-
static getInstance(this: any, firebaseConfig: any, { collection, configurationId }: any): any;
|
|
8
|
-
/**
|
|
9
|
-
* @param {any} enforcer
|
|
10
|
-
* @param {any} _param1
|
|
11
|
-
*/
|
|
12
|
-
constructor(enforcer: any, { configurationId, collection }: any);
|
|
13
|
-
type: string;
|
|
14
|
-
collection: any;
|
|
15
|
-
configurationId: any;
|
|
16
|
-
get isInitialized(): boolean;
|
|
17
|
-
/** @param {any} [firebaseConfig] */
|
|
18
|
-
initializeFireStore(firebaseConfig?: any): void;
|
|
19
|
-
firestore: FirebaseFirestore.Firestore | undefined;
|
|
20
|
-
[singleton]: FirebaseSessionHandler | undefined;
|
|
21
|
-
}
|
|
22
|
-
export function storeCallback(instance?: any, shopId?: any, session?: any): Promise<any>;
|
|
23
|
-
export function updateCallback(instance?: any, shopId?: any, session?: any): Promise<any>;
|
|
24
|
-
export function loadCallback(instance?: any, shopId?: any, sessionId?: any): Promise<any>;
|
|
25
|
-
export function loadState(instance?: any, shop?: any): Promise<any>;
|
|
26
|
-
export function storeState(instance?: any, state?: any): Promise<any>;
|
|
27
|
-
export function deleteState(instance?: any, shop?: any): Promise<any>;
|
|
28
|
-
export function deleteSessionsCallback(instance?: any, shopId?: any, sessionIds?: any): Promise<any[]>;
|
|
29
|
-
export function findSessionIdsByShop(instance?: any, shopId?: any): Promise<string[]>;
|
|
30
|
-
declare const singleton: unique symbol;
|
|
31
|
-
export {};
|
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
-
var t = {};
|
|
4
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
-
t[p] = s[p];
|
|
6
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
-
t[p[i]] = s[p[i]];
|
|
10
|
-
}
|
|
11
|
-
return t;
|
|
12
|
-
};
|
|
13
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.findSessionIdsByShop = exports.deleteSessionsCallback = exports.deleteState = exports.storeState = exports.loadState = exports.loadCallback = exports.updateCallback = exports.storeCallback = exports.FirebaseSessionHandler = void 0;
|
|
15
|
-
const app_1 = require("firebase-admin/app");
|
|
16
|
-
const firestore_1 = require("firebase-admin/firestore");
|
|
17
|
-
const index_js_1 = require("../../shared/index.js");
|
|
18
|
-
const singleton = Symbol('singleton');
|
|
19
|
-
const singletonEnforcer = Symbol('singletonEnforcer');
|
|
20
|
-
class FirebaseSessionHandler {
|
|
21
|
-
/**
|
|
22
|
-
* @param {any} enforcer
|
|
23
|
-
* @param {any} _param1
|
|
24
|
-
*/
|
|
25
|
-
constructor(enforcer, { configurationId, collection = index_js_1.COLLECTION_ROOT }) {
|
|
26
|
-
if (enforcer !== singletonEnforcer) {
|
|
27
|
-
throw new Error('Cannot instantiate FirebaseSessionHandler directly, please use getInstance()');
|
|
28
|
-
}
|
|
29
|
-
this.type = 'FirebaseSessionHandler';
|
|
30
|
-
this.collection = collection;
|
|
31
|
-
this.configurationId = configurationId;
|
|
32
|
-
}
|
|
33
|
-
get isInitialized() {
|
|
34
|
-
return (this.firestore != undefined);
|
|
35
|
-
}
|
|
36
|
-
/** @param {any} [firebaseConfig] */
|
|
37
|
-
initializeFireStore(firebaseConfig) {
|
|
38
|
-
if ((0, index_js_1.objectNotEmpty)(firebaseConfig)) {
|
|
39
|
-
const project = (0, app_1.getApps)().length
|
|
40
|
-
? (0, app_1.getApp)()
|
|
41
|
-
: (0, app_1.initializeApp)({
|
|
42
|
-
credential: (0, app_1.cert)(firebaseConfig),
|
|
43
|
-
databaseURL: `https://${firebaseConfig.project_id}.firebaseio.com`,
|
|
44
|
-
});
|
|
45
|
-
this.firestore = (0, firestore_1.getFirestore)(project);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* @param {any} firebaseConfig
|
|
50
|
-
* @param {any} _param1
|
|
51
|
-
* @this {any}
|
|
52
|
-
*/
|
|
53
|
-
static getInstance(firebaseConfig, { collection, configurationId }) {
|
|
54
|
-
if (!this[singleton]) {
|
|
55
|
-
this[singleton] = new FirebaseSessionHandler(singletonEnforcer, { collection, configurationId });
|
|
56
|
-
}
|
|
57
|
-
if (!this[singleton].isInitialized) {
|
|
58
|
-
this[singleton].initializeFireStore(firebaseConfig);
|
|
59
|
-
}
|
|
60
|
-
return this[singleton];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
exports.FirebaseSessionHandler = FirebaseSessionHandler;
|
|
64
|
-
/**
|
|
65
|
-
* @param {any} [instance]
|
|
66
|
-
* @param {any} [shopId]
|
|
67
|
-
* @param {any} [session]
|
|
68
|
-
*/
|
|
69
|
-
const storeCallback = async (instance, shopId, session) => {
|
|
70
|
-
try {
|
|
71
|
-
const docRef = instance.firestore
|
|
72
|
-
.collection(instance.collection)
|
|
73
|
-
.doc(shopId)
|
|
74
|
-
.collection(instance.configurationId)
|
|
75
|
-
.doc(session.id);
|
|
76
|
-
// HMMMM why the freeze here?
|
|
77
|
-
// TODO: do we need to remove/can we remove session.id here??
|
|
78
|
-
const _a = JSON.parse(JSON.stringify(session)), { onlineAccessInfo: _onlineAccessInfo } = _a, docData = __rest(_a, ["onlineAccessInfo"]);
|
|
79
|
-
// NOTE: destructuring out associated_user for DPA compliance
|
|
80
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
81
|
-
const _b = _onlineAccessInfo !== null && _onlineAccessInfo !== void 0 ? _onlineAccessInfo : {}, { associated_user } = _b, onlineAccessInfo = __rest(_b, ["associated_user"]);
|
|
82
|
-
const payload = Object.assign({ onlineAccessInfo }, docData);
|
|
83
|
-
await docRef.set(payload);
|
|
84
|
-
return (await docRef.get()).data();
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
console.error('FirebaseSessionHandler.storeCallback error', error);
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
exports.storeCallback = storeCallback;
|
|
92
|
-
/**
|
|
93
|
-
* @param {any} [instance]
|
|
94
|
-
* @param {any} [shopId]
|
|
95
|
-
* @param {any} [session]
|
|
96
|
-
*/
|
|
97
|
-
const updateCallback = async (instance, shopId, session) => {
|
|
98
|
-
try {
|
|
99
|
-
const docRef = instance.firestore
|
|
100
|
-
.collection(instance.collection)
|
|
101
|
-
.doc(shopId)
|
|
102
|
-
.collection(instance.configurationId)
|
|
103
|
-
.doc(session.id);
|
|
104
|
-
// HMMMM why the freeze here?
|
|
105
|
-
// TODO: do we need to remove/can we remove session.id here??
|
|
106
|
-
const _a = JSON.parse(JSON.stringify(session)), { onlineAccessInfo: _onlineAccessInfo } = _a, docData = __rest(_a, ["onlineAccessInfo"]);
|
|
107
|
-
// NOTE: destructuring out associated_user for DPA compliance
|
|
108
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
109
|
-
const _b = _onlineAccessInfo !== null && _onlineAccessInfo !== void 0 ? _onlineAccessInfo : {}, { associated_user } = _b, onlineAccessInfo = __rest(_b, ["associated_user"]);
|
|
110
|
-
const payload = Object.assign({ onlineAccessInfo }, docData);
|
|
111
|
-
await docRef.set(payload, { merge: true });
|
|
112
|
-
return (await docRef.get()).data();
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
console.error('FirebaseSessionHandler.updateCallback error', error);
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
exports.updateCallback = updateCallback;
|
|
120
|
-
/**
|
|
121
|
-
* @param {any} [instance]
|
|
122
|
-
* @param {any} [shopId]
|
|
123
|
-
* @param {any} [sessionId]
|
|
124
|
-
*/
|
|
125
|
-
const loadCallback = async (instance, shopId, sessionId) => {
|
|
126
|
-
if ((0, index_js_1.stringEmpty)(sessionId)) {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
try {
|
|
130
|
-
const document = await instance.firestore
|
|
131
|
-
.collection(instance.collection)
|
|
132
|
-
.doc(shopId)
|
|
133
|
-
.collection(instance.configurationId)
|
|
134
|
-
.doc(sessionId)
|
|
135
|
-
.get();
|
|
136
|
-
if (document.exists) {
|
|
137
|
-
return document.data();
|
|
138
|
-
}
|
|
139
|
-
return undefined;
|
|
140
|
-
}
|
|
141
|
-
catch (error) {
|
|
142
|
-
console.error('FirebaseSessionHandler.loadCallback error', error);
|
|
143
|
-
throw error;
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
exports.loadCallback = loadCallback;
|
|
147
|
-
/**
|
|
148
|
-
* @param {any} [instance]
|
|
149
|
-
* @param {any} [shop]
|
|
150
|
-
*/
|
|
151
|
-
const loadState = async (instance, shop) => {
|
|
152
|
-
if ((0, index_js_1.stringEmpty)(shop)) {
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
try {
|
|
156
|
-
const document = await instance.firestore
|
|
157
|
-
.collection(instance.collection)
|
|
158
|
-
.doc(shop)
|
|
159
|
-
.collection(instance.configurationId)
|
|
160
|
-
.doc(`${index_js_1.STATE_PREFIX}${shop}`)
|
|
161
|
-
.get();
|
|
162
|
-
if (document.exists) {
|
|
163
|
-
return document.data();
|
|
164
|
-
}
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
console.error('FirebaseSessionHandler.loadState error', error);
|
|
169
|
-
throw error;
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
exports.loadState = loadState;
|
|
173
|
-
/**
|
|
174
|
-
* @param {any} [instance]
|
|
175
|
-
* @param {any} [state]
|
|
176
|
-
*/
|
|
177
|
-
const storeState = async (instance, state) => {
|
|
178
|
-
if ((0, index_js_1.stringEmpty)(state === null || state === void 0 ? void 0 : state.shop)) {
|
|
179
|
-
return undefined;
|
|
180
|
-
}
|
|
181
|
-
try {
|
|
182
|
-
const docRef = instance.firestore
|
|
183
|
-
.collection(instance.collection)
|
|
184
|
-
.doc(state.shop)
|
|
185
|
-
.collection(instance.configurationId)
|
|
186
|
-
.doc(`${index_js_1.STATE_PREFIX}${state.shop}`);
|
|
187
|
-
await docRef.set(state);
|
|
188
|
-
return (await docRef.get()).data();
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
console.error('FirebaseSessionHandler.storeState error', error);
|
|
192
|
-
throw error;
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
exports.storeState = storeState;
|
|
196
|
-
/**
|
|
197
|
-
* @param {any} [instance]
|
|
198
|
-
* @param {any} [shop]
|
|
199
|
-
*/
|
|
200
|
-
const deleteState = async (instance, shop) => {
|
|
201
|
-
if ((0, index_js_1.stringEmpty)(shop)) {
|
|
202
|
-
return undefined;
|
|
203
|
-
}
|
|
204
|
-
try {
|
|
205
|
-
const docRef = instance.firestore
|
|
206
|
-
.collection(instance.collection)
|
|
207
|
-
.doc(shop)
|
|
208
|
-
.collection(instance.configurationId)
|
|
209
|
-
.doc(`${index_js_1.STATE_PREFIX}${shop}`);
|
|
210
|
-
return docRef.delete();
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
console.error('FirebaseSessionHandler.deleteState error', error);
|
|
214
|
-
throw error;
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
exports.deleteState = deleteState;
|
|
218
|
-
/**
|
|
219
|
-
* @param {any} [instance]
|
|
220
|
-
* @param {any} [shopId]
|
|
221
|
-
* @param {any} [sessionIds]
|
|
222
|
-
*/
|
|
223
|
-
const deleteSessionsCallback = async (instance, shopId, sessionIds) => {
|
|
224
|
-
/**
|
|
225
|
-
* @param {any} [_collectionRef]
|
|
226
|
-
* @param {any} [_sessionId]
|
|
227
|
-
*/
|
|
228
|
-
const deleteSessionCallback = async (_collectionRef, _sessionId) => {
|
|
229
|
-
try {
|
|
230
|
-
return _collectionRef.doc(_sessionId).delete();
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
console.error('FirebaseSessionHandler.deleteSessionCallback error', error);
|
|
234
|
-
throw error;
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
try {
|
|
238
|
-
const collectionRef = instance.firestore
|
|
239
|
-
.collection(instance.collection)
|
|
240
|
-
.doc(shopId)
|
|
241
|
-
.collection(instance.configurationId);
|
|
242
|
-
return Promise.all((0, index_js_1.ensureArray)(sessionIds).map((sessionId) => deleteSessionCallback(collectionRef, sessionId)));
|
|
243
|
-
}
|
|
244
|
-
catch (error) {
|
|
245
|
-
console.error('FirebaseSessionHandler.deleteSessionsCallback error', error);
|
|
246
|
-
throw error;
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
exports.deleteSessionsCallback = deleteSessionsCallback;
|
|
250
|
-
/**
|
|
251
|
-
* @param {any} [instance]
|
|
252
|
-
* @param {any} [shopId]
|
|
253
|
-
*/
|
|
254
|
-
const findSessionIdsByShop = async (instance, shopId) => {
|
|
255
|
-
if ((0, index_js_1.stringEmpty)(shopId)) {
|
|
256
|
-
// NOTE: this happens on initial install
|
|
257
|
-
return [];
|
|
258
|
-
}
|
|
259
|
-
try {
|
|
260
|
-
const collection = await instance.firestore
|
|
261
|
-
.collection(instance.collection)
|
|
262
|
-
.doc(shopId)
|
|
263
|
-
.collection(instance.configurationId)
|
|
264
|
-
.get();
|
|
265
|
-
/** @type {string[]} */
|
|
266
|
-
const sessionIds = [];
|
|
267
|
-
collection.forEach((/** @type {any} */ _doc) => {
|
|
268
|
-
sessionIds.push(_doc.id);
|
|
269
|
-
});
|
|
270
|
-
return sessionIds;
|
|
271
|
-
}
|
|
272
|
-
catch (error) {
|
|
273
|
-
console.error('FirebaseSessionHandler.findSessionIdsByShop error', error);
|
|
274
|
-
throw error;
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
exports.findSessionIdsByShop = findSessionIdsByShop;
|