import { readFile } from 'node:fs/promises'; import 'dotenv/config'; import sql from 'mssql'; import chalk from 'chalk'; import * as p from '@clack/prompts'; import LibDashStore from '../src/index.js'; import { MssqlProvider } from '../src/providers/mssql/index.js'; const config = { server: process.env.MSSQL_SERVER, database: process.env.MSSQL_DATABASE, user: process.env.MSSQL_USER, password: process.env.MSSQL_PASSWORD, options: { encrypt: false, trustServerCertificate: true, }, }; function printResult(label, value) { p.log.step(chalk.cyan(label)); const text = value === undefined ? chalk.dim('undefined') : typeof value === 'string' ? value : JSON.stringify(value, null, 2); console.log(chalk.white(text)); console.log(); } async function askText(message, { placeholder, validate } = {}) { const value = await p.text({ message, placeholder, validate }); if (p.isCancel(value)) return null; return value.trim(); } async function askInt(message, placeholder = '1') { return askText(message, { placeholder, validate: (v) => (!/^\d+$/.test(v?.trim() ?? '') ? 'Integer required' : undefined), }); } async function askNumber(message, placeholder = '19') { const value = await askText(message, { placeholder, validate: (v) => v?.trim() === '' || Number.isNaN(Number(v)) ? 'Number required' : undefined, }); if (value == null) return null; return Number(value); } async function shutdown(pool, code = 0) { try { await pool.close(); } catch { // ignore } process.exit(code); } const MENU = { setup: { label: chalk.cyan('setup'), hint: 'guided flows', items: [ { value: 'setup.company', label: 'company', hint: 'email + name + lang' }, ], }, company: { label: 'company', items: [ { value: 'company.create', label: 'create', hint: 'googleAuthId, iso639, name, taxes → result' }, { value: 'company.update', label: 'update', hint: 'id + fields → result' }, { value: 'company.list', label: 'list', hint: 'googleAuthId → result[]' }, { value: 'company.delete', label: 'delete', hint: 'companyId' }, ], }, lang: { label: 'lang', items: [ { value: 'lang.create', label: 'create', hint: 'iso639 → id' }, { value: 'lang.list', label: 'list', hint: '→ rows' }, { value: 'lang.delete', label: 'delete', hint: 'id' }, ], }, tax: { label: 'tax', items: [ { value: 'tax.create', label: 'create', hint: 'rate → id' }, { value: 'tax.list', label: 'list', hint: '→ rows' }, { value: 'tax.ensure', label: 'ensure', hint: 'rate → id' }, ], }, unit: { label: 'unit', items: [ { value: 'unit.list', label: 'list', hint: 'googleAuthId → rows', }, { value: 'unit.upsert', label: 'upsert', hint: 'googleAuthId, JSON unit → row', }, { value: 'unit.delete', label: 'delete', hint: 'googleAuthId, si', }, ], }, companysetting: { label: 'companysetting', items: [ { value: 'companysetting.list', label: 'list', hint: 'googleAuthId, frontendOnly? → rows', }, { value: 'companysetting.get', label: 'get', hint: 'googleAuthId, key → row', }, { value: 'companysetting.set', label: 'set', hint: 'googleAuthId, JSON setting → row', }, { value: 'companysetting.delete', label: 'delete', hint: 'googleAuthId, key', }, ], }, brand: { label: 'brand', items: [ { value: 'brand.list', label: 'list', hint: 'googleAuthId → rows', }, { value: 'brand.upsert', label: 'upsert', hint: 'googleAuthId, JSON brand → row', }, { value: 'brand.delete', label: 'delete', hint: 'googleAuthId, id', }, ], }, customergroup: { label: 'customergroup', items: [ { value: 'customergroup.list', label: 'list', hint: 'googleAuthId → rows' }, { value: 'customergroup.create', label: 'create', hint: 'googleAuthId, name → row' }, { value: 'customergroup.update', label: 'update', hint: 'googleAuthId, id, name → row' }, { value: 'customergroup.order', label: 'order', hint: 'googleAuthId, JSON customergroupIds → { customergroupIds }' }, { value: 'customergroup.delete', label: 'delete', hint: 'googleAuthId, id' }, ], }, entity: { label: 'entity', items: [ { value: 'entity.list', label: 'list', hint: 'googleAuthId, customergroupId?|role=customer → rows' }, { value: 'entity.getById', label: 'getById', hint: 'googleAuthId, id → row' }, { value: 'entity.create', label: 'create', hint: 'googleAuthId, JSON → row' }, { value: 'entity.update', label: 'update', hint: 'googleAuthId, id, JSON → row' }, { value: 'entity.delete', label: 'delete', hint: 'googleAuthId, id' }, { value: 'entity.peekNextNumber', label: 'peekNextNumber', hint: 'googleAuthId → next number' }, { value: 'entity.isNumberFree', label: 'isNumberFree', hint: 'googleAuthId, number, excludeEntityId? → bool' }, ], }, entityaddress: { label: 'entityaddress', items: [ { value: 'entityaddress.list', label: 'list', hint: 'googleAuthId, entityId → rows' }, { value: 'entityaddress.create', label: 'create', hint: 'googleAuthId, entityId, JSON → row' }, { value: 'entityaddress.update', label: 'update', hint: 'googleAuthId, entityId, id, JSON → row' }, { value: 'entityaddress.delete', label: 'delete', hint: 'googleAuthId, entityId, id' }, ], }, entitycontact: { label: 'entitycontact', items: [ { value: 'entitycontact.list', label: 'list', hint: 'googleAuthId, entityId → rows' }, { value: 'entitycontact.create', label: 'create', hint: 'googleAuthId, entityId, JSON → row' }, { value: 'entitycontact.update', label: 'update', hint: 'googleAuthId, entityId, id, JSON → row' }, { value: 'entitycontact.delete', label: 'delete', hint: 'googleAuthId, entityId, id' }, ], }, supplier: { label: 'supplier', items: [ { value: 'supplier.list', label: 'list', hint: 'googleAuthId → rows', }, { value: 'supplier.create', label: 'create', hint: 'googleAuthId, name → row', }, { value: 'supplier.update', label: 'update', hint: 'googleAuthId, id, name → row', }, { value: 'supplier.delete', label: 'delete', hint: 'googleAuthId, id', }, ], }, delivery: { label: 'delivery', items: [ { value: 'delivery.list', label: 'list', hint: 'googleAuthId → summaries', }, { value: 'delivery.getById', label: 'getById', hint: 'googleAuthId, id → delivery', }, { value: 'delivery.create', label: 'create', hint: 'googleAuthId, JSON body → delivery', }, { value: 'delivery.update', label: 'update', hint: 'googleAuthId, id, JSON patch → delivery', }, { value: 'delivery.delete', label: 'delete', hint: 'googleAuthId, id', }, ], }, quote: { label: 'quote', items: [ { value: 'quote.list', label: 'list', hint: 'googleAuthId → summaries', }, { value: 'quote.getById', label: 'getById', hint: 'googleAuthId, id → quote', }, { value: 'quote.create', label: 'create', hint: 'googleAuthId, JSON body → quote', }, { value: 'quote.update', label: 'update', hint: 'googleAuthId, id, JSON patch → quote', }, { value: 'quote.delete', label: 'delete', hint: 'googleAuthId, id', }, ], }, order: { label: 'order', items: [ { value: 'order.list', label: 'list', hint: 'googleAuthId → summaries', }, { value: 'order.getById', label: 'getById', hint: 'googleAuthId, id → order', }, { value: 'order.create', label: 'create', hint: 'googleAuthId, JSON body → order', }, { value: 'order.update', label: 'update', hint: 'googleAuthId, id, JSON patch → order', }, { value: 'order.delete', label: 'delete', hint: 'googleAuthId, id', }, ], }, category: { label: 'category', items: [ { value: 'category.createUnderParent', label: 'createUnderParent', hint: 'googleAuthId, parentId, name → row' }, { value: 'category.rename', label: 'rename', hint: 'googleAuthId, id, name → row' }, { value: 'category.move', label: 'move', hint: 'googleAuthId, id, parentId → row' }, { value: 'category.order', label: 'order', hint: 'googleAuthId, parentId, JSON categoryIds' }, { value: 'category.delete', label: 'delete', hint: 'googleAuthId, id' }, { value: 'category.getTreeForCompany', label: 'getTreeForCompany', hint: 'googleAuthId → rows' }, { value: 'category.getCategoryArticles', label: 'getCategoryArticles', hint: 'googleAuthId, categoryId → rows' }, ], }, article: { label: 'article', items: [ { value: 'article.createUnderParent', label: 'createUnderParent', hint: 'googleAuthId, parentId, name → row', }, { value: 'article.getBySku', label: 'getBySku', hint: 'googleAuthId, sku → row', }, { value: 'article.getHistoryBySku', label: 'getHistoryBySku', hint: 'googleAuthId, sku → history[]', }, { value: 'article.updateBySku', label: 'updateBySku', hint: 'googleAuthId, sku, name/unitamount/unitSi → row', }, { value: 'article.setPricesBySku', label: 'setPricesBySku', hint: 'googleAuthId, sku, JSON prices → row', }, { value: 'article.setEansBySku', label: 'setEansBySku', hint: 'googleAuthId, sku, JSON eans → row', }, { value: 'article.setBrandIdBySku', label: 'setBrandIdBySku', hint: 'googleAuthId, sku, brandId|null → row', }, { value: 'article.setWeightBySku', label: 'setWeightBySku', hint: 'googleAuthId, sku, weight → row', }, { value: 'article.setTrackStockBySku', label: 'setTrackStockBySku', hint: 'googleAuthId, sku, trackStock (true|false) → row', }, { value: 'article.setCategoryIdsBySku', label: 'setCategoryIdsBySku', hint: 'googleAuthId, sku, JSON categoryIds → row', }, { value: 'article.addImage', label: 'addImage', hint: 'googleAuthId, sku, avif file path → sizes', }, { value: 'article.deleteImage', label: 'deleteImage', hint: 'googleAuthId, sku, fullHash', }, { value: 'article.orderImages', label: 'orderImages', hint: 'googleAuthId, sku, comma fullHashes', }, { value: 'article.delete', label: 'delete', hint: 'googleAuthId, sku', }, ], }, pic: { label: 'pic', items: [ { value: 'pic.getImageByHash', label: 'getImageByHash', hint: 'googleAuthId, fullHash → meta', }, { value: 'pic.getThumbByHash', label: 'getThumbByHash', hint: 'googleAuthId, fullHash → meta', }, ], }, companyuser: { label: 'companyuser', items: [ { value: 'companyuser.add', label: 'add', hint: 'companyId, googleAuthId → id' }, { value: 'companyuser.delete', label: 'delete', hint: 'id' }, { value: 'companyuser.getByEmail', label: 'getByEmail', hint: 'email → ids' }, ], }, google_auth: { label: 'google_auth', items: [ { value: 'google_auth.add', label: 'add', hint: 'email → id' }, { value: 'google_auth.getByEmail', label: 'getByEmail', hint: 'email → row' }, { value: 'google_auth.getById', label: 'getById', hint: 'googleAuthId → row' }, { value: 'google_auth.listAllowedEmails', label: 'listAllowedEmails', hint: '→ rows' }, { value: 'google_auth.removeAllowedEmail', label: 'removeAllowedEmail', hint: 'email' }, { value: 'google_auth.setSelectedCompany', label: 'setSelectedCompany', hint: 'googleAuthId, companyId' }, { value: 'google_auth.setProfile', label: 'setProfile', hint: 'googleAuthId, picture, sub' }, ], }, schema: { label: 'schema', items: [ { value: 'initialize', label: 'initialize', hint: 'ensure schema (no purge)' }, { value: 'purge', label: chalk.red('purge'), hint: 'drop all · exit' }, ], }, }; async function pickAction() { const group = await p.select({ message: 'Group', options: [ ...Object.entries(MENU).map(([value, group]) => ({ value, label: group.label, hint: group.hint, })), { value: 'exit', label: 'exit', hint: 'quit without purge' }, ], }); if (p.isCancel(group) || group === 'exit') return 'exit'; const action = await p.select({ message: MENU[group].label, options: [ ...MENU[group].items, { value: 'back', label: chalk.dim('← back') }, ], }); if (p.isCancel(action) || action === 'back') return null; return action; } async function runMethod(client, action) { switch (action) { case 'setup.company': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; const name = await askText('Company name', { placeholder: 'Acme', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; const iso639 = await askText('Default language (iso639)', { placeholder: 'de', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (iso639 == null) return; const taxrate = await askNumber('Tax rate', '19'); if (taxrate == null) return; const reducedtaxrate = await askNumber('Reduced tax rate', '7'); if (reducedtaxrate == null) return; const existing = await client.google_auth.getByEmail(email); const googleAuthId = existing ? existing.id : await client.google_auth.add(email); const created = await client.company.create({ iso639, name, taxrate, reducedtaxrate, googleAuthId, }); const companyId = created.id; await client.google_auth.setSelectedCompany(googleAuthId, companyId); return [ 'created', { ...created, googleAuthId, email, name, iso639, taxrate, reducedtaxrate, }, ]; } case 'company.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const name = await askText('Name', { placeholder: 'Acme', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; const iso639 = await askText('iso639', { placeholder: 'de', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (iso639 == null) return; const taxrate = await askNumber('taxrate', '19'); if (taxrate == null) return; const reducedtaxrate = await askNumber('reducedtaxrate', '7'); if (reducedtaxrate == null) return; return [ 'result', await client.company.create({ iso639, name, taxrate, reducedtaxrate, googleAuthId: Number(googleAuthId), }), ]; } case 'company.update': { const id = await askInt('Company id'); if (id == null) return; const name = await askText('Name', { placeholder: 'Acme', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; const iso639 = await askText('iso639', { placeholder: 'de', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (iso639 == null) return; const taxrate = await askNumber('taxrate', '19'); if (taxrate == null) return; const reducedtaxrate = await askNumber('reducedtaxrate', '7'); if (reducedtaxrate == null) return; return [ 'result', await client.company.update(Number(id), { iso639, name, taxrate, reducedtaxrate, }), ]; } case 'company.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return ['result[]', await client.company.list(Number(googleAuthId))]; } case 'company.delete': { const companyId = await askInt('Company id'); if (companyId == null) return; await client.company.delete(Number(companyId)); return ['ok', true]; } case 'tax.create': { const rate = await askNumber('rate', '19'); if (rate == null) return; return ['id', await client.tax.create(rate)]; } case 'tax.list': { return ['rows', await client.tax.list()]; } case 'tax.ensure': { const rate = await askNumber('rate', '19'); if (rate == null) return; return ['id', await client.tax.ensure(rate)]; } case 'unit.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'result[]', await client.unit.list(Number(googleAuthId)), ]; } case 'unit.upsert': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const unitJson = await askText('unit JSON', { placeholder: '{"si":"ml","basesi":"l","basesifactor":0.001,"symbol":"ml","name":"Milliliter"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (unitJson == null) return; return [ 'result', await client.unit.upsert( Number(googleAuthId), JSON.parse(unitJson), ), ]; } case 'unit.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const si = await askText('si', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (si == null) return; await client.unit.delete(Number(googleAuthId), si); return ['ok', true]; } case 'companysetting.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const frontendOnly = await askText('frontendOnly (true/false)', { placeholder: 'false', }); if (frontendOnly == null) return; return [ 'result[]', await client.companysetting.list(Number(googleAuthId), { frontendOnly: frontendOnly.trim().toLowerCase() === 'true', }), ]; } case 'companysetting.get': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const key = await askText('key', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (key == null) return; const frontendOnly = await askText('frontendOnly (true/false)', { placeholder: 'false', }); if (frontendOnly == null) return; return [ 'result', await client.companysetting.get( Number(googleAuthId), key, { frontendOnly: frontendOnly.trim().toLowerCase() === 'true' }, ), ]; } case 'companysetting.set': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const settingJson = await askText('setting JSON', { placeholder: '{"key":"theme","value":"dark","frontend":true}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (settingJson == null) return; return [ 'result', await client.companysetting.set( Number(googleAuthId), JSON.parse(settingJson), ), ]; } case 'companysetting.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const key = await askText('key', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (key == null) return; await client.companysetting.delete( Number(googleAuthId), key, ); return ['ok', true]; } case 'brand.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'result[]', await client.brand.list(Number(googleAuthId)), ]; } case 'brand.upsert': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const brandJson = await askText('brand JSON', { placeholder: '{"name":"Nike","gsprpinfo":"..."}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (brandJson == null) return; return [ 'result', await client.brand.upsert( Number(googleAuthId), JSON.parse(brandJson), ), ]; } case 'brand.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('brand id'); if (id == null) return; await client.brand.delete(Number(googleAuthId), id); return ['ok', true]; } case 'customergroup.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'result[]', await client.customergroup.list(Number(googleAuthId)), ]; } case 'customergroup.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'result', await client.customergroup.create( Number(googleAuthId), { name }, ), ]; } case 'customergroup.update': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('customergroup id'); if (id == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'result', await client.customergroup.update( Number(googleAuthId), Number(id), { name }, ), ]; } case 'customergroup.order': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const idsJson = await askText('customergroupIds JSON', { placeholder: '[3,1,2]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (idsJson == null) return; return [ 'result', await client.customergroup.order( Number(googleAuthId), { customergroupIds: JSON.parse(idsJson) }, ), ]; } case 'customergroup.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('customergroup id'); if (id == null) return; await client.customergroup.delete( Number(googleAuthId), Number(id), ); return ['ok', true]; } case 'entity.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const roleRaw = await askText('role (customer|empty)', { placeholder: '', validate: (v) => { const t = v?.trim?.() ?? ''; if (!t || t === 'customer') return undefined; return 'customer or empty'; }, }); if (roleRaw == null) return; const role = roleRaw.trim() || undefined; let customergroupId; if (!role) { const groupRaw = await askText('customergroup id (empty = all)', { placeholder: '', validate: (v) => v?.trim() && !/^\d+$/.test(v.trim()) ? 'Integer or empty' : undefined, }); if (groupRaw == null) return; customergroupId = groupRaw.trim() ? Number(groupRaw.trim()) : undefined; } return [ 'result[]', await client.entity.list(Number(googleAuthId), { customergroupId, role, }), ]; } case 'entity.getById': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('entity id'); if (id == null) return; return [ 'result', await client.entity.getById( Number(googleAuthId), Number(id), ), ]; } case 'entity.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const bodyJson = await askText('entity JSON', { placeholder: '{"customergroupId":1,"name":"Acme","firstName":null,"number":null}', validate: (v) => { if (!v?.trim()) return 'Required'; try { JSON.parse(v); } catch { return 'Invalid JSON'; } }, }); if (bodyJson == null) return; return [ 'result', await client.entity.create( Number(googleAuthId), JSON.parse(bodyJson), ), ]; } case 'entity.update': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('entity id'); if (id == null) return; const patchJson = await askText('patch JSON', { placeholder: '{"name":"Acme","firstName":"Ada","number":"C-1"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { JSON.parse(v); } catch { return 'Invalid JSON'; } }, }); if (patchJson == null) return; return [ 'result', await client.entity.update( Number(googleAuthId), Number(id), JSON.parse(patchJson), ), ]; } case 'entity.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('entity id'); if (id == null) return; await client.entity.delete( Number(googleAuthId), Number(id), ); return ['ok', true]; } case 'entity.peekNextNumber': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'result', await client.entity.peekNextNumber( Number(googleAuthId), ), ]; } case 'entity.isNumberFree': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const number = await askText('number', { validate: (v) => (v?.trim() ? undefined : 'Required'), }); if (number == null) return; const excludeRaw = await askText('exclude entity id (optional)', { placeholder: 'leave empty', validate: (v) => { const t = v?.trim() ?? ''; if (!t) return undefined; return /^\d+$/.test(t) ? undefined : 'Integer or empty'; }, }); if (excludeRaw == null) return; const excludeEntityId = excludeRaw.trim() ? Number(excludeRaw.trim()) : null; return [ 'result', await client.entity.isNumberFree( Number(googleAuthId), number.trim(), excludeEntityId == null ? {} : { excludeEntityId }, ), ]; } case 'entityaddress.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const entityId = await askInt('entity id'); if (entityId == null) return; return [ 'result[]', await client.entityaddress.list( Number(googleAuthId), Number(entityId), ), ]; } case 'entityaddress.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const entityId = await askInt('entity id'); if (entityId == null) return; const bodyJson = await askText('address JSON', { placeholder: '{"addressType":"billing","street":"Main","streetNumber":"1","zip":"1010","city":"Wien"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { JSON.parse(v); } catch { return 'Invalid JSON'; } }, }); if (bodyJson == null) return; return [ 'result', await client.entityaddress.create( Number(googleAuthId), Number(entityId), JSON.parse(bodyJson), ), ]; } case 'entityaddress.update': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const entityId = await askInt('entity id'); if (entityId == null) return; const id = await askInt('address id'); if (id == null) return; const patchJson = await askText('patch JSON', { placeholder: '{"city":"Graz"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { JSON.parse(v); } catch { return 'Invalid JSON'; } }, }); if (patchJson == null) return; return [ 'result', await client.entityaddress.update( Number(googleAuthId), Number(entityId), Number(id), JSON.parse(patchJson), ), ]; } case 'entityaddress.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const entityId = await askInt('entity id'); if (entityId == null) return; const id = await askInt('address id'); if (id == null) return; await client.entityaddress.delete( Number(googleAuthId), Number(entityId), Number(id), ); return ['ok', true]; } case 'entitycontact.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const entityId = await askInt('entity id'); if (entityId == null) return; return [ 'result[]', await client.entitycontact.list( Number(googleAuthId), Number(entityId), ), ]; } case 'entitycontact.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const entityId = await askInt('entity id'); if (entityId == null) return; const bodyJson = await askText('contact JSON', { placeholder: '{"contactType":"email","contact":"a@b.c"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { JSON.parse(v); } catch { return 'Invalid JSON'; } }, }); if (bodyJson == null) return; return [ 'result', await client.entitycontact.create( Number(googleAuthId), Number(entityId), JSON.parse(bodyJson), ), ]; } case 'entitycontact.update': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const entityId = await askInt('entity id'); if (entityId == null) return; const id = await askInt('contact id'); if (id == null) return; const patchJson = await askText('patch JSON', { placeholder: '{"contact":"b@c.d"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { JSON.parse(v); } catch { return 'Invalid JSON'; } }, }); if (patchJson == null) return; return [ 'result', await client.entitycontact.update( Number(googleAuthId), Number(entityId), Number(id), JSON.parse(patchJson), ), ]; } case 'entitycontact.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const entityId = await askInt('entity id'); if (entityId == null) return; const id = await askInt('contact id'); if (id == null) return; await client.entitycontact.delete( Number(googleAuthId), Number(entityId), Number(id), ); return ['ok', true]; } case 'supplier.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'result[]', await client.supplier.list(Number(googleAuthId)), ]; } case 'supplier.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'result', await client.supplier.create(Number(googleAuthId), { name, }), ]; } case 'supplier.update': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('supplier id'); if (id == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'result', await client.supplier.update( Number(googleAuthId), Number(id), { name }, ), ]; } case 'supplier.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('supplier id'); if (id == null) return; await client.supplier.delete( Number(googleAuthId), Number(id), ); return ['ok', true]; } case 'delivery.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'result[]', await client.delivery.list(Number(googleAuthId)), ]; } case 'delivery.getById': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('delivery id'); if (id == null) return; return [ 'result', await client.delivery.getById( Number(googleAuthId), Number(id), ), ]; } case 'delivery.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const bodyJson = await askText('delivery JSON', { placeholder: '{"number":"DHL-123","date":"2026-08-10","supplierId":1,"lines":[{"sku":"1","amount":10,"net":99.5,"tax":19}]}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (bodyJson == null) return; return [ 'result', await client.delivery.create( Number(googleAuthId), JSON.parse(bodyJson), ), ]; } case 'delivery.update': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('delivery id'); if (id == null) return; const patchJson = await askText('patch JSON', { placeholder: '{"number":"DHL-124","lines":[]}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (patchJson == null) return; return [ 'result', await client.delivery.update( Number(googleAuthId), Number(id), JSON.parse(patchJson), ), ]; } case 'delivery.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('delivery id'); if (id == null) return; await client.delivery.delete( Number(googleAuthId), Number(id), ); return ['ok', true]; } case 'quote.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'result[]', await client.quote.list(Number(googleAuthId)), ]; } case 'quote.getById': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('quote id'); if (id == null) return; return [ 'result', await client.quote.getById( Number(googleAuthId), Number(id), ), ]; } case 'quote.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const bodyJson = await askText('quote JSON', { placeholder: '{"number":"A-2026-001","date":"2026-09-01","entityId":12,"lines":[{"sku":"1","amount":10,"net":99.5,"tax":19}]}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (bodyJson == null) return; return [ 'result', await client.quote.create( Number(googleAuthId), JSON.parse(bodyJson), ), ]; } case 'quote.update': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('quote id'); if (id == null) return; const patchJson = await askText('patch JSON', { placeholder: '{"number":"A-2026-002","lines":[]}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (patchJson == null) return; return [ 'result', await client.quote.update( Number(googleAuthId), Number(id), JSON.parse(patchJson), ), ]; } case 'quote.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('quote id'); if (id == null) return; await client.quote.delete( Number(googleAuthId), Number(id), ); return ['ok', true]; } case 'order.list': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'result[]', await client.order.list(Number(googleAuthId)), ]; } case 'order.getById': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('order id'); if (id == null) return; return [ 'result', await client.order.getById( Number(googleAuthId), Number(id), ), ]; } case 'order.create': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const bodyJson = await askText('order JSON', { placeholder: '{"number":"B-2026-001","date":"2026-09-15","entityId":12,"lines":[{"sku":"1","amount":10,"net":99.5,"tax":19}]}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (bodyJson == null) return; return [ 'result', await client.order.create( Number(googleAuthId), JSON.parse(bodyJson), ), ]; } case 'order.update': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('order id'); if (id == null) return; const patchJson = await askText('patch JSON', { placeholder: '{"number":"B-2026-002","lines":[]}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (patchJson == null) return; return [ 'result', await client.order.update( Number(googleAuthId), Number(id), JSON.parse(patchJson), ), ]; } case 'order.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('order id'); if (id == null) return; await client.order.delete( Number(googleAuthId), Number(id), ); return ['ok', true]; } case 'lang.create': { const iso639 = await askText('ISO 639', { placeholder: 'de', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (iso639 == null) return; return ['id', await client.lang.create(iso639)]; } case 'lang.list': { return ['rows', await client.lang.list()]; } case 'lang.delete': { const id = await askInt('Language id'); if (id == null) return; await client.lang.delete(Number(id)); return ['ok', true]; } case 'category.createUnderParent': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const parentId = await askInt('parent category id'); if (parentId == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'result', await client.category.createUnderParent({ googleAuthId: Number(googleAuthId), parentId: Number(parentId), name, }), ]; } case 'category.rename': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('Category id'); if (id == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'row', await client.category.rename({ googleAuthId: Number(googleAuthId), id: Number(id), name, }), ]; } case 'category.move': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('Category id'); if (id == null) return; const parentId = await askInt('new parent id'); if (parentId == null) return; return [ 'row', await client.category.move({ googleAuthId: Number(googleAuthId), id: Number(id), parentId: Number(parentId), }), ]; } case 'category.order': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const parentId = await askInt('parent id'); if (parentId == null) return; const categoryIdsJson = await askText('categoryIds JSON', { placeholder: '[2,4,3,5]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (categoryIdsJson == null) return; return [ 'result', await client.category.order({ googleAuthId: Number(googleAuthId), parentId: Number(parentId), categoryIds: JSON.parse(categoryIdsJson), }), ]; } case 'category.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const id = await askInt('Category id'); if (id == null) return; await client.category.delete({ googleAuthId: Number(googleAuthId), id: Number(id), }); return ['ok', true]; } case 'category.getTreeForCompany': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; return [ 'rows', await client.category.getTreeForCompany(Number(googleAuthId)), ]; } case 'category.getCategoryArticles': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const categoryId = await askInt('category id'); if (categoryId == null) return; return [ 'rows', await client.category.getCategoryArticles( Number(googleAuthId), Number(categoryId), ), ]; } case 'article.createUnderParent': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const parentId = await askInt('category id'); if (parentId == null) return; const name = await askText('name', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (name == null) return; return [ 'result', await client.article.createUnderParent({ googleAuthId: Number(googleAuthId), parentId: Number(parentId), name, }), ]; } case 'article.getBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; return [ 'result', await client.article.getBySku(Number(googleAuthId), sku), ]; } case 'article.getHistoryBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; return [ 'result', await client.article.getHistoryBySku(Number(googleAuthId), sku), ]; } case 'article.updateBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const patchJson = await askText('patch JSON', { placeholder: '{"name":"Tea","unitamount":0.24,"unitSi":"l"}', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return 'Must be a JSON object'; } } catch { return 'Invalid JSON'; } }, }); if (patchJson == null) return; return [ 'result', await client.article.updateBySku( Number(googleAuthId), sku, JSON.parse(patchJson), ), ]; } case 'article.setPricesBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const pricesJson = await askText('prices JSON', { placeholder: '[{"price":9.99,"tax":19}]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (pricesJson == null) return; return [ 'result', await client.article.setPricesBySku( Number(googleAuthId), sku, JSON.parse(pricesJson), ), ]; } case 'article.setEansBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const eansJson = await askText('eans JSON', { placeholder: '["4006381333931","4012345678901"]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (eansJson == null) return; return [ 'result', await client.article.setEansBySku( Number(googleAuthId), sku, JSON.parse(eansJson), ), ]; } case 'article.setBrandIdBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const raw = await askText('brandId (or "null")', { validate: (v) => { if (v === null || v === undefined || v.trim() === '') return 'Required'; const t = v.trim(); if (t === 'null') return undefined; if (!Number.isFinite(Number(t))) return 'Must be a number or null'; }, }); if (raw == null) return; const brandId = raw.trim() === 'null' ? null : Number(raw.trim()); return [ 'result', await client.article.setBrandIdBySku( Number(googleAuthId), sku, brandId, ), ]; } case 'article.setWeightBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const weight = await askInt('weight'); if (weight == null) return; return [ 'result', await client.article.setWeightBySku( Number(googleAuthId), sku, weight, ), ]; } case 'article.setTrackStockBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const trackStockRaw = await p.confirm({ message: 'Track stock?', initialValue: true, }); if (p.isCancel(trackStockRaw)) return; return [ 'result', await client.article.setTrackStockBySku( Number(googleAuthId), sku, Boolean(trackStockRaw), ), ]; } case 'article.setCategoryIdsBySku': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const categoryIdsJson = await askText('categoryIds JSON', { placeholder: '[10,23,42]', validate: (v) => { if (!v?.trim()) return 'Required'; try { const parsed = JSON.parse(v); if (!Array.isArray(parsed)) return 'Must be a JSON array'; } catch { return 'Invalid JSON'; } }, }); if (categoryIdsJson == null) return; return [ 'result', await client.article.setCategoryIdsBySku( Number(googleAuthId), sku, JSON.parse(categoryIdsJson), ), ]; } case 'article.addImage': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const filePath = await askText('AVIF file path', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (filePath == null) return; const image = await readFile(filePath.trim()); return [ 'result', await client.article.addImage(Number(googleAuthId), sku, image), ]; } case 'article.deleteImage': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const fullHash = await askText('fullHash', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (fullHash == null) return; await client.article.deleteImage( Number(googleAuthId), sku, fullHash.trim(), ); return ['ok', true]; } case 'article.orderImages': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; const hashes = await askText('fullHashes (comma-separated)', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (hashes == null) return; const fullHashes = hashes .split(',') .map((h) => h.trim()) .filter(Boolean); return [ 'result', await client.article.orderImages(Number(googleAuthId), sku, { fullHashes, }), ]; } case 'article.delete': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const sku = await askText('sku', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (sku == null) return; await client.article.delete(Number(googleAuthId), sku); return ['ok', true]; } case 'pic.getImageByHash': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const fullHash = await askText('fullHash', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (fullHash == null) return; const image = await client.pic.getImageByHash( Number(googleAuthId), fullHash.trim(), ); return [ 'result', { w: image.w, h: image.h, size: image.size, contentType: image.contentType, bufferLength: image.buffer.length, }, ]; } case 'pic.getThumbByHash': { const googleAuthId = await askInt('google_auth id'); if (googleAuthId == null) return; const fullHash = await askText('fullHash', { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (fullHash == null) return; const image = await client.pic.getThumbByHash( Number(googleAuthId), fullHash.trim(), ); return [ 'result', { w: image.w, h: image.h, contentType: image.contentType, bufferLength: image.buffer.length, }, ]; } case 'companyuser.add': { const companyId = await askInt('Company id'); if (companyId == null) return; const googleAuthId = await askInt('Auth id'); if (googleAuthId == null) return; return ['id', await client.companyuser.add(Number(companyId), Number(googleAuthId))]; } case 'companyuser.delete': { const id = await askInt('Company user id'); if (id == null) return; await client.companyuser.delete(Number(id)); return ['ok', true]; } case 'companyuser.getByEmail': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; return ['ids', await client.companyuser.getByEmail(email)]; } case 'google_auth.add': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; return ['id', await client.google_auth.add(email)]; } case 'google_auth.getByEmail': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; return ['row', await client.google_auth.getByEmail(email)]; } case 'google_auth.getById': { const id = await askInt('Auth id'); if (id == null) return; return ['row', await client.google_auth.getById(Number(id))]; } case 'google_auth.listAllowedEmails': { return ['rows', await client.google_auth.listAllowedEmails()]; } case 'google_auth.removeAllowedEmail': { const email = await askText('Email', { placeholder: 'user@example.com', validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; await client.google_auth.removeAllowedEmail(email); return ['ok', true]; } case 'google_auth.setSelectedCompany': { const id = await askInt('Auth id'); if (id == null) return; const companyId = await askInt('Company id'); if (companyId == null) return; await client.google_auth.setSelectedCompany(Number(id), Number(companyId)); return ['ok', true]; } case 'google_auth.setProfile': { const id = await askInt('Auth id'); if (id == null) return; const picture = await askText('picture URL', { placeholder: '(empty = null)' }); if (picture == null) return; const sub = await askText('Google sub', { placeholder: '(empty = null)' }); if (sub == null) return; await client.google_auth.setProfile(Number(id), { picture: picture.trim() || null, sub: sub.trim() || null, }); return ['ok', true]; } case 'initialize': { await client.initialize(); return ['ok', true]; } default: return null; } } async function main() { console.clear(); p.intro(chalk.bgMagenta.black(' libdashstore ') + chalk.dim(' api tester')); const spin = p.spinner(); spin.start('Connecting to MSSQL…'); let pool; try { pool = await sql.connect(config); } catch (err) { spin.stop(chalk.red('Connection failed')); p.log.error(err.message); p.outro(chalk.red('Aborted')); process.exit(1); } spin.message('Initializing schema…'); let client; try { client = await new LibDashStore(new MssqlProvider(pool)); spin.stop(chalk.green('Ready')); } catch (err) { spin.stop(chalk.red('Initialize failed')); p.log.error(err.message); await shutdown(pool, 1); } p.note( [ chalk.dim(`${config.server} / ${config.database}`), chalk.dim('initialize = ensure schema · purge ends session'), ].join('\n'), 'Connected', ); while (true) { const action = await pickAction(); if (action === 'exit') { p.outro(chalk.dim('Bye')); await shutdown(pool, 0); } if (action == null) continue; if (action === 'purge') { const confirm1 = await p.confirm({ message: chalk.red('Drop ALL tables?'), initialValue: false, }); if (p.isCancel(confirm1) || !confirm1) { p.log.warn('Purge cancelled'); continue; } const confirm2 = await p.confirm({ message: chalk.red('Really purge? This cannot be undone.'), initialValue: false, }); if (p.isCancel(confirm2) || !confirm2) { p.log.warn('Purge cancelled'); continue; } const run = p.spinner(); run.start('Purging…'); await client.purge(); run.stop(chalk.green('purge')); p.outro(chalk.red('Database purged — exiting')); await shutdown(pool, 0); } try { const result = await runMethod(client, action); if (result) { p.log.success(action); printResult(result[0], result[1]); } } catch (err) { p.log.error(err.message); console.log(chalk.dim(err.stack?.split('\n').slice(1, 4).join('\n') ?? '')); console.log(); } } } main().catch(async (err) => { console.error(chalk.red(err.stack ?? err.message)); process.exit(1); });