From 09042759109fabb7af3f4d2e152216012b4afbf7 Mon Sep 17 00:00:00 2001 From: Sasha Goloshchapov Date: Mon, 27 Apr 2026 17:37:31 -0500 Subject: [PATCH 01/18] feat: initial working app --- lib/auth.ts | 12 ++-- lib/db.ts | 43 ++++++++----- lib/dbs/memory.ts | 124 ++++++++++++++++++++++++++++++++++++ next-env.d.ts | 2 +- pages/api/products/index.ts | 4 ++ pages/api/products/list.ts | 4 ++ pages/index.tsx | 2 + 7 files changed, 169 insertions(+), 22 deletions(-) create mode 100644 lib/dbs/memory.ts diff --git a/lib/auth.ts b/lib/auth.ts index 24529d5d..d71ae17e 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -51,10 +51,12 @@ export function getBCVerify({ signed_payload_jwt }: QueryParams) { return bigcommerceSigned.verifyJWT(signed_payload_jwt); } -export function setSession(session: SessionProps) { - db.setUser(session); - db.setStore(session); - db.setStoreUser(session); +export async function setSession(session: SessionProps) { + await Promise.all([ + db.setUser(session), + db.setStore(session), + db.setStoreUser(session), + ]); } export async function getSession({ query: { context = '' } }: NextApiRequest) { @@ -64,6 +66,8 @@ export async function getSession({ query: { context = '' } }: NextApiRequest) { // Before retrieving session/ hitting APIs, check user if (!hasUser) { + console.error(`Unauthorized access attempt for storeHash: ${storeHash}, userId: ${user?.id}`); + throw new Error('User is not available. Please login or ensure you have access permissions.'); } diff --git a/lib/db.ts b/lib/db.ts index 2842017f..7d2b1ca1 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -1,21 +1,30 @@ -import { Db } from '../types'; -import * as firebaseDB from './dbs/firebase'; -import * as sqlDB from './dbs/mysql'; +// import { Db } from '../types'; +// import * as firebaseDB from './dbs/firebase'; +import * as memoryDB from './dbs/memory'; +// import * as sqlDB from './dbs/mysql'; -const { DB_TYPE } = process.env; +export default memoryDB; -let db: Db; +// const { DB_TYPE } = process.env; -switch (DB_TYPE) { - case 'firebase': - db = firebaseDB; - break; - case 'mysql': - db = sqlDB; - break; - default: - db = firebaseDB; - break; -} +// console.log(`Using ${DB_TYPE} database`); -export default db; +// let db: Db; + +// switch (DB_TYPE) { +// case 'firebase': +// db = firebaseDB; +// break; +// case 'in-memory': +// case 'memory': +// db = memoryDB; +// break; +// case 'mysql': +// db = sqlDB; +// break; +// default: +// db = firebaseDB; +// break; +// } + +// export default db; diff --git a/lib/dbs/memory.ts b/lib/dbs/memory.ts new file mode 100644 index 00000000..22f20878 --- /dev/null +++ b/lib/dbs/memory.ts @@ -0,0 +1,124 @@ +import { SessionProps, StoreData, UserData } from '../../types'; + +interface StoreRecord extends StoreData { + adminId?: number; +} + +interface StoreUserRecord { + isAdmin: boolean; + storeHash: string; + userId: string; +} + +const stores = new Map(); +const storeUsers = new Map(); +const users = new Map(); + +function getStoreHash(session: SessionProps) { + const contextString = session.context ?? session.sub; + + return contextString?.split('/')[1] || ''; +} + +function getStoreUserKey(userId: string | number, storeHash: string) { + return `${String(userId)}_${storeHash}`; +} + +export async function setUser({ user }: SessionProps) { + if (!user) return null; + + const { email, id, username } = user; + const data: UserData = { email }; + + if (username) { + data.username = username; + } + + users.set(String(id), data); +} + +export async function setStore(session: SessionProps) { + const { + access_token: accessToken, + context, + scope, + user: { id }, + } = session; + + if (!accessToken || !scope) return null; + + const storeHash = context?.split('/')[1] || ''; + stores.set(storeHash, { accessToken, adminId: id, scope, storeHash }); + + // eslint-disable-next-line no-console + console.log('Store set:', stores.get(storeHash)); +} + +export async function setStoreUser(session: SessionProps) { + const { + access_token: accessToken, + owner, + user: { id: userId }, + } = session; + if (!userId) return null; + + const storeHash = getStoreHash(session); + const key = getStoreUserKey(userId, storeHash); + const storeUser = storeUsers.get(key); + + if (accessToken) { + if (!storeUser) { + storeUsers.set(key, { isAdmin: true, storeHash, userId: String(userId) }); + } else if (!storeUser.isAdmin) { + storeUsers.set(key, { ...storeUser, isAdmin: true }); + } + } else if (!storeUser) { + storeUsers.set(key, { + isAdmin: owner?.id === userId, + storeHash, + userId: String(userId), + }); + } + + // eslint-disable-next-line no-console + console.log('StoreUser set:', storeUsers.get(key)); +} + +export async function deleteUser({ context, user, sub }: SessionProps) { + const contextString = context ?? sub; + const storeHash = contextString?.split('/')[1] || ''; + const key = getStoreUserKey(user?.id, storeHash); + + storeUsers.delete(key); + + // eslint-disable-next-line no-console + console.log('StoreUser deleted:', key); +} + +export async function hasStoreUser(storeHash: string, userId: string) { + if (!storeHash || !userId) return false; + + const result = storeUsers.has(getStoreUserKey(userId, storeHash)); + + // eslint-disable-next-line no-console + console.log(`Checking store user for storeHash: ${storeHash}, userId: ${userId} - Result: ${result}`); + + return result; +} + +export async function getStoreToken(storeHash: string) { + if (!storeHash) return null; + + const result = stores.get(storeHash)?.accessToken ?? null; + + // eslint-disable-next-line no-console + console.log(`Getting store token for storeHash: ${storeHash} - Result: ${result}`); + + return result; +} + +export async function deleteStore({ store_hash: storeHash }: SessionProps) { + if (!storeHash) return; + + stores.delete(storeHash); +} \ No newline at end of file diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03d..a4a7b3f5 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/pages/api/products/index.ts b/pages/api/products/index.ts index 9ef29baa..a0b8445e 100644 --- a/pages/api/products/index.ts +++ b/pages/api/products/index.ts @@ -4,6 +4,10 @@ import { bigcommerceClient, getSession } from '../../../lib/auth'; export default async function products(req: NextApiRequest, res: NextApiResponse) { try { const { accessToken, storeHash } = await getSession(req); + + // eslint-disable-next-line no-console + console.log('Access Token:', accessToken); + const bigcommerce = bigcommerceClient(accessToken, storeHash); const { data } = await bigcommerce.get('/catalog/summary'); diff --git a/pages/api/products/list.ts b/pages/api/products/list.ts index 3416eeaf..a9e35c71 100644 --- a/pages/api/products/list.ts +++ b/pages/api/products/list.ts @@ -5,6 +5,10 @@ import { bigcommerceClient, getSession } from '../../../lib/auth'; export default async function list(req: NextApiRequest, res: NextApiResponse) { try { const { accessToken, storeHash } = await getSession(req); + + // eslint-disable-next-line no-console + console.log('Access Token:', accessToken); + const bigcommerce = bigcommerceClient(accessToken, storeHash); const { page, limit, sort, direction } = req.query; const params = new URLSearchParams({ page, limit, ...(sort && {sort, direction}) }).toString(); diff --git a/pages/index.tsx b/pages/index.tsx index 40277a0f..4f2d1203 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -7,6 +7,8 @@ import { useProducts } from '../lib/hooks'; const Index = () => { const { error, isLoading, summary } = useProducts(); + // console.log({error, isLoading, summary}) + if (isLoading) return ; if (error) return ; From 0cc8771df3a13436337e5accc2477ff7c012048b Mon Sep 17 00:00:00 2001 From: Sasha Goloshchapov Date: Tue, 28 Apr 2026 00:16:18 -0500 Subject: [PATCH 02/18] feat: switched to sqlite --- .gitignore | 1 + lib/db.ts | 31 +-- lib/dbs/sqlite.ts | 88 +++++++ package-lock.json | 605 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 + 5 files changed, 674 insertions(+), 53 deletions(-) create mode 100644 lib/dbs/sqlite.ts diff --git a/.gitignore b/.gitignore index 67045665..4f9c8770 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,4 @@ dist # TernJS port file .tern-port +blobfish.db diff --git a/lib/db.ts b/lib/db.ts index 7d2b1ca1..0d5a7cdd 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -1,30 +1,3 @@ -// import { Db } from '../types'; -// import * as firebaseDB from './dbs/firebase'; -import * as memoryDB from './dbs/memory'; -// import * as sqlDB from './dbs/mysql'; +import * as sqliteDB from './dbs/sqlite'; -export default memoryDB; - -// const { DB_TYPE } = process.env; - -// console.log(`Using ${DB_TYPE} database`); - -// let db: Db; - -// switch (DB_TYPE) { -// case 'firebase': -// db = firebaseDB; -// break; -// case 'in-memory': -// case 'memory': -// db = memoryDB; -// break; -// case 'mysql': -// db = sqlDB; -// break; -// default: -// db = firebaseDB; -// break; -// } - -// export default db; +export default sqliteDB; diff --git a/lib/dbs/sqlite.ts b/lib/dbs/sqlite.ts new file mode 100644 index 00000000..846db215 --- /dev/null +++ b/lib/dbs/sqlite.ts @@ -0,0 +1,88 @@ +import BetterSqlite3 from 'better-sqlite3'; +import path from 'path'; +import { SessionProps } from '../../types'; + +const db = new BetterSqlite3(path.join(process.cwd(), 'blobfish.db')); + +db.exec(` + CREATE TABLE IF NOT EXISTS stores ( + store_hash TEXT PRIMARY KEY, + access_token TEXT, + scope TEXT, + admin_id INTEGER + ); + CREATE TABLE IF NOT EXISTS store_users ( + user_id TEXT NOT NULL, + store_hash TEXT NOT NULL, + is_admin INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, store_hash) + ); + CREATE TABLE IF NOT EXISTS users ( + user_id TEXT PRIMARY KEY, + email TEXT, + username TEXT + ); +`); + +function getStoreHash(session: SessionProps) { + const contextString = session.context ?? session.sub; + + return contextString?.split('/')[1] || ''; +} + +export async function setUser({ user }: SessionProps) { + if (!user) return; + const { email, id, username } = user; + db.prepare(` + INSERT INTO users (user_id, email, username) + VALUES (?, ?, ?) + ON CONFLICT(user_id) DO UPDATE SET email = excluded.email, username = excluded.username + `).run(String(id), email, username ?? null); +} + +export async function setStore(session: SessionProps) { + const { access_token: accessToken, context, scope, user: { id } } = session; + if (!accessToken || !scope) return; + const storeHash = context?.split('/')[1] || ''; + db.prepare(` + INSERT INTO stores (store_hash, access_token, scope, admin_id) + VALUES (?, ?, ?, ?) + ON CONFLICT(store_hash) DO UPDATE SET access_token = excluded.access_token, scope = excluded.scope, admin_id = excluded.admin_id + `).run(storeHash, accessToken, scope, id); +} + +export async function setStoreUser(session: SessionProps) { + const { access_token: accessToken, owner, user: { id: userId } } = session; + if (!userId) return; + const storeHash = getStoreHash(session); + const isAdmin = accessToken ? 1 : (owner?.id === userId ? 1 : 0); + db.prepare(` + INSERT INTO store_users (user_id, store_hash, is_admin) + VALUES (?, ?, ?) + ON CONFLICT(user_id, store_hash) DO UPDATE SET is_admin = MAX(is_admin, excluded.is_admin) + `).run(String(userId), storeHash, isAdmin); +} + +export async function hasStoreUser(storeHash: string, userId: string) { + if (!storeHash || !userId) return false; + const row = db.prepare('SELECT 1 FROM store_users WHERE user_id = ? AND store_hash = ?').get(userId, storeHash); + + return row !== undefined; +} + +export async function getStoreToken(storeHash: string) { + if (!storeHash) return null; + const row = db.prepare('SELECT access_token FROM stores WHERE store_hash = ?').get(storeHash) as { access_token: string } | undefined; + + return row?.access_token ?? null; +} + +export async function deleteStore({ store_hash: storeHash }: SessionProps) { + if (!storeHash) return; + db.prepare('DELETE FROM stores WHERE store_hash = ?').run(storeHash); +} + +export async function deleteUser({ context, user, sub }: SessionProps) { + const storeHash = (context ?? sub)?.split('/')[1] || ''; + db.prepare('DELETE FROM store_users WHERE user_id = ? AND store_hash = ?').run(String(user?.id), storeHash); +} diff --git a/package-lock.json b/package-lock.json index a8ae2ab2..c5f20700 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@bigcommerce/big-design": "^0.36.2", "@bigcommerce/big-design-icons": "^0.23.1", "@bigcommerce/big-design-theme": "^0.19.1", + "better-sqlite3": "^11.10.0", "dotenv": "^16.3.0", "firebase-admin": "^13.3.0", "jsonwebtoken": "^9.0.1", @@ -27,6 +28,7 @@ "@testing-library/dom": "^7.31.2", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.0", + "@types/better-sqlite3": "^7.6.13", "@types/jest": "^27.5.2", "@types/node": "^18.0.0", "@types/react": "^18.2.0", @@ -3273,6 +3275,16 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -4374,6 +4386,17 @@ } ] }, + "node_modules/better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, "node_modules/bignumber.js": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", @@ -4382,6 +4405,26 @@ "node": "*" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -4454,6 +4497,30 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -4584,6 +4651,12 @@ "node": ">=10" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/ci-info": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", @@ -4853,6 +4926,21 @@ "node": ">=0.10" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -4891,6 +4979,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -4966,6 +5063,15 @@ "node": ">=0.10" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -5118,7 +5224,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "optional": true, "dependencies": { "once": "^1.4.0" } @@ -6025,6 +6130,15 @@ "node": ">= 0.8.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/expect": { "version": "28.1.1", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.1.tgz", @@ -6287,6 +6401,12 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -6394,6 +6514,12 @@ "node": ">= 6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6626,6 +6752,12 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -7000,6 +7132,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -7075,8 +7227,13 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "devOptional": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, "node_modules/internal-slot": { "version": "1.0.7", @@ -11123,6 +11280,18 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -11147,8 +11316,13 @@ "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" }, "node_modules/ms": { "version": "2.1.2", @@ -11217,6 +11391,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -11273,6 +11453,30 @@ } } }, + "node_modules/node-abi": { + "version": "3.89.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-bigcommerce": { "version": "4.1.0", "resolved": "git+https://git@github.com/bigcommerce/node-bigcommerce.git#bed20382d0b851fa8054cb732c05c3bb8f82343d", @@ -11534,7 +11738,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "devOptional": true, "dependencies": { "wrappy": "1" } @@ -11812,6 +12015,33 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -11940,6 +12170,16 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -11980,6 +12220,30 @@ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -12017,7 +12281,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -12361,6 +12624,51 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -12477,7 +12785,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "optional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -12795,6 +13102,34 @@ "node": ">=6" } }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/teeny-request": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", @@ -12967,6 +13302,18 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -13081,8 +13428,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "optional": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { "version": "11.1.0", @@ -13348,8 +13694,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "devOptional": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -15986,6 +16331,15 @@ "@babel/types": "^7.3.0" } }, + "@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -16839,11 +17193,38 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "requires": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, "bignumber.js": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -16905,6 +17286,15 @@ "node-int64": "^0.4.0" } }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -16995,6 +17385,11 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "ci-info": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", @@ -17221,6 +17616,14 @@ "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + } + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -17253,6 +17656,11 @@ "which-typed-array": "^1.1.13" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -17304,6 +17712,11 @@ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -17425,7 +17838,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "optional": true, "requires": { "once": "^1.4.0" } @@ -18108,6 +18520,11 @@ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" + }, "expect": { "version": "28.1.1", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.1.tgz", @@ -18317,6 +18734,11 @@ "flat-cache": "^3.0.4" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -18409,6 +18831,11 @@ "mime-types": "^2.1.12" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -18571,6 +18998,11 @@ "integrity": "sha512-8mPf1bBzF2S+fyuyYOQWjDcaJTTgJ14UAnXW9I3KwrqioRWG1byRXHwciYdqXpbdOiu7Fg4WJbymBIakGk+aMA==", "dev": true }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -18855,6 +19287,11 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -18906,8 +19343,12 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "devOptional": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "internal-slot": { "version": "1.0.7", @@ -22024,6 +22465,11 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -22042,8 +22488,12 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "ms": { "version": "2.1.2", @@ -22092,6 +22542,11 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" }, + "napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -22121,6 +22576,21 @@ "styled-jsx": "5.1.1" } }, + "node-abi": { + "version": "3.89.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", + "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", + "requires": { + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==" + } + } + }, "node-bigcommerce": { "version": "git+https://git@github.com/bigcommerce/node-bigcommerce.git#bed20382d0b851fa8054cb732c05c3bb8f82343d", "integrity": "sha512-oU39CH8fMpZw/QHAlfYgaceTM7kPHBbVPiJvKzSKtlkzYixAaB97UwReN1IKF+kdLL/tIOZDGKRZoJS5ZyAkNA==", @@ -22316,7 +22786,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "devOptional": true, "requires": { "wrappy": "1" } @@ -22509,6 +22978,25 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" }, + "prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -22614,6 +23102,15 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, + "pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -22637,6 +23134,24 @@ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" + } + } + }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -22668,7 +23183,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -22910,6 +23424,21 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -23004,7 +23533,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "optional": true, "requires": { "safe-buffer": "~5.2.0" } @@ -23233,6 +23761,29 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, + "tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, "teeny-request": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", @@ -23370,6 +23921,14 @@ "tslib": "^1.8.1" } }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -23457,8 +24016,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "optional": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "uuid": { "version": "11.1.0", @@ -23656,8 +24214,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "devOptional": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "3.0.3", diff --git a/package.json b/package.json index 6634c1bc..4955bfda 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@bigcommerce/big-design": "^0.36.2", "@bigcommerce/big-design-icons": "^0.23.1", "@bigcommerce/big-design-theme": "^0.19.1", + "better-sqlite3": "^11.10.0", "dotenv": "^16.3.0", "firebase-admin": "^13.3.0", "jsonwebtoken": "^9.0.1", @@ -38,6 +39,7 @@ "@testing-library/dom": "^7.31.2", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.0", + "@types/better-sqlite3": "^7.6.13", "@types/jest": "^27.5.2", "@types/node": "^18.0.0", "@types/react": "^18.2.0", From 1b0b8e56943683111dd966cf5826723d81d40e77 Mon Sep 17 00:00:00 2001 From: Sasha Goloshchapov Date: Tue, 28 Apr 2026 00:57:09 -0500 Subject: [PATCH 03/18] feat: upgrade to TS 5 --- package-lock.json | 17 +++++++++-------- package.json | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5f20700..114f0821 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "jest": "^28.1.2", "jest-environment-jsdom": "^28.1.2", - "typescript": "^4.7.3" + "typescript": "^5.9.3" }, "engines": { "node": ">=18 <20", @@ -13357,16 +13357,17 @@ } }, "node_modules/typescript": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -23960,9 +23961,9 @@ } }, "typescript": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", - "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true }, "unbox-primitive": { diff --git a/package.json b/package.json index 4955bfda..0adafe8d 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,6 @@ "eslint-plugin-react-hooks": "^4.6.0", "jest": "^28.1.2", "jest-environment-jsdom": "^28.1.2", - "typescript": "^4.7.3" + "typescript": "^5.9.3" } } From 9b3a0638b7c1f29345722a339645a44c1b46e723 Mon Sep 17 00:00:00 2001 From: Sasha Goloshchapov Date: Tue, 28 Apr 2026 01:18:09 -0500 Subject: [PATCH 04/18] feat: add tada and BC API indicator --- components/header.tsx | 37 +- graphql-env.d.ts | 117 ++++ lib/account-client.ts | 13 + lib/graphql.ts | 9 + lib/hooks.ts | 12 + package-lock.json | 187 +++++- package.json | 7 +- pages/api/gql-check.ts | 24 + pages/gql-check.tsx | 27 + schema.graphql | 1265 +++++++++++++++++++++++++++++++++++ scripts/generate-schema.mjs | 39 ++ tsconfig.json | 7 + 12 files changed, 1737 insertions(+), 7 deletions(-) create mode 100644 graphql-env.d.ts create mode 100644 lib/account-client.ts create mode 100644 lib/graphql.ts create mode 100644 pages/api/gql-check.ts create mode 100644 pages/gql-check.tsx create mode 100644 schema.graphql create mode 100644 scripts/generate-schema.mjs diff --git a/components/header.tsx b/components/header.tsx index 6c531053..018d1c89 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -1,6 +1,8 @@ -import { Box, Tabs } from '@bigcommerce/big-design'; +import { Box, Tabs, Text } from '@bigcommerce/big-design'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; +import styled from 'styled-components'; +import { useGqlCheck } from '../lib/hooks'; import InnerHeader from './innerHeader'; export const TabIds = { @@ -30,8 +32,12 @@ const HeaderTypes = { HEADERLESS: 'headerless', }; +const dotColor = { ok: '#3d9970', error: '#e84040', checking: '#aaaaaa' }; +const dotLabel = { ok: 'BigCommerce API connected', error: 'BigCommerce API unreachable', checking: 'Checking BigCommerce API…' }; + const Header = () => { const [activeTab, setActiveTab] = useState(''); + const { status } = useGqlCheck(); const [headerType, setHeaderType] = useState(HeaderTypes.GLOBAL); const router = useRouter(); const { pathname } = router; @@ -73,14 +79,39 @@ const Header = () => { if (headerType === HeaderTypes.INNER) return ; return ( - + - + + + {dotLabel[status]} + + ); }; +const HeaderWrapper = styled(Box)` + position: relative; +`; + +const StatusBadge = styled.div` + position: absolute; + top: 0; + right: 0; + display: flex; + align-items: center; +`; + +const Dot = styled.span<{ color: string }>` + width: 10px; + height: 10px; + border-radius: 50%; + background: ${p => p.color}; + margin-right: 8px; + flex-shrink: 0; +`; + export default Header; diff --git a/graphql-env.d.ts b/graphql-env.d.ts new file mode 100644 index 00000000..900d672c --- /dev/null +++ b/graphql-env.d.ts @@ -0,0 +1,117 @@ +/* eslint-disable */ +/* prettier-ignore */ + +export type introspection_types = { + 'Account': { kind: 'OBJECT'; name: 'Account'; fields: { 'accountInfo': { name: 'accountInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AccountInfo'; ofType: null; }; } }; 'apps': { name: 'apps'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AppsConnection'; ofType: null; }; } }; 'checkout': { name: 'checkout'; type: { kind: 'OBJECT'; name: 'Checkout'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'stores': { name: 'stores'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StoreConnection'; ofType: null; }; } }; 'subscriptions': { name: 'subscriptions'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SubscriptionConnection'; ofType: null; }; } }; 'users': { name: 'users'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserConnection'; ofType: null; }; } }; }; }; + 'AccountInfo': { kind: 'OBJECT'; name: 'AccountInfo'; fields: { 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; + 'AccountMutations': { kind: 'OBJECT'; name: 'AccountMutations'; fields: { 'addUserToAccount': { name: 'addUserToAccount'; type: { kind: 'OBJECT'; name: 'AddUserToAccountResult'; ofType: null; } }; 'removeUserFromAccount': { name: 'removeUserFromAccount'; type: { kind: 'OBJECT'; name: 'RemoveUserFromAccountResult'; ofType: null; } }; }; }; + 'AddUserToAccountInput': { kind: 'INPUT_OBJECT'; name: 'AddUserToAccountInput'; isOneOf: false; inputFields: [{ name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; + 'AddUserToAccountResult': { kind: 'OBJECT'; name: 'AddUserToAccountResult'; fields: { 'account': { name: 'account'; type: { kind: 'OBJECT'; name: 'Account'; ofType: null; } }; }; }; + 'AddUserToStoreInput': { kind: 'INPUT_OBJECT'; name: 'AddUserToStoreInput'; isOneOf: false; inputFields: [{ name: 'storeId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'user'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INPUT_OBJECT'; name: 'UserIdentifierInput'; ofType: null; }; }; defaultValue: null }]; }; + 'AddUserToStoreResult': { kind: 'OBJECT'; name: 'AddUserToStoreResult'; fields: { 'storeId': { name: 'storeId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; + 'App': { kind: 'OBJECT'; name: 'App'; fields: { 'eventSources': { name: 'eventSources'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AwsEventSourcesConnection'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; + 'AppMutations': { kind: 'OBJECT'; name: 'AppMutations'; fields: { 'createEventSource': { name: 'createEventSource'; type: { kind: 'OBJECT'; name: 'CreateEventSourceResult'; ofType: null; } }; 'deleteEventSource': { name: 'deleteEventSource'; type: { kind: 'OBJECT'; name: 'DeleteEventSourceResult'; ofType: null; } }; }; }; + 'AppsConnection': { kind: 'OBJECT'; name: 'AppsConnection'; fields: { 'edges': { name: 'edges'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'AppsEdge'; ofType: null; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; }; }; + 'AppsEdge': { kind: 'OBJECT'; name: 'AppsEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'App'; ofType: null; }; } }; }; }; + 'AwsEventSource': { kind: 'OBJECT'; name: 'AwsEventSource'; fields: { 'arn': { name: 'arn'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'awsAccount': { name: 'awsAccount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'eventSourceName': { name: 'eventSourceName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'eventSourceRegion': { name: 'eventSourceRegion'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'AwsRegion'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'webhooksCount': { name: 'webhooksCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Long'; ofType: null; }; } }; }; }; + 'AwsEventSourcesConnection': { kind: 'OBJECT'; name: 'AwsEventSourcesConnection'; fields: { 'edges': { name: 'edges'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'AwsEventSourcesEdge'; ofType: null; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; }; }; + 'AwsEventSourcesEdge': { kind: 'OBJECT'; name: 'AwsEventSourcesEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AwsEventSource'; ofType: null; }; } }; }; }; + 'AwsRegion': { name: 'AwsRegion'; enumValues: 'AF_SOUTH_1' | 'AP_EAST_1' | 'AP_NORTHEAST_1' | 'AP_NORTHEAST_2' | 'AP_NORTHEAST_3' | 'AP_SOUTHEAST_1' | 'AP_SOUTHEAST_2' | 'AP_SOUTHEAST_3' | 'AP_SOUTHEAST_4' | 'AP_SOUTH_1' | 'AP_SOUTH_2' | 'CA_CENTRAL_1' | 'EU_CENTRAL_1' | 'EU_CENTRAL_2' | 'EU_NORTH_1' | 'EU_SOUTH_1' | 'EU_SOUTH_2' | 'EU_WEST_1' | 'EU_WEST_2' | 'EU_WEST_3' | 'IL_CENTRAL_1' | 'ME_CENTRAL_1' | 'SA_EAST_1' | 'US_EAST_1' | 'US_EAST_2' | 'US_WEST_1' | 'US_WEST_2'; }; + 'BigDecimal': unknown; + 'Boolean': unknown; + 'CancelSubscriptionInput': { kind: 'INPUT_OBJECT'; name: 'CancelSubscriptionInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }]; }; + 'CancelSubscriptionResult': { kind: 'OBJECT'; name: 'CancelSubscriptionResult'; fields: { 'cancelledAt': { name: 'cancelledAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'subscriptionId': { name: 'subscriptionId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; }; }; + 'Checkout': { kind: 'OBJECT'; name: 'Checkout'; fields: { 'accountId': { name: 'accountId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'checkoutUrl': { name: 'checkoutUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'items': { name: 'items'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckoutItemConnection'; ofType: null; }; } }; 'status': { name: 'status'; type: { kind: 'ENUM'; name: 'CheckoutStatus'; ofType: null; } }; }; }; + 'CheckoutItem': { kind: 'OBJECT'; name: 'CheckoutItem'; fields: { 'description': { name: 'description'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'pricingPlan': { name: 'pricingPlan'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckoutItemPricingPlan'; ofType: null; }; } }; 'product': { name: 'product'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckoutItemProduct'; ofType: null; }; } }; 'redirectUrl': { name: 'redirectUrl'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'scope': { name: 'scope'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckoutItemScope'; ofType: null; }; } }; 'status': { name: 'status'; type: { kind: 'ENUM'; name: 'CheckoutItemStatus'; ofType: null; } }; 'subscriptionId': { name: 'subscriptionId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; }; }; + 'CheckoutItemConnection': { kind: 'OBJECT'; name: 'CheckoutItemConnection'; fields: { 'edges': { name: 'edges'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckoutItemEdge'; ofType: null; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; }; }; + 'CheckoutItemEdge': { kind: 'OBJECT'; name: 'CheckoutItemEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckoutItem'; ofType: null; }; } }; }; }; + 'CheckoutItemInput': { kind: 'INPUT_OBJECT'; name: 'CheckoutItemInput'; isOneOf: false; inputFields: [{ name: 'description'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'pricingPlan'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INPUT_OBJECT'; name: 'PricingPlanInput'; ofType: null; }; }; defaultValue: null }, { name: 'product'; type: { kind: 'INPUT_OBJECT'; name: 'CheckoutItemProductInput'; ofType: null; }; defaultValue: null }, { name: 'redirectUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'scope'; type: { kind: 'INPUT_OBJECT'; name: 'CheckoutItemScopeInput'; ofType: null; }; defaultValue: null }, { name: 'subscriptionId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }]; }; + 'CheckoutItemPricingInterval': { name: 'CheckoutItemPricingInterval'; enumValues: 'ANNUAL' | 'MONTH' | 'ONCE' | 'QUARTER' | 'SEMIANNUAL'; }; + 'CheckoutItemPricingPlan': { kind: 'OBJECT'; name: 'CheckoutItemPricingPlan'; fields: { 'interval': { name: 'interval'; type: { kind: 'ENUM'; name: 'CheckoutItemPricingInterval'; ofType: null; } }; 'price': { name: 'price'; type: { kind: 'OBJECT'; name: 'Money'; ofType: null; } }; 'trialDays': { name: 'trialDays'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; }; }; + 'CheckoutItemProduct': { kind: 'OBJECT'; name: 'CheckoutItemProduct'; fields: { 'id': { name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; 'productLevel': { name: 'productLevel'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'type': { name: 'type'; type: { kind: 'ENUM'; name: 'CheckoutItemProductType'; ofType: null; } }; }; }; + 'CheckoutItemProductInput': { kind: 'INPUT_OBJECT'; name: 'CheckoutItemProductInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'productLevel'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'type'; type: { kind: 'ENUM'; name: 'CheckoutItemProductType'; ofType: null; }; defaultValue: null }]; }; + 'CheckoutItemProductType': { name: 'CheckoutItemProductType'; enumValues: 'APPLICATION'; }; + 'CheckoutItemScope': { kind: 'OBJECT'; name: 'CheckoutItemScope'; fields: { 'id': { name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; 'type': { name: 'type'; type: { kind: 'ENUM'; name: 'CheckoutItemScopeType'; ofType: null; } }; }; }; + 'CheckoutItemScopeInput': { kind: 'INPUT_OBJECT'; name: 'CheckoutItemScopeInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'type'; type: { kind: 'ENUM'; name: 'CheckoutItemScopeType'; ofType: null; }; defaultValue: null }]; }; + 'CheckoutItemScopeType': { name: 'CheckoutItemScopeType'; enumValues: 'STORE'; }; + 'CheckoutItemStatus': { name: 'CheckoutItemStatus'; enumValues: 'COMPLETE' | 'PENDING' | 'PROCESSING'; }; + 'CheckoutMutations': { kind: 'OBJECT'; name: 'CheckoutMutations'; fields: { 'createCheckout': { name: 'createCheckout'; type: { kind: 'OBJECT'; name: 'CreateCheckoutResult'; ofType: null; } }; }; }; + 'CheckoutStatus': { name: 'CheckoutStatus'; enumValues: 'COMPLETE' | 'PENDING' | 'PROCESSING'; }; + 'CollectionInfo': { kind: 'OBJECT'; name: 'CollectionInfo'; fields: { 'totalItems': { name: 'totalItems'; type: { kind: 'SCALAR'; name: 'Long'; ofType: null; } }; }; }; + 'CreateCheckoutInput': { kind: 'INPUT_OBJECT'; name: 'CreateCheckoutInput'; isOneOf: false; inputFields: [{ name: 'accountId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'items'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INPUT_OBJECT'; name: 'CheckoutItemInput'; ofType: null; }; }; }; }; defaultValue: null }]; }; + 'CreateCheckoutResult': { kind: 'OBJECT'; name: 'CreateCheckoutResult'; fields: { 'checkout': { name: 'checkout'; type: { kind: 'OBJECT'; name: 'Checkout'; ofType: null; } }; }; }; + 'CreateEventSourceInput': { kind: 'INPUT_OBJECT'; name: 'CreateEventSourceInput'; isOneOf: false; inputFields: [{ name: 'appId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'awsAccount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'eventSourceName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'eventSourceRegion'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'AwsRegion'; ofType: null; }; }; defaultValue: null }]; }; + 'CreateEventSourceResult': { kind: 'OBJECT'; name: 'CreateEventSourceResult'; fields: { 'eventSource': { name: 'eventSource'; type: { kind: 'OBJECT'; name: 'AwsEventSource'; ofType: null; } }; }; }; + 'CreateUserInput': { kind: 'INPUT_OBJECT'; name: 'CreateUserInput'; isOneOf: false; inputFields: [{ name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'firstName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'lastName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'locale'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; + 'CreateUserResult': { kind: 'OBJECT'; name: 'CreateUserResult'; fields: { 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; }; }; + 'CreateUserWithPasswordInput': { kind: 'INPUT_OBJECT'; name: 'CreateUserWithPasswordInput'; isOneOf: false; inputFields: [{ name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'firstName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'lastName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'locale'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'password'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'passwordConfirmation'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; + 'CreateUserWithPasswordResult': { kind: 'OBJECT'; name: 'CreateUserWithPasswordResult'; fields: { 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; }; }; + 'CurrencyCode': { name: 'CurrencyCode'; enumValues: 'AED' | 'AFN' | 'ALL' | 'AMD' | 'ANG' | 'AOA' | 'ARS' | 'AUD' | 'AWG' | 'AZN' | 'BAM' | 'BBD' | 'BDT' | 'BGN' | 'BHD' | 'BIF' | 'BMD' | 'BND' | 'BOB' | 'BOV' | 'BRL' | 'BSD' | 'BTN' | 'BWP' | 'BYN' | 'BYR' | 'BZD' | 'CAD' | 'CDF' | 'CHE' | 'CHF' | 'CHW' | 'CLF' | 'CLP' | 'CNY' | 'COP' | 'COU' | 'CRC' | 'CUC' | 'CUP' | 'CVE' | 'CZK' | 'DJF' | 'DKK' | 'DOP' | 'DZD' | 'EGP' | 'ERN' | 'ETB' | 'EUR' | 'FJD' | 'FKP' | 'GBP' | 'GEL' | 'GHS' | 'GIP' | 'GMD' | 'GNF' | 'GTQ' | 'GYD' | 'HKD' | 'HNL' | 'HRK' | 'HTG' | 'HUF' | 'IDR' | 'ILS' | 'INR' | 'IQD' | 'IRR' | 'ISK' | 'JMD' | 'JOD' | 'JPY' | 'KES' | 'KGS' | 'KHR' | 'KMF' | 'KPW' | 'KRW' | 'KWD' | 'KYD' | 'KZT' | 'LAK' | 'LBP' | 'LKR' | 'LRD' | 'LSL' | 'LYD' | 'MAD' | 'MDL' | 'MGA' | 'MKD' | 'MMK' | 'MNT' | 'MOP' | 'MRO' | 'MRU' | 'MUR' | 'MVR' | 'MWK' | 'MXN' | 'MYR' | 'MZN' | 'NAD' | 'NGN' | 'NIO' | 'NOK' | 'NPR' | 'NZD' | 'OMR' | 'PAB' | 'PEN' | 'PGK' | 'PHP' | 'PKR' | 'PLN' | 'PYG' | 'QAR' | 'RON' | 'RSD' | 'RUB' | 'RWF' | 'SAR' | 'SBD' | 'SCR' | 'SDG' | 'SEK' | 'SGD' | 'SHP' | 'SLL' | 'SOS' | 'SRD' | 'SSP' | 'STD' | 'STN' | 'SVC' | 'SYP' | 'SZL' | 'THB' | 'TJS' | 'TMT' | 'TND' | 'TOP' | 'TRY' | 'TTD' | 'TWD' | 'TZS' | 'UAH' | 'UGX' | 'USD' | 'USN' | 'UYI' | 'UYU' | 'UZS' | 'VEF' | 'VES' | 'VND' | 'VUV' | 'WST' | 'XAF' | 'XAG' | 'XAU' | 'XBA' | 'XBB' | 'XBC' | 'XBD' | 'XCD' | 'XDR' | 'XOF' | 'XPD' | 'XPF' | 'XPT' | 'XSU' | 'XTS' | 'XUA' | 'XXX' | 'YER' | 'ZAR' | 'ZMW' | 'ZWL'; }; + 'DateTime': unknown; + 'DeleteEventSourceInput': { kind: 'INPUT_OBJECT'; name: 'DeleteEventSourceInput'; isOneOf: false; inputFields: [{ name: 'appId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }]; }; + 'DeleteEventSourceResult': { kind: 'OBJECT'; name: 'DeleteEventSourceResult'; fields: { 'affectedWebhooksIds': { name: 'affectedWebhooksIds'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; }; } }; 'deletedEventSourceId': { name: 'deletedEventSourceId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; + 'ID': unknown; + 'Int': unknown; + 'Long': unknown; + 'Money': { kind: 'OBJECT'; name: 'Money'; fields: { 'currencyCode': { name: 'currencyCode'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'value': { name: 'value'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'BigDecimal'; ofType: null; }; } }; }; }; + 'MoneyInput': { kind: 'INPUT_OBJECT'; name: 'MoneyInput'; isOneOf: false; inputFields: [{ name: 'currencyCode'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'CurrencyCode'; ofType: null; }; }; defaultValue: null }, { name: 'value'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'BigDecimal'; ofType: null; }; }; defaultValue: null }]; }; + 'Mutation': { kind: 'OBJECT'; name: 'Mutation'; fields: { 'account': { name: 'account'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AccountMutations'; ofType: null; }; } }; 'app': { name: 'app'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AppMutations'; ofType: null; }; } }; 'checkout': { name: 'checkout'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckoutMutations'; ofType: null; }; } }; 'subscription': { name: 'subscription'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SubscriptionMutations'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserMutations'; ofType: null; }; } }; }; }; + 'Node': { kind: 'INTERFACE'; name: 'Node'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; possibleTypes: 'Checkout' | 'Subscription'; }; + 'PageInfo': { kind: 'OBJECT'; name: 'PageInfo'; fields: { 'endCursor': { name: 'endCursor'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'hasNextPage': { name: 'hasNextPage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'hasPreviousPage': { name: 'hasPreviousPage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'startCursor': { name: 'startCursor'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; }; + 'PricingPlanInput': { kind: 'INPUT_OBJECT'; name: 'PricingPlanInput'; isOneOf: false; inputFields: [{ name: 'interval'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'CheckoutItemPricingInterval'; ofType: null; }; }; defaultValue: null }, { name: 'price'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INPUT_OBJECT'; name: 'MoneyInput'; ofType: null; }; }; defaultValue: null }, { name: 'trialDays'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }]; }; + 'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'account': { name: 'account'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Account'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'INTERFACE'; name: 'Node'; ofType: null; } }; 'nodes': { name: 'nodes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'INTERFACE'; name: 'Node'; ofType: null; }; }; } }; 'system': { name: 'system'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'System'; ofType: null; }; } }; }; }; + 'RemoveUserFromAccountInput': { kind: 'INPUT_OBJECT'; name: 'RemoveUserFromAccountInput'; isOneOf: false; inputFields: [{ name: 'email'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'userId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }]; }; + 'RemoveUserFromAccountResult': { kind: 'OBJECT'; name: 'RemoveUserFromAccountResult'; fields: { 'accountId': { name: 'accountId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'userId': { name: 'userId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; + 'RemoveUserFromStoreInput': { kind: 'INPUT_OBJECT'; name: 'RemoveUserFromStoreInput'; isOneOf: false; inputFields: [{ name: 'storeId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'userId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }]; }; + 'RemoveUserFromStoreResult': { kind: 'OBJECT'; name: 'RemoveUserFromStoreResult'; fields: { 'storeId': { name: 'storeId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; }; }; + 'Store': { kind: 'OBJECT'; name: 'Store'; fields: { 'apps': { name: 'apps'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AppsConnection'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'storeHash': { name: 'storeHash'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'users': { name: 'users'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StoreUserConnection'; ofType: null; }; } }; }; }; + 'StoreConnection': { kind: 'OBJECT'; name: 'StoreConnection'; fields: { 'edges': { name: 'edges'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'StoreEdge'; ofType: null; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; }; }; + 'StoreEdge': { kind: 'OBJECT'; name: 'StoreEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Store'; ofType: null; }; } }; }; }; + 'StoreFilterInput': { kind: 'INPUT_OBJECT'; name: 'StoreFilterInput'; isOneOf: false; inputFields: [{ name: 'ids'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; }; defaultValue: null }, { name: 'storeHashes'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; defaultValue: null }]; }; + 'StoreUser': { kind: 'OBJECT'; name: 'StoreUser'; fields: { 'apps': { name: 'apps'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AppsConnection'; ofType: null; }; } }; 'email': { name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'firstName': { name: 'firstName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'lastLoginAt': { name: 'lastLoginAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'lastName': { name: 'lastName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'locale': { name: 'locale'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'permissions': { name: 'permissions'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; } }; 'status': { name: 'status'; type: { kind: 'ENUM'; name: 'StoreUserStatus'; ofType: null; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; }; }; + 'StoreUserConnection': { kind: 'OBJECT'; name: 'StoreUserConnection'; fields: { 'collectionInfo': { name: 'collectionInfo'; type: { kind: 'OBJECT'; name: 'CollectionInfo'; ofType: null; } }; 'edges': { name: 'edges'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'StoreUserEdge'; ofType: null; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; }; }; + 'StoreUserEdge': { kind: 'OBJECT'; name: 'StoreUserEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StoreUser'; ofType: null; }; } }; }; }; + 'StoreUserStatus': { name: 'StoreUserStatus'; enumValues: 'ACTIVE' | 'INACTIVE'; }; + 'String': unknown; + 'Subscription': { kind: 'OBJECT'; name: 'Subscription'; fields: { 'accountId': { name: 'accountId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'activationDate': { name: 'activationDate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'billingInterval': { name: 'billingInterval'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'SubscriptionBillingInterval'; ofType: null; }; } }; 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'currentPeriodEnd': { name: 'currentPeriodEnd'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'pricePerInterval': { name: 'pricePerInterval'; type: { kind: 'OBJECT'; name: 'Money'; ofType: null; } }; 'product': { name: 'product'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SubscriptionProduct'; ofType: null; }; } }; 'scope': { name: 'scope'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SubscriptionScope'; ofType: null; }; } }; 'status': { name: 'status'; type: { kind: 'ENUM'; name: 'SubscriptionStatus'; ofType: null; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; }; }; + 'SubscriptionBillingInterval': { name: 'SubscriptionBillingInterval'; enumValues: 'ANNUAL' | 'MONTH' | 'NONE' | 'ONCE' | 'QUARTER' | 'SEMIANNUAL'; }; + 'SubscriptionConnection': { kind: 'OBJECT'; name: 'SubscriptionConnection'; fields: { 'edges': { name: 'edges'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'SubscriptionEdge'; ofType: null; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; }; }; + 'SubscriptionEdge': { kind: 'OBJECT'; name: 'SubscriptionEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Subscription'; ofType: null; }; } }; }; }; + 'SubscriptionFiltersInput': { kind: 'INPUT_OBJECT'; name: 'SubscriptionFiltersInput'; isOneOf: false; inputFields: [{ name: 'ids'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; }; defaultValue: null }, { name: 'productId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'productType'; type: { kind: 'ENUM'; name: 'SubscriptionProductType'; ofType: null; }; defaultValue: null }, { name: 'scopeId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'scopeType'; type: { kind: 'ENUM'; name: 'SubscriptionScopeType'; ofType: null; }; defaultValue: null }, { name: 'status'; type: { kind: 'ENUM'; name: 'SubscriptionStatus'; ofType: null; }; defaultValue: null }, { name: 'updatedAfter'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; defaultValue: null }]; }; + 'SubscriptionMutations': { kind: 'OBJECT'; name: 'SubscriptionMutations'; fields: { 'cancelSubscription': { name: 'cancelSubscription'; type: { kind: 'OBJECT'; name: 'CancelSubscriptionResult'; ofType: null; } }; }; }; + 'SubscriptionProduct': { kind: 'OBJECT'; name: 'SubscriptionProduct'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'productLevel': { name: 'productLevel'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'type': { name: 'type'; type: { kind: 'ENUM'; name: 'SubscriptionProductType'; ofType: null; } }; }; }; + 'SubscriptionProductType': { name: 'SubscriptionProductType'; enumValues: 'APPLICATION' | 'NONE'; }; + 'SubscriptionScope': { kind: 'OBJECT'; name: 'SubscriptionScope'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'type': { name: 'type'; type: { kind: 'ENUM'; name: 'SubscriptionScopeType'; ofType: null; } }; }; }; + 'SubscriptionScopeType': { name: 'SubscriptionScopeType'; enumValues: 'STORE'; }; + 'SubscriptionStatus': { name: 'SubscriptionStatus'; enumValues: 'ACTIVE' | 'CANCELLED' | 'SUSPENDED'; }; + 'System': { kind: 'OBJECT'; name: 'System'; fields: { 'time': { name: 'time'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Long'; ofType: null; }; } }; }; }; + 'User': { kind: 'OBJECT'; name: 'User'; fields: { 'email': { name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'firstName': { name: 'firstName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'lastName': { name: 'lastName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'locale': { name: 'locale'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; + 'UserConnection': { kind: 'OBJECT'; name: 'UserConnection'; fields: { 'collectionInfo': { name: 'collectionInfo'; type: { kind: 'OBJECT'; name: 'CollectionInfo'; ofType: null; } }; 'edges': { name: 'edges'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'UserEdge'; ofType: null; }; } }; 'pageInfo': { name: 'pageInfo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PageInfo'; ofType: null; }; } }; }; }; + 'UserEdge': { kind: 'OBJECT'; name: 'UserEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'User'; ofType: null; }; } }; }; }; + 'UserIdentifierInput': { kind: 'INPUT_OBJECT'; name: 'UserIdentifierInput'; isOneOf: false; inputFields: [{ name: 'email'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }]; }; + 'UserMutations': { kind: 'OBJECT'; name: 'UserMutations'; fields: { 'addUserToStore': { name: 'addUserToStore'; type: { kind: 'OBJECT'; name: 'AddUserToStoreResult'; ofType: null; } }; 'createUser': { name: 'createUser'; type: { kind: 'OBJECT'; name: 'CreateUserResult'; ofType: null; } }; 'createUserWithPassword': { name: 'createUserWithPassword'; type: { kind: 'OBJECT'; name: 'CreateUserWithPasswordResult'; ofType: null; } }; 'removeUserFromStore': { name: 'removeUserFromStore'; type: { kind: 'OBJECT'; name: 'RemoveUserFromStoreResult'; ofType: null; } }; }; }; +}; + +/** An IntrospectionQuery representation of your schema. + * + * @remarks + * This is an introspection of your schema saved as a file by GraphQLSP. + * It will automatically be used by `gql.tada` to infer the types of your GraphQL documents. + * If you need to reuse this data or update your `scalars`, update `tadaOutputLocation` to + * instead save to a .ts instead of a .d.ts file. + */ +export type introspection = { + name: never; + query: 'Query'; + mutation: 'Mutation'; + subscription: 'Subscription'; + types: introspection_types; +}; + +import * as gqlTada from 'gql.tada'; + +declare module 'gql.tada' { + interface setupSchema { + introspection: introspection + } +} \ No newline at end of file diff --git a/lib/account-client.ts b/lib/account-client.ts new file mode 100644 index 00000000..f23e1de2 --- /dev/null +++ b/lib/account-client.ts @@ -0,0 +1,13 @@ +import { GraphQLClient } from 'graphql-request'; + +const accountUuid = process.env.BC_ACCOUNT_UUID; +const apiToken = process.env.BC_ACCOUNT_API_TOKEN; + +export const accountClient = new GraphQLClient( + `https://api.bigcommerce.com/accounts/${accountUuid}/graphql`, + { + headers: { + 'X-Auth-Token': apiToken ?? '', + }, + } +); diff --git a/lib/graphql.ts b/lib/graphql.ts new file mode 100644 index 00000000..cacd0630 --- /dev/null +++ b/lib/graphql.ts @@ -0,0 +1,9 @@ +import { initGraphQLTada } from 'gql.tada'; +import type { introspection } from '../graphql-env'; + +export const graphql = initGraphQLTada<{ + introspection: introspection; +}>(); + +export type { FragmentOf, ResultOf, VariablesOf } from 'gql.tada'; +export { readFragment } from 'gql.tada'; diff --git a/lib/hooks.ts b/lib/hooks.ts index 1016b64a..b3ec5564 100644 --- a/lib/hooks.ts +++ b/lib/hooks.ts @@ -16,6 +16,12 @@ async function fetcher(url: string, query: string) { return res.json(); } +async function simpleFetcher(url: string) { + const res = await fetch(url); + if (!res.ok) throw new Error(`${res.status}`); + return res.json(); +} + // Reusable SWR hooks // https://swr.vercel.app/ export function useProducts() { @@ -82,6 +88,12 @@ export const useOrder = (orderId: number) => { }; } +export function useGqlCheck() { + const { data, error } = useSWR('/api/gql-check', simpleFetcher); + const status = !data && !error ? 'checking' : error ? 'error' : 'ok'; + return { status }; +} + export const useShippingAndProductsInfo = (orderId: number) => { const { context } = useSession(); const params = new URLSearchParams({ context }).toString(); diff --git a/package-lock.json b/package-lock.json index 114f0821..9af306b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,9 @@ "better-sqlite3": "^11.10.0", "dotenv": "^16.3.0", "firebase-admin": "^13.3.0", + "gql.tada": "^1.9.2", + "graphql": "^16.13.2", + "graphql-request": "^7.4.0", "jsonwebtoken": "^9.0.1", "mysql2": "^3.9.4", "next": "^14.2.35", @@ -25,6 +28,7 @@ "swr": "^1.3.0" }, "devDependencies": { + "@0no-co/graphqlsp": "^1.15.4", "@testing-library/dom": "^7.31.2", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.0", @@ -52,6 +56,34 @@ "npm": ">=8 <10" } }, + "node_modules/@0no-co/graphql.web": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz", + "integrity": "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==", + "license": "MIT", + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + }, + "peerDependenciesMeta": { + "graphql": { + "optional": true + } + } + }, + "node_modules/@0no-co/graphqlsp": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/@0no-co/graphqlsp/-/graphqlsp-1.15.4.tgz", + "integrity": "sha512-Nt1DVHcZ08lKRKwhiU0amXH77fSdrO6DzyjLE0DkCxfbM/N1SAs32d76y1xtCzM5H9eT0iDS7SdksgRXWJu05g==", + "license": "MIT", + "dependencies": { + "@gql.tada/internal": "^1.0.0", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0 || ^6.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -1029,6 +1061,54 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@gql.tada/cli-utils": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@gql.tada/cli-utils/-/cli-utils-1.7.3.tgz", + "integrity": "sha512-3iQY5E/jvv3Lnh6D1Mh7zr+Bb9C/TGk1DHkm+lbIjQBnZAu2m+BcTcr1e3spUt6Aa6HG/xAN2XxpbWw9oZALEg==", + "license": "MIT", + "dependencies": { + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/internal": "1.0.9", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + }, + "peerDependencies": { + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/svelte-support": "1.0.2", + "@gql.tada/vue-support": "1.0.2", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "@gql.tada/svelte-support": { + "optional": true + }, + "@gql.tada/vue-support": { + "optional": true + } + } + }, + "node_modules/@gql.tada/internal": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@gql.tada/internal/-/internal-1.0.9.tgz", + "integrity": "sha512-Bp8yi+kLrzIJ3l5Dfxhz48H4OCH2LCX+pShaPcJgh+oiBt6clrjUKDYNDD3Z78aDQ3+Tyrxe4dd0MfLgpSLPPg==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.5" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@grpc/grpc-js": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.3.tgz", @@ -6921,12 +7001,52 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gql.tada": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/gql.tada/-/gql.tada-1.9.2.tgz", + "integrity": "sha512-QxRHVpxtrOVdYXz6oavq0lBM+Zdp0swapLGJcD4SLpXDcsD337BHDFrzqqjfkbepv0sSAiO0LGabu1kI5D5Gyg==", + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.5", + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/cli-utils": "1.7.3", + "@gql.tada/internal": "1.0.9" + }, + "bin": { + "gql-tada": "bin/cli.js", + "gql.tada": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^6.0.0" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/graphql": { + "version": "16.13.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz", + "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-request": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.4.0.tgz", + "integrity": "sha512-xfr+zFb/QYbs4l4ty0dltqiXIp07U6sl+tOKAb0t50/EnQek6CVVBLjETXi+FghElytvgaAWtIOt3EV7zLzIAQ==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, "node_modules/gtoken": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", @@ -13360,7 +13480,6 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -13823,6 +13942,21 @@ } }, "dependencies": { + "@0no-co/graphql.web": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz", + "integrity": "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==", + "requires": {} + }, + "@0no-co/graphqlsp": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/@0no-co/graphqlsp/-/graphqlsp-1.15.4.tgz", + "integrity": "sha512-Nt1DVHcZ08lKRKwhiU0amXH77fSdrO6DzyjLE0DkCxfbM/N1SAs32d76y1xtCzM5H9eT0iDS7SdksgRXWJu05g==", + "requires": { + "@gql.tada/internal": "^1.0.0", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + } + }, "@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -14601,6 +14735,30 @@ } } }, + "@gql.tada/cli-utils": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@gql.tada/cli-utils/-/cli-utils-1.7.3.tgz", + "integrity": "sha512-3iQY5E/jvv3Lnh6D1Mh7zr+Bb9C/TGk1DHkm+lbIjQBnZAu2m+BcTcr1e3spUt6Aa6HG/xAN2XxpbWw9oZALEg==", + "requires": { + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/internal": "1.0.9", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + } + }, + "@gql.tada/internal": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@gql.tada/internal/-/internal-1.0.9.tgz", + "integrity": "sha512-Bp8yi+kLrzIJ3l5Dfxhz48H4OCH2LCX+pShaPcJgh+oiBt6clrjUKDYNDD3Z78aDQ3+Tyrxe4dd0MfLgpSLPPg==", + "requires": { + "@0no-co/graphql.web": "^1.0.5" + } + }, + "@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "requires": {} + }, "@grpc/grpc-js": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.3.tgz", @@ -19131,11 +19289,35 @@ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "devOptional": true }, + "gql.tada": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/gql.tada/-/gql.tada-1.9.2.tgz", + "integrity": "sha512-QxRHVpxtrOVdYXz6oavq0lBM+Zdp0swapLGJcD4SLpXDcsD337BHDFrzqqjfkbepv0sSAiO0LGabu1kI5D5Gyg==", + "requires": { + "@0no-co/graphql.web": "^1.0.5", + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/cli-utils": "1.7.3", + "@gql.tada/internal": "1.0.9" + } + }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "graphql": { + "version": "16.13.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz", + "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==" + }, + "graphql-request": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.4.0.tgz", + "integrity": "sha512-xfr+zFb/QYbs4l4ty0dltqiXIp07U6sl+tOKAb0t50/EnQek6CVVBLjETXi+FghElytvgaAWtIOt3EV7zLzIAQ==", + "requires": { + "@graphql-typed-document-node/core": "^3.2.0" + } + }, "gtoken": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", @@ -23963,8 +24145,7 @@ "typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==" }, "unbox-primitive": { "version": "1.0.2", diff --git a/package.json b/package.json index 0adafe8d..ed8ee471 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "start": "next start -p $PORT", "test": "jest --updateSnapshot", "lint": "eslint . --ext .ts,.tsx,.js", - "db:setup": "node scripts/db.js" + "db:setup": "node scripts/db.js", + "generate-schema": "node scripts/generate-schema.mjs" }, "keywords": [], "author": "", @@ -26,6 +27,9 @@ "better-sqlite3": "^11.10.0", "dotenv": "^16.3.0", "firebase-admin": "^13.3.0", + "gql.tada": "^1.9.2", + "graphql": "^16.13.2", + "graphql-request": "^7.4.0", "jsonwebtoken": "^9.0.1", "mysql2": "^3.9.4", "next": "^14.2.35", @@ -36,6 +40,7 @@ "swr": "^1.3.0" }, "devDependencies": { + "@0no-co/graphqlsp": "^1.15.4", "@testing-library/dom": "^7.31.2", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.0", diff --git a/pages/api/gql-check.ts b/pages/api/gql-check.ts new file mode 100644 index 00000000..860afd39 --- /dev/null +++ b/pages/api/gql-check.ts @@ -0,0 +1,24 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import { graphql } from '../../lib/graphql'; +import { accountClient } from '../../lib/account-client'; + +const AccountQuery = graphql(` + query GqlCheck { + account { + id + accountInfo { + name + } + } + } +`); + +export default async function gqlCheck(_req: NextApiRequest, res: NextApiResponse) { + try { + const data = await accountClient.request(AccountQuery); + res.status(200).json(data); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + res.status(500).json({ message }); + } +} diff --git a/pages/gql-check.tsx b/pages/gql-check.tsx new file mode 100644 index 00000000..a206b497 --- /dev/null +++ b/pages/gql-check.tsx @@ -0,0 +1,27 @@ +import { Box, H3, Panel, Text } from '@bigcommerce/big-design'; +import useSWR from 'swr'; + +const fetcher = (url: string) => fetch(url).then(r => r.json()); + +const GqlCheck = () => { + const { data, error } = useSWR('/api/gql-check', fetcher); + + return ( + + + {!data && !error && Checking...} + {error && Error: {error.message}} + {data && ( + <> +

Connected

+
+                            {JSON.stringify(data, null, 2)}
+                        
+ + )} +
+
+ ); +}; + +export default GqlCheck; diff --git a/schema.graphql b/schema.graphql new file mode 100644 index 00000000..08ad8dce --- /dev/null +++ b/schema.graphql @@ -0,0 +1,1265 @@ +"""Account mutations.""" +type AccountMutations { + """Adds a user to an account.""" + addUserToAccount(input: AddUserToAccountInput!): AddUserToAccountResult + + """Removes a user from an account and all associated entities.""" + removeUserFromAccount(input: RemoveUserFromAccountInput!): RemoveUserFromAccountResult +} + +input AddUserToAccountInput { + """The email of the user to be added to the account.""" + email: String! +} + +type AddUserToAccountResult { + """An account the user was added to.""" + account: Account +} + +input AddUserToStoreInput { + """The store the user is to be added.""" + storeId: ID! + + """The user to be added to the store.""" + user: UserIdentifierInput! +} + +type AddUserToStoreResult { + """The ID of the object.""" + storeId: ID! +} + +"""App mutations.""" +type AppMutations { + """Creates an Amazon EventSource.""" + createEventSource(input: CreateEventSourceInput!): CreateEventSourceResult + + """Deletes an Amazon EventSource.""" + deleteEventSource(input: DeleteEventSourceInput!): DeleteEventSourceResult +} + +input CancelSubscriptionInput { + """The ID of the Subscription.""" + id: ID! +} + +type CancelSubscriptionResult { + """The ID of the Subscription.""" + subscriptionId: ID + + """The date the subscription will be cancelled.""" + cancelledAt: DateTime +} + +"""Input needed to create a CheckoutItem.""" +input CheckoutItemInput { + """A description the line item.""" + description: String! + + """How to bill the Merchant for this Product.""" + pricingPlan: PricingPlanInput! + + """ + The product the Merchant will be entitled to after the Checkout is complete. + """ + product: CheckoutItemProductInput + + """ + URL used to redirect the Merchant after successfully completing the Checkout. + """ + redirectUrl: String! + + """The scope for this line item (e.g. store, account).""" + scope: CheckoutItemScopeInput + + """ + When left blank, a new subscription will be created upon successful completion of the Checkout. To create a Checkout for updating an existing subscription, include the subscription ID of the existing subscription, which will be updated upon successful completion of the Checkout. + """ + subscriptionId: ID +} + +""" +The input information needed to set the CheckoutItemProduct on a CheckoutItem. +""" +input CheckoutItemProductInput { + """Product's unique identifier.""" + id: ID + + """ + A description of the product level, if applicable. (e.g. app tier for an application). + """ + productLevel: String + + """The type of this product (e.g. APPLICATION).""" + type: CheckoutItemProductType +} + +""" +The input information needed to set the CheckoutItemScope on a CheckoutItem. +""" +input CheckoutItemScopeInput { + """Scope's unique identifier.""" + id: ID + + """Scope of access for the associated product (e.g. STORE).""" + type: CheckoutItemScopeType +} + +"""Checkout mutations.""" +type CheckoutMutations { + """Creates a Checkout for a Merchant.""" + createCheckout(input: CreateCheckoutInput!): CreateCheckoutResult +} + +input CreateCheckoutInput { + """Merchant's account UUID.""" + accountId: ID! + + """ + Product and billing details used to create a Checkout for the Merchant. + """ + items: [CheckoutItemInput!]! +} + +type CreateCheckoutResult { + """The Checkout that is created as a result of mutation.""" + checkout: Checkout +} + +input CreateEventSourceInput { + """The ID of an app.""" + appId: ID! + + """An Amazon EventSource account ID.""" + awsAccount: String! + + """An event source name.""" + eventSourceName: String! + + """The region of an event source.""" + eventSourceRegion: AwsRegion! +} + +type CreateEventSourceResult { + """The Amazon EventSource created as a result of mutation.""" + eventSource: AwsEventSource +} + +input CreateUserInput { + """The email of the user to be added to the account.""" + email: String! + + """The first name of the user to be added to the account.""" + firstName: String! + + """The last name of the user to be added to the account.""" + lastName: String! + + """The locale of the user to be added to the account.""" + locale: String! +} + +type CreateUserResult { + """Created user.""" + user: User +} + +input CreateUserWithPasswordInput { + """The email of the user to be added to the account.""" + email: String! + + """The first name of the user to be added to the account.""" + firstName: String! + + """The last name of the user to be added to the account.""" + lastName: String! + + """The locale of the user to be added to the account.""" + locale: String! + + """The password of the user to be added to the account.""" + password: String! + + """Enter password again to confirm.""" + passwordConfirmation: String! +} + +type CreateUserWithPasswordResult { + """Created user.""" + user: User +} + +"""Currency Code.""" +enum CurrencyCode { + AED + AFN + ALL + AMD + ANG + AOA + ARS + AUD + AWG + AZN + BAM + BBD + BDT + BGN + BHD + BIF + BMD + BND + BOB + BOV + BRL + BSD + BTN + BWP + BYN + BYR + BZD + CAD + CDF + CHE + CHF + CHW + CLF + CLP + CNY + COP + COU + CRC + CUC + CUP + CVE + CZK + DJF + DKK + DOP + DZD + EGP + ERN + ETB + EUR + FJD + FKP + GBP + GEL + GHS + GIP + GMD + GNF + GTQ + GYD + HKD + HNL + HRK + HTG + HUF + IDR + ILS + INR + IQD + IRR + ISK + JMD + JOD + JPY + KES + KGS + KHR + KMF + KPW + KRW + KWD + KYD + KZT + LAK + LBP + LKR + LRD + LSL + LYD + MAD + MDL + MGA + MKD + MMK + MNT + MOP + MRO + MRU + MUR + MVR + MWK + MXN + MYR + MZN + NAD + NGN + NIO + NOK + NPR + NZD + OMR + PAB + PEN + PGK + PHP + PKR + PLN + PYG + QAR + RON + RSD + RUB + RWF + SAR + SBD + SCR + SDG + SEK + SGD + SHP + SLL + SOS + SRD + SSP + STD + STN + SVC + SYP + SZL + THB + TJS + TMT + TND + TOP + TRY + TTD + TWD + TZS + UAH + UGX + USD + USN + UYI + UYU + UZS + VEF + VES + VND + VUV + WST + XAF + XAG + XAU + XBA + XBB + XBC + XBD + XCD + XDR + XOF + XPD + XPF + XPT + XSU + XTS + XUA + XXX + YER + ZAR + ZMW + ZWL +} + +input DeleteEventSourceInput { + """The ID of an app.""" + appId: ID! + + """The ID of the object.""" + id: ID! +} + +type DeleteEventSourceResult { + """The ID of the object.""" + deletedEventSourceId: ID! + + """ + The IDs of affected webhooks by event source delete mutation. Their status is automatically changed to disabled. + """ + affectedWebhooksIds: [ID!]! +} + +"""Input to create a Money object.""" +input MoneyInput { + """The ISO 4217 currency code.""" + currencyCode: CurrencyCode! + + """ + The monetary amount. Must be a positive decimal number. For example, 10.10 with a currency code of USD represents $10.10. + """ + value: BigDecimal! +} + +type Mutation { + """Account mutations.""" + account: AccountMutations! + + """Checkout mutations.""" + checkout: CheckoutMutations! + + """Subscription mutations.""" + subscription: SubscriptionMutations! + + """User mutations.""" + user: UserMutations! + + """App mutations.""" + app: AppMutations! +} + +"""The inputs needed to set the PricingPlan when creating a CheckoutItem.""" +input PricingPlanInput { + """The billing interval for this line item (e.g. ONCE, MONTH, etc).""" + interval: CheckoutItemPricingInterval! + + """The price to charge the merchant per billing interval.""" + price: MoneyInput! + + """The number of days to delay billing and allow for a free trial.""" + trialDays: Int +} + +"""Input needed to remove a user from an account.""" +input RemoveUserFromAccountInput { + """The email of the user to be removed from the account.""" + email: String + + """The id of the user to be removed.""" + userId: ID +} + +type RemoveUserFromAccountResult { + """The ID of the object.""" + accountId: ID! + + """The ID of the object.""" + userId: ID! +} + +input RemoveUserFromStoreInput { + """The store from which user is to be removed.""" + storeId: ID! + + """The user to be removed.""" + userId: ID! +} + +type RemoveUserFromStoreResult { + """The store from which the user is removed from.""" + storeId: ID! + + """Removed user.""" + user: User +} + +"""Subscription mutations.""" +type SubscriptionMutations { + """Cancels a Subscription for a Merchant.""" + cancelSubscription(input: CancelSubscriptionInput!): CancelSubscriptionResult +} + +"""The user to add to the store.""" +input UserIdentifierInput { + """The email of the user to be added to the store.""" + email: String + + """The ID of the user to be added.""" + id: ID +} + +"""User mutations.""" +type UserMutations { + """Creates a user with password.""" + createUserWithPassword(input: CreateUserWithPasswordInput!): CreateUserWithPasswordResult @deprecated(reason: "This mutation is deprecated and will be removed in a future release. Use `createUser` instead.") + + """Creates a user.""" + createUser(input: CreateUserInput!): CreateUserResult + + """Adds a user to a store.""" + addUserToStore(input: AddUserToStoreInput!): AddUserToStoreResult + + """Removes user from a store.""" + removeUserFromStore(input: RemoveUserFromStoreInput!): RemoveUserFromStoreResult +} + +"""An account.""" +type Account { + """Provides account details.""" + accountInfo: AccountInfo! + + """Apps owned by the account.""" + apps(before: String, after: String, first: Int, last: Int): AppsConnection! + + """ + Allows for billing a Merchant for Products on a one-time or recurring basis. + """ + checkout( + """The ID of the object.""" + id: ID! + ): Checkout + + """The ID of the object.""" + id: ID! + + """The stores related to an account.""" + stores( + """Filter stores by IDs or store hashes.""" + filter: StoreFilterInput + before: String + after: String + first: Int + last: Int + ): StoreConnection! + + """Subscriptions belonging to the account.""" + subscriptions(filters: SubscriptionFiltersInput, first: Int, after: String, last: Int, before: String): SubscriptionConnection! + + """Users belonging to the account.""" + users(before: String, after: String, first: Int, last: Int): UserConnection! +} + +"""Account details.""" +type AccountInfo { + """The name of an account.""" + name: String! +} + +"""An application.""" +type App { + """The ID of the object.""" + id: ID! + + """The app name.""" + name: String! + + """Amazon EventSources.""" + eventSources(before: String, after: String, first: Int, last: Int): AwsEventSourcesConnection! +} + +"""A connection to a list of items.""" +type AppsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [AppsEdge] +} + +"""An edge in a connection.""" +type AppsEdge { + """The item at the end of the edge.""" + node: App! + + """A cursor for use in pagination.""" + cursor: String! +} + +"""An Amazon EventSource.""" +type AwsEventSource { + """The ID of an object.""" + id: ID! + + """An Amazon EventSource account ID.""" + awsAccount: String! + + """An event source name.""" + eventSourceName: String! + + """The region of an event source.""" + eventSourceRegion: AwsRegion! + + """An Amazon EventSource resource name.""" + arn: String! + + """Number of webhooks attached to an Amazon EventSource.""" + webhooksCount: Long! +} + +"""A connection to a list of items.""" +type AwsEventSourcesConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [AwsEventSourcesEdge] +} + +"""An edge in a connection.""" +type AwsEventSourcesEdge { + """The item at the end of the edge.""" + node: AwsEventSource! + + """A cursor for use in pagination.""" + cursor: String! +} + +"""The Amazon EventSource region.""" +enum AwsRegion { + """af-south-1""" + AF_SOUTH_1 + + """ap-east-1""" + AP_EAST_1 + + """ap-northeast-1""" + AP_NORTHEAST_1 + + """ap-northeast-2""" + AP_NORTHEAST_2 + + """ap-northeast-3""" + AP_NORTHEAST_3 + + """ap-southeast-1""" + AP_SOUTHEAST_1 + + """ap-southeast-2""" + AP_SOUTHEAST_2 + + """ap-southeast-3""" + AP_SOUTHEAST_3 + + """ap-southeast-4""" + AP_SOUTHEAST_4 + + """ap-south-1""" + AP_SOUTH_1 + + """ap-south-2""" + AP_SOUTH_2 + + """ca-central-1""" + CA_CENTRAL_1 + + """eu-central-1""" + EU_CENTRAL_1 + + """eu-central-2""" + EU_CENTRAL_2 + + """eu-north-1""" + EU_NORTH_1 + + """eu-south-1""" + EU_SOUTH_1 + + """eu-south-2""" + EU_SOUTH_2 + + """eu-west-1""" + EU_WEST_1 + + """eu-west-2""" + EU_WEST_2 + + """eu-west-3""" + EU_WEST_3 + + """il-central-1""" + IL_CENTRAL_1 + + """me-central-1""" + ME_CENTRAL_1 + + """sa-east-1""" + SA_EAST_1 + + """us-east-1""" + US_EAST_1 + + """us-east-2""" + US_EAST_2 + + """us-west-1""" + US_WEST_1 + + """us-west-2""" + US_WEST_2 +} + +""" +A container for all information needed to charge a Merchant for products on a one-time or recurring basis. +""" +type Checkout implements Node { + """The ID of the object.""" + id: ID! + + """The merchant's account UUID.""" + accountId: ID! + + """ + The URL for redirecting the merchant to the BigCommerce hosted checkout page. + """ + checkoutUrl: String! + + """The item(s) associated with the checkout.""" + items(before: String, after: String, first: Int, last: Int): CheckoutItemConnection! + + """The current status of the checkout.""" + status: CheckoutStatus +} + +""" +Individual line items describing one-time or recurring charges to be billed to the Merchant upon completing the Checkout. +""" +type CheckoutItem { + """A description of this line item.""" + description: String + + """The pricing plan denoting how to bill the Merchant for this Product.""" + pricingPlan: CheckoutItemPricingPlan! + + """ + The product the Merchant will be entitled to after the Checkout is complete. + """ + product: CheckoutItemProduct! + + """The URL used to redirect the Merchant after completing the Checkout.""" + redirectUrl: String + + """The scope for this Line Item (e.g. store, account).""" + scope: CheckoutItemScope! + + """The status of this line item.""" + status: CheckoutItemStatus + + """ + If available, the ID of the Subscription associated with this Line Item. + """ + subscriptionId: ID +} + +"""A connection to a list of items.""" +type CheckoutItemConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [CheckoutItemEdge] +} + +"""An edge in a connection.""" +type CheckoutItemEdge { + """The item at the end of the edge.""" + node: CheckoutItem! + + """A cursor for use in pagination.""" + cursor: String! +} + +""" +The billing interval representing how often to charge the Merchant for this product. +""" +enum CheckoutItemPricingInterval { + """ + Charge the Merchant on a recurring basis each year on their Account's billing day. + """ + ANNUAL + + """ + Charge the Merchant on a recurring basis each month on their Account's billing day. + """ + MONTH + + """Charge the Merchant only one time, immediately.""" + ONCE + + """ + Charge the Merchant on a recurring basis each quarter on their Account's billing day. + """ + QUARTER + + """ + Charge the Merchant on a recurring basis every six months on their Account's billing day. + """ + SEMIANNUAL +} + +"""The container for all pricing information.""" +type CheckoutItemPricingPlan { + """The billing interval for the subscription.""" + interval: CheckoutItemPricingInterval + + """The amount to charge for each interval.""" + price: Money + + """Number of days to delay billing to offer a free trial period.""" + trialDays: Int +} + +"""The product the Merchant is purchasing.""" +type CheckoutItemProduct { + """The unique ID of the product.""" + id: ID + + """ + A description of the product level, if applicable (e.g. app tier for an application). + """ + productLevel: String + + """The type of product (e.g. APPLICATION).""" + type: CheckoutItemProductType +} + +"""The type of product.""" +enum CheckoutItemProductType { + """Type for applications available in the BigCommerce App Marketplace.""" + APPLICATION +} + +"""The scope to which the purchase should be limited.""" +type CheckoutItemScope { + """The unique ID of the scope.""" + id: ID + + """Where to scope access for this item (e.g. STORE).""" + type: CheckoutItemScopeType +} + +""" +The scope of access to be granted upon successful completion of the Checkout. +""" +enum CheckoutItemScopeType { + """Limits access for the purchased product to a particular Store.""" + STORE +} + +"""The current status of the Checkout Item.""" +enum CheckoutItemStatus { + """ + The Merchant has been invoiced and any necessary provisioning or Subscription updates have been completed. + """ + COMPLETE + + """ + This CheckoutItem has been created, but has not yet been accepted by the Merchant. + """ + PENDING + + """ + This CheckoutItem has been approved by the Merchant and BigCommerce is in the process of handling any provisioning or Subscription updates (if necessary). + """ + PROCESSING +} + +"""The current status of the Checkout.""" +enum CheckoutStatus { + """ + The Merchant has been invoiced and any necessary provisioning or Subscription updates have been completed. + """ + COMPLETE + + """ + This Checkout has been created, but has not yet been accepted by the Merchant. + """ + PENDING + + """ + This Checkout has been approved by the Merchant and BigCommerce is in the process of handling any provisioning or Subscription updates (if necessary). + """ + PROCESSING +} + +"""Collection details.""" +type CollectionInfo { + """The total number of items in the collection.""" + totalItems: Long +} + +"""ISO-8601 formatted date in UTC""" +scalar DateTime + +"""A money object - includes currency code and a money amount.""" +type Money { + """Currency code of the current money.""" + currencyCode: String! + + """The amount of money.""" + value: BigDecimal! +} + +"""An object with an ID.""" +interface Node { + """The ID of the object.""" + id: ID! +} + +"""Information about pagination in a connection.""" +type PageInfo { + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String +} + +type Query { + """An account.""" + account: Account! + + """Fetches an object given its ID.""" + node( + """The ID of the object.""" + id: ID! + ): Node + + """Fetches objects given their IDs.""" + nodes( + """The IDs of the objects.""" + ids: [ID!]! + ): [Node]! + + """System settings.""" + system: System! +} + +"""A store related to an account.""" +type Store { + """The ID of the object.""" + id: ID! + + """The list of store apps.""" + apps(before: String, after: String, first: Int, last: Int): AppsConnection! + + """The store name for the store.""" + name: String! + + """The store hash for the store.""" + storeHash: String! + + """Users belonging to the store.""" + users(before: String, after: String, first: Int, last: Int): StoreUserConnection! +} + +"""A connection to a list of items.""" +type StoreConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [StoreEdge] +} + +"""An edge in a connection.""" +type StoreEdge { + """The item at the end of the edge.""" + node: Store! + + """A cursor for use in pagination.""" + cursor: String! +} + +"""The input needed to filter stores.""" +input StoreFilterInput { + """The ID of the stores.""" + ids: [ID!] + + """The store hashes.""" + storeHashes: [String!] +} + +"""A user belonging to a store.""" +type StoreUser { + """The ID of the object.""" + id: ID! + + """Apps accessible by the user on the store.""" + apps(before: String, after: String, first: Int, last: Int): AppsConnection! + + """The email for the user.""" + email: String! + + """First name.""" + firstName: String! + + """When the user last logged in.""" + lastLoginAt: DateTime + + """Last name.""" + lastName: String! + + """User preferred locale.""" + locale: String! + + """The permissions for the user on the store.""" + permissions: [String!]! + + """Current status of the user for the store.""" + status: StoreUserStatus + + """When was the user last updated.""" + updatedAt: DateTime +} + +"""A connection to a list of items.""" +type StoreUserConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [StoreUserEdge] + + """Collection details.""" + collectionInfo: CollectionInfo +} + +"""An edge in a connection.""" +type StoreUserEdge { + """The item at the end of the edge.""" + node: StoreUser! + + """A cursor for use in pagination.""" + cursor: String! +} + +"""The status of the user on the store.""" +enum StoreUserStatus { + """The user is active.""" + ACTIVE + + """The user is inactive.""" + INACTIVE +} + +""" +A one-time or recurring charge for a Product created as a result of a Merchant completing a Checkout. +""" +type Subscription implements Node { + """The ID of the object.""" + id: ID! + + """ID of the account associated with the subscription.""" + accountId: ID! + + """ + The date when the Subscription becomes active, ending any trial period, and billing the Merchant for the first time. + """ + activationDate: DateTime! + + """The frequency of charges for the Merchant.""" + billingInterval: SubscriptionBillingInterval! + + """The date the subscription was created.""" + createdAt: DateTime! + + """The end of the current billing interval for the Merchant.""" + currentPeriodEnd: DateTime + + """The price the Merchant pays each billing interval.""" + pricePerInterval: Money + + """The product the Merchant is entitled to with this Subscription.""" + product: SubscriptionProduct! + + """The status of this subscription.""" + status: SubscriptionStatus + + """The scope of the subscription.""" + scope: SubscriptionScope! + + """The date the subscription was last updated.""" + updatedAt: DateTime! +} + +"""The frequency of charges for a Merchant.""" +enum SubscriptionBillingInterval { + """ + The Merchant will be billed on a recurring basis each year on their Account's billing day. + """ + ANNUAL + + """ + The Merchant will be billed on a recurring basis each month on their Account's billing day. + """ + MONTH + + """ + The Merchant was billed one time, at creation, and will not charge on a recurring basis. + """ + NONE + + """The Merchant will be billed only once.""" + ONCE + + """ + The Merchant will be billed on a recurring basis each quarter on their Account's billing day. + """ + QUARTER + + """ + The Merchant will be billed on a recurring basis every six months on their Account's billing day. + """ + SEMIANNUAL +} + +"""A connection to a list of subscriptions.""" +type SubscriptionConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of subscription edges.""" + edges: [SubscriptionEdge] +} + +"""An edge in a connection.""" +type SubscriptionEdge { + """The item at the end of the edge.""" + node: Subscription! + + """A cursor for use in pagination.""" + cursor: String! +} + +input SubscriptionFiltersInput { + """A list of subscription IDs for filtering the query.""" + ids: [ID!] + + """A product ID for filtering the query.""" + productId: ID + + """A product type for filtering the query.""" + productType: SubscriptionProductType + + """A scope ID for filtering the query.""" + scopeId: ID + + """A scope type for filtering the query.""" + scopeType: SubscriptionScopeType + + """A status for filtering the query.""" + status: SubscriptionStatus + + """A date for filtering the query.""" + updatedAfter: DateTime +} + +"""The product the Merchant is entitled to with this Subscription.""" +type SubscriptionProduct { + """The unique ID of the product.""" + id: ID! + + """The type of product (e.g. APP).""" + type: SubscriptionProductType + + """ + A description of the product level, if applicable (e.g. app tier for an application). + """ + productLevel: String! +} + +"""The product the Merchant purchased.""" +enum SubscriptionProductType { + """Type for applications available in the BigCommerce App Marketplace.""" + APPLICATION + + """This product type is not supported""" + NONE +} + +"""The scope of the subscription.""" +type SubscriptionScope { + """The unique ID of the scope.""" + id: ID! + + """The type of scope (e.g. STORE).""" + type: SubscriptionScopeType +} + +"""The type of scope of the subscription.""" +enum SubscriptionScopeType { + """The subscription is scoped to a store.""" + STORE +} + +"""The current status of the Subscription.""" +enum SubscriptionStatus { + """ + The Subscription is active and will continue billing the Merchant according to its pricing plan. + """ + ACTIVE + + """ + The Subscription has been cancelled and is no longer billing the Merchant. + """ + CANCELLED + + """ + The Subscription has been suspended. The Merchant is no longer being billing according to the pricing plan. However, this Subscription may be reactivated. + """ + SUSPENDED +} + +"""System settings.""" +type System { + """The current time.""" + time: Long! +} + +"""A user belonging to an account.""" +type User { + """The ID of the object.""" + id: ID! + + """The email for the user.""" + email: String! + + """First name.""" + firstName: String! + + """Last name.""" + lastName: String! + + """User preferred locale.""" + locale: String! +} + +"""A connection to a list of items.""" +type UserConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [UserEdge] + + """Collection details.""" + collectionInfo: CollectionInfo +} + +"""An edge in a connection.""" +type UserEdge { + """The item at the end of the edge.""" + node: User! + + """A cursor for use in pagination.""" + cursor: String! +} + +""" +The `BigDecimal` scalar type represents signed fractional values with arbitrary precision. +""" +scalar BigDecimal + +""" +The `Long` scalar type represents non-fractional signed whole numeric values. Long can represent values between -(2^63) and 2^63 - 1. +""" +scalar Long \ No newline at end of file diff --git a/scripts/generate-schema.mjs b/scripts/generate-schema.mjs new file mode 100644 index 00000000..29d74b9c --- /dev/null +++ b/scripts/generate-schema.mjs @@ -0,0 +1,39 @@ +#!/usr/bin/env node +import { writeFileSync } from 'fs'; +import { createRequire } from 'module'; +import { getIntrospectionQuery, buildClientSchema, printSchema } from 'graphql'; + +const require = createRequire(import.meta.url); +require('dotenv').config({ path: '.env' }); + +const accountUuid = process.env.BC_ACCOUNT_UUID; +const apiToken = process.env.BC_ACCOUNT_API_TOKEN; + +if (!accountUuid || !apiToken) { + console.error('Missing BC_ACCOUNT_UUID or BC_ACCOUNT_API_TOKEN in .env'); + process.exit(1); +} + +const url = `https://api.bigcommerce.com/accounts/${accountUuid}/graphql`; + +console.log(`Fetching schema from ${url}...`); + +const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Auth-Token': apiToken, + }, + body: JSON.stringify({ query: getIntrospectionQuery() }), +}); + +if (!res.ok) { + const text = await res.text(); + console.error(`Failed: ${res.status} ${res.statusText}\n${text}`); + process.exit(1); +} + +const { data } = await res.json(); +const schema = printSchema(buildClientSchema(data)); +writeFileSync('schema.graphql', schema); +console.log('Wrote schema.graphql'); diff --git a/tsconfig.json b/tsconfig.json index 5c733f91..a5ce05ad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,12 @@ { "compilerOptions": { + "plugins": [ + { + "name": "@0no-co/graphqlsp", + "schema": "./schema.graphql", + "tadaOutputLocation": "./graphql-env.d.ts" + } + ], "target": "es5", "lib": [ "dom", From cdad728b5cf1c114c8c2596d513a68ea526ac8bf Mon Sep 17 00:00:00 2001 From: Sasha Goloshchapov Date: Tue, 28 Apr 2026 01:22:44 -0500 Subject: [PATCH 05/18] chore: formatting --- .editorconfig | 9 + .husky/pre-commit | 1 + .prettierrc | 6 + .vscode/settings.json | 10 + components/error.tsx | 32 +- components/form.tsx | 243 +-- components/header.tsx | 199 ++- components/innerHeader.tsx | 50 +- components/loading.tsx | 18 +- context/session.tsx | 42 +- graphql-env.d.ts | 16 +- jest.config.js | 2 +- jest.setup.ts | 10 +- lib/account-client.ts | 20 +- lib/auth.ts | 128 +- lib/db.ts | 4 +- lib/dbs/firebase.ts | 142 +- lib/dbs/memory.ts | 156 +- lib/dbs/mysql.ts | 140 +- lib/dbs/sqlite.ts | 143 +- lib/graphql.ts | 12 +- lib/hooks.ts | 188 +-- package-lock.json | 1306 ++++++++++++++++- package.json | 9 +- pages/_app.tsx | 44 +- pages/_document.tsx | 17 +- pages/api/auth.ts | 24 +- pages/api/gql-check.ts | 41 +- pages/api/load.ts | 30 +- pages/api/logout.ts | 25 +- pages/api/orders/[orderId]/index.ts | 54 +- .../api/orders/[orderId]/shipping_products.ts | 58 +- pages/api/products/[pid].ts | 75 +- pages/api/products/index.ts | 31 +- pages/api/products/list.ts | 36 +- pages/api/removeUser.ts | 25 +- pages/api/uninstall.ts | 25 +- pages/gql-check.tsx | 44 +- pages/index.tsx | 72 +- pages/orders/[orderId]/index.tsx | 157 +- pages/orders/[orderId]/modal.tsx | 220 +-- .../productAppExtension/[productId]/index.tsx | 74 +- pages/products/[pid].tsx | 114 +- pages/products/index.tsx | 220 +-- scripts/bcSdk.js | 37 +- scripts/db.js | 43 +- test/mocks/hooks.ts | 53 +- test/pages/index.spec.tsx | 20 +- test/pages/productAppExtension/index.spec.tsx | 30 +- test/pages/products/[pid].spec.tsx | 40 +- test/pages/products/index.spec.tsx | 52 +- test/utils.tsx | 21 +- types/auth.ts | 34 +- types/data.ts | 22 +- types/db.ts | 26 +- types/error.ts | 6 +- types/index.ts | 10 +- types/order.ts | 82 +- 58 files changed, 3135 insertions(+), 1613 deletions(-) create mode 100644 .editorconfig create mode 100644 .husky/pre-commit create mode 100644 .prettierrc create mode 100644 .vscode/settings.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c6c8b362 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..2312dc58 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..fd36598c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..45fe25f6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/components/error.tsx b/components/error.tsx index e38986e2..7ba4c288 100644 --- a/components/error.tsx +++ b/components/error.tsx @@ -1,23 +1,23 @@ -import { H3, Panel } from '@bigcommerce/big-design'; -import { ErrorMessageProps, ErrorProps } from '../types'; +import { H3, Panel } from '@bigcommerce/big-design' +import { ErrorMessageProps, ErrorProps } from '../types' const ErrorContent = ({ message }: Pick) => ( - <> -

Failed to load

- {message} - + <> +

Failed to load

+ {message} + ) const ErrorMessage = ({ error, renderPanel = true }: ErrorMessageProps) => { - if (renderPanel) { - return ( - - - - ) - } + if (renderPanel) { + return ( + + + + ) + } - return -}; + return +} -export default ErrorMessage; +export default ErrorMessage diff --git a/components/form.tsx b/components/form.tsx index 1f494b2b..9aff0367 100644 --- a/components/form.tsx +++ b/components/form.tsx @@ -1,126 +1,149 @@ -import { Button, Checkbox, Flex, FormGroup, Input, Panel, Select, Form as StyledForm, Textarea } from '@bigcommerce/big-design'; -import { ChangeEvent, FormEvent, useState } from 'react'; -import { FormData, StringKeyValue } from '../types'; +import { + Button, + Checkbox, + Flex, + FormGroup, + Input, + Panel, + Select, + Form as StyledForm, + Textarea, +} from '@bigcommerce/big-design' +import { ChangeEvent, FormEvent, useState } from 'react' +import { FormData, StringKeyValue } from '../types' interface FormProps { - formData: FormData; - onCancel(): void; - onSubmit(form: FormData): void; + formData: FormData + onCancel(): void + onSubmit(form: FormData): void } const FormErrors = { - name: 'Product name is required', - price: 'Default price is required', -}; + name: 'Product name is required', + price: 'Default price is required', +} const Form = ({ formData, onCancel, onSubmit }: FormProps) => { - const { description, isVisible, name, price, type } = formData; - const [form, setForm] = useState({ description, isVisible, name, price, type }); - const [errors, setErrors] = useState({}); + const { description, isVisible, name, price, type } = formData + const [form, setForm] = useState({ + description, + isVisible, + name, + price, + type, + }) + const [errors, setErrors] = useState({}) - const handleChange = (event: ChangeEvent) => { - const { name: formName, value } = event.target || {}; - setForm(prevForm => ({ ...prevForm, [formName]: value })); + const handleChange = ( + event: ChangeEvent + ) => { + const { name: formName, value } = event.target || {} + setForm((prevForm) => ({ ...prevForm, [formName]: value })) - // Add error if it exists in FormErrors and the input is empty, otherwise remove from errors - !value && FormErrors[formName] - ? setErrors(prevErrors => ({ ...prevErrors, [formName]: FormErrors[formName] })) - : setErrors(({ [formName]: removed, ...prevErrors }) => ({ ...prevErrors })); - }; + // Add error if it exists in FormErrors and the input is empty, otherwise remove from errors + !value && FormErrors[formName] + ? setErrors((prevErrors) => ({ + ...prevErrors, + [formName]: FormErrors[formName], + })) + : setErrors(({ [formName]: removed, ...prevErrors }) => ({ + ...prevErrors, + })) + } - const handleSelectChange = (value: string) => { - setForm(prevForm => ({ ...prevForm, type: value })); - }; + const handleSelectChange = (value: string) => { + setForm((prevForm) => ({ ...prevForm, type: value })) + } - const handleCheckboxChange = (event: ChangeEvent) => { - const { checked, name: formName } = event.target || {}; - setForm(prevForm => ({ ...prevForm, [formName]: checked })); - }; + const handleCheckboxChange = (event: ChangeEvent) => { + const { checked, name: formName } = event.target || {} + setForm((prevForm) => ({ ...prevForm, [formName]: checked })) + } - const handleSubmit = (event: FormEvent) => { - event.preventDefault(); + const handleSubmit = (event: FormEvent) => { + event.preventDefault() - // If there are errors, do not submit the form - const hasErrors = Object.keys(errors).length > 0; - if (hasErrors) return; + // If there are errors, do not submit the form + const hasErrors = Object.keys(errors).length > 0 + if (hasErrors) return - onSubmit(form); - }; + onSubmit(form) + } - return ( - - - - - - - - - - - - - - - {/* Using description for demo purposes. Consider using a wysiwig instead (e.g. TinyMCE) */} -