feat: add AccountsModule with list and QR endpoints
Implements GET /accounts (list all accounts) and GET /accounts/:id/qr (returns QR code as base64 data URL) using the qrcode package. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
"@tower/logger": "workspace:*",
|
||||
"@tower/search": "workspace:*",
|
||||
"@tower/types": "workspace:*",
|
||||
"qrcode": "^1.5.4",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
"rxjs": "^7.8.0"
|
||||
},
|
||||
@@ -27,6 +28,7 @@
|
||||
"@nestjs/testing": "^11.0.0",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"dotenv": "^17.4.2",
|
||||
"jest": "^29.0.0",
|
||||
"prisma": "^6.0.0",
|
||||
|
||||
@@ -5,6 +5,7 @@ import { HealthModule } from './modules/health/health.module';
|
||||
import { SearchModule } from './modules/search/search.module';
|
||||
import { GroupsModule } from './modules/groups/groups.module';
|
||||
import { RoutesModule } from './modules/routes/routes.module';
|
||||
import { AccountsModule } from './modules/accounts/accounts.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -14,6 +15,7 @@ import { RoutesModule } from './modules/routes/routes.module';
|
||||
SearchModule,
|
||||
GroupsModule,
|
||||
RoutesModule,
|
||||
AccountsModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AccountsController } from './accounts.controller';
|
||||
import { AccountsService } from './accounts.service';
|
||||
|
||||
const mockAccounts = [
|
||||
{ id: 'acc_1', platform: 'whatsapp', jid: '111@s.whatsapp.net', displayName: 'Test', status: 'ACTIVE' },
|
||||
];
|
||||
const mockService = {
|
||||
list: jest.fn().mockResolvedValue(mockAccounts),
|
||||
getQr: jest.fn().mockResolvedValue({ status: 'DISCONNECTED', qrDataUrl: 'data:image/png;base64,fake' }),
|
||||
};
|
||||
|
||||
describe('AccountsController', () => {
|
||||
let controller: AccountsController;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AccountsController],
|
||||
providers: [{ provide: AccountsService, useValue: mockService }],
|
||||
}).compile();
|
||||
controller = module.get<AccountsController>(AccountsController);
|
||||
});
|
||||
|
||||
it('list() returns accounts from service', async () => {
|
||||
const result = await controller.list();
|
||||
expect(result).toEqual(mockAccounts);
|
||||
expect(mockService.list).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('getQr() calls service with the account id', async () => {
|
||||
const result = await controller.getQr('acc_1');
|
||||
expect(mockService.getQr).toHaveBeenCalledWith('acc_1');
|
||||
expect(result.qrDataUrl).toBe('data:image/png;base64,fake');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Controller, Get, Param } from '@nestjs/common';
|
||||
import { AccountsService } from './accounts.service';
|
||||
|
||||
@Controller('accounts')
|
||||
export class AccountsController {
|
||||
constructor(private readonly service: AccountsService) {}
|
||||
|
||||
@Get()
|
||||
list() {
|
||||
return this.service.list();
|
||||
}
|
||||
|
||||
@Get(':id/qr')
|
||||
getQr(@Param('id') id: string) {
|
||||
return this.service.getQr(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AccountsController } from './accounts.controller';
|
||||
import { AccountsService } from './accounts.service';
|
||||
|
||||
@Module({
|
||||
controllers: [AccountsController],
|
||||
providers: [AccountsService],
|
||||
})
|
||||
export class AccountsModule {}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AccountsService } from './accounts.service';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
import * as QRCode from 'qrcode';
|
||||
|
||||
jest.mock('qrcode', () => ({
|
||||
toDataURL: jest.fn().mockResolvedValue('data:image/png;base64,fakedata'),
|
||||
}));
|
||||
|
||||
const mockAccounts = [
|
||||
{ id: 'acc_1', platform: 'whatsapp', jid: '111@s.whatsapp.net', displayName: 'Test Account', status: 'ACTIVE' },
|
||||
];
|
||||
|
||||
const mockPrisma = {
|
||||
account: {
|
||||
findMany: jest.fn().mockResolvedValue(mockAccounts),
|
||||
findUnique: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
describe('AccountsService', () => {
|
||||
let service: AccountsService;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
AccountsService,
|
||||
{ provide: PrismaService, useValue: mockPrisma },
|
||||
],
|
||||
}).compile();
|
||||
service = module.get<AccountsService>(AccountsService);
|
||||
});
|
||||
|
||||
describe('list()', () => {
|
||||
it('returns accounts from Prisma without qrCode field', async () => {
|
||||
const result = await service.list();
|
||||
expect(result).toEqual(mockAccounts);
|
||||
expect(mockPrisma.account.findMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ select: expect.not.objectContaining({ qrCode: true }) }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getQr()', () => {
|
||||
it('returns null qrDataUrl when account has no qrCode', async () => {
|
||||
mockPrisma.account.findUnique.mockResolvedValue({ status: 'ACTIVE', qrCode: null });
|
||||
const result = await service.getQr('acc_1');
|
||||
expect(result).toEqual({ status: 'ACTIVE', qrDataUrl: null });
|
||||
expect(QRCode.toDataURL).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('converts qrCode string to data URL when qrCode is present', async () => {
|
||||
mockPrisma.account.findUnique.mockResolvedValue({ status: 'DISCONNECTED', qrCode: 'raw-qr-string' });
|
||||
const result = await service.getQr('acc_1');
|
||||
expect(QRCode.toDataURL).toHaveBeenCalledWith('raw-qr-string');
|
||||
expect(result).toEqual({ status: 'DISCONNECTED', qrDataUrl: 'data:image/png;base64,fakedata' });
|
||||
});
|
||||
|
||||
it('returns not_found status when account does not exist', async () => {
|
||||
mockPrisma.account.findUnique.mockResolvedValue(null);
|
||||
const result = await service.getQr('nonexistent');
|
||||
expect(result).toEqual({ status: 'not_found', qrDataUrl: null });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
import * as QRCode from 'qrcode';
|
||||
|
||||
export interface AccountSummary {
|
||||
id: string;
|
||||
platform: string;
|
||||
jid: string;
|
||||
displayName: string | null;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface AccountQr {
|
||||
status: string;
|
||||
qrDataUrl: string | null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AccountsService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
list(): Promise<AccountSummary[]> {
|
||||
return this.prisma.account.findMany({
|
||||
orderBy: { createdAt: 'asc' },
|
||||
select: { id: true, platform: true, jid: true, displayName: true, status: true },
|
||||
});
|
||||
}
|
||||
|
||||
async getQr(id: string): Promise<AccountQr> {
|
||||
const account = await this.prisma.account.findUnique({
|
||||
where: { id },
|
||||
select: { status: true, qrCode: true },
|
||||
});
|
||||
if (!account) return { status: 'not_found', qrDataUrl: null };
|
||||
if (!account.qrCode) return { status: account.status, qrDataUrl: null };
|
||||
const qrDataUrl = await QRCode.toDataURL(account.qrCode);
|
||||
return { status: account.status, qrDataUrl };
|
||||
}
|
||||
}
|
||||
Generated
+97
@@ -50,6 +50,9 @@ importers:
|
||||
'@tower/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/types
|
||||
qrcode:
|
||||
specifier: ^1.5.4
|
||||
version: 1.5.4
|
||||
reflect-metadata:
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.2
|
||||
@@ -72,6 +75,9 @@ importers:
|
||||
'@types/node':
|
||||
specifier: ^22.0.0
|
||||
version: 22.19.19
|
||||
'@types/qrcode':
|
||||
specifier: ^1.5.6
|
||||
version: 1.5.6
|
||||
dotenv:
|
||||
specifier: ^17.4.2
|
||||
version: 17.4.2
|
||||
@@ -1369,6 +1375,9 @@ packages:
|
||||
'@types/qrcode-terminal@0.12.2':
|
||||
resolution: {integrity: sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q==}
|
||||
|
||||
'@types/qrcode@1.5.6':
|
||||
resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==}
|
||||
|
||||
'@types/react-dom@19.2.3':
|
||||
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
|
||||
peerDependencies:
|
||||
@@ -1766,6 +1775,9 @@ packages:
|
||||
client-only@0.0.1:
|
||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||
|
||||
cliui@6.0.0:
|
||||
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
|
||||
|
||||
cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1915,6 +1927,10 @@ packages:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
decamelize@1.2.0:
|
||||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
decimal.js@10.6.0:
|
||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||
|
||||
@@ -1975,6 +1991,9 @@ packages:
|
||||
resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
dijkstrajs@1.0.3:
|
||||
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||
|
||||
dom-accessibility-api@0.5.16:
|
||||
resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
|
||||
|
||||
@@ -3078,6 +3097,10 @@ packages:
|
||||
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
pngjs@5.0.0:
|
||||
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
postcss@8.4.31:
|
||||
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@@ -3149,6 +3172,11 @@ packages:
|
||||
resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==}
|
||||
hasBin: true
|
||||
|
||||
qrcode@1.5.4:
|
||||
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
qs@6.15.2:
|
||||
resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==}
|
||||
engines: {node: '>=0.6'}
|
||||
@@ -3224,6 +3252,9 @@ packages:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
require-main-filename@2.0.0:
|
||||
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
||||
|
||||
requires-port@1.0.0:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
|
||||
@@ -3312,6 +3343,9 @@ packages:
|
||||
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
set-blocking@2.0.0:
|
||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||
|
||||
setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
|
||||
@@ -3750,6 +3784,9 @@ packages:
|
||||
resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
which-module@2.0.1:
|
||||
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||
|
||||
which@2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -3795,6 +3832,9 @@ packages:
|
||||
xmlchars@2.2.0:
|
||||
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
|
||||
|
||||
y18n@4.0.3:
|
||||
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
|
||||
|
||||
y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -3802,10 +3842,18 @@ packages:
|
||||
yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
|
||||
yargs-parser@18.1.3:
|
||||
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yargs@15.4.1:
|
||||
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
yargs@17.7.2:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -4980,6 +5028,10 @@ snapshots:
|
||||
|
||||
'@types/qrcode-terminal@0.12.2': {}
|
||||
|
||||
'@types/qrcode@1.5.6':
|
||||
dependencies:
|
||||
'@types/node': 22.19.19
|
||||
|
||||
'@types/react-dom@19.2.3(@types/react@19.2.15)':
|
||||
dependencies:
|
||||
'@types/react': 19.2.15
|
||||
@@ -5437,6 +5489,12 @@ snapshots:
|
||||
|
||||
client-only@0.0.1: {}
|
||||
|
||||
cliui@6.0.0:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 6.2.0
|
||||
|
||||
cliui@8.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
@@ -5566,6 +5624,8 @@ snapshots:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decamelize@1.2.0: {}
|
||||
|
||||
decimal.js@10.6.0: {}
|
||||
|
||||
dedent@1.7.2: {}
|
||||
@@ -5598,6 +5658,8 @@ snapshots:
|
||||
|
||||
diff@4.0.4: {}
|
||||
|
||||
dijkstrajs@1.0.3: {}
|
||||
|
||||
dom-accessibility-api@0.5.16: {}
|
||||
|
||||
dom-accessibility-api@0.6.3: {}
|
||||
@@ -6915,6 +6977,8 @@ snapshots:
|
||||
|
||||
pluralize@8.0.0: {}
|
||||
|
||||
pngjs@5.0.0: {}
|
||||
|
||||
postcss@8.4.31:
|
||||
dependencies:
|
||||
nanoid: 3.3.12
|
||||
@@ -6998,6 +7062,12 @@ snapshots:
|
||||
|
||||
qrcode-terminal@0.12.0: {}
|
||||
|
||||
qrcode@1.5.4:
|
||||
dependencies:
|
||||
dijkstrajs: 1.0.3
|
||||
pngjs: 5.0.0
|
||||
yargs: 15.4.1
|
||||
|
||||
qs@6.15.2:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
@@ -7066,6 +7136,8 @@ snapshots:
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
require-main-filename@2.0.0: {}
|
||||
|
||||
requires-port@1.0.0: {}
|
||||
|
||||
resolve-cwd@3.0.0:
|
||||
@@ -7166,6 +7238,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
set-blocking@2.0.0: {}
|
||||
|
||||
setprototypeof@1.2.0: {}
|
||||
|
||||
sharp@0.34.5:
|
||||
@@ -7591,6 +7665,8 @@ snapshots:
|
||||
tr46: 3.0.0
|
||||
webidl-conversions: 7.0.0
|
||||
|
||||
which-module@2.0.1: {}
|
||||
|
||||
which@2.0.2:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
@@ -7624,12 +7700,33 @@ snapshots:
|
||||
|
||||
xmlchars@2.2.0: {}
|
||||
|
||||
y18n@4.0.3: {}
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yallist@3.1.1: {}
|
||||
|
||||
yargs-parser@18.1.3:
|
||||
dependencies:
|
||||
camelcase: 5.3.1
|
||||
decamelize: 1.2.0
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
yargs@15.4.1:
|
||||
dependencies:
|
||||
cliui: 6.0.0
|
||||
decamelize: 1.2.0
|
||||
find-up: 4.1.0
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
require-main-filename: 2.0.0
|
||||
set-blocking: 2.0.0
|
||||
string-width: 4.2.3
|
||||
which-module: 2.0.1
|
||||
y18n: 4.0.3
|
||||
yargs-parser: 18.1.3
|
||||
|
||||
yargs@17.7.2:
|
||||
dependencies:
|
||||
cliui: 8.0.1
|
||||
|
||||
Reference in New Issue
Block a user