@simonfestl/husky-cli 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/biz/emove.d.ts +12 -0
- package/dist/commands/biz/emove.js +249 -0
- package/dist/commands/biz/skuterzone.d.ts +12 -0
- package/dist/commands/biz/skuterzone.js +248 -0
- package/dist/commands/biz/wattiz.d.ts +12 -0
- package/dist/commands/biz/wattiz.js +252 -0
- package/dist/commands/biz.js +7 -1
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.js +12 -1
- package/dist/lib/biz/emove-playwright.d.ts +22 -0
- package/dist/lib/biz/emove-playwright.js +289 -0
- package/dist/lib/biz/emove-types.d.ts +60 -0
- package/dist/lib/biz/emove-types.js +7 -0
- package/dist/lib/biz/skuterzone-playwright.d.ts +21 -0
- package/dist/lib/biz/skuterzone-playwright.js +271 -0
- package/dist/lib/biz/skuterzone-types.d.ts +60 -0
- package/dist/lib/biz/skuterzone-types.js +7 -0
- package/dist/lib/biz/wattiz-playwright.d.ts +22 -0
- package/dist/lib/biz/wattiz-playwright.js +380 -0
- package/dist/lib/biz/wattiz-types.d.ts +61 -0
- package/dist/lib/biz/wattiz-types.js +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emove Distribution Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for emovedistribution.com integration
|
|
5
|
+
* WooCommerce-based B2B parts supplier (Spanish locale)
|
|
6
|
+
*/
|
|
7
|
+
export interface EmoveConfig {
|
|
8
|
+
username: string;
|
|
9
|
+
password: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
}
|
|
12
|
+
export interface EmoveLoginResult {
|
|
13
|
+
success: boolean;
|
|
14
|
+
cookies: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface EmoveOrder {
|
|
18
|
+
id: string;
|
|
19
|
+
orderNumber: string;
|
|
20
|
+
date: string;
|
|
21
|
+
status: string;
|
|
22
|
+
total: string;
|
|
23
|
+
itemCount: number;
|
|
24
|
+
}
|
|
25
|
+
export interface EmoveOrderDetails extends EmoveOrder {
|
|
26
|
+
invoiceUrl?: string;
|
|
27
|
+
trackingNumber?: string;
|
|
28
|
+
customer: {
|
|
29
|
+
name: string;
|
|
30
|
+
company?: string;
|
|
31
|
+
address: string;
|
|
32
|
+
city: string;
|
|
33
|
+
postcode: string;
|
|
34
|
+
email: string;
|
|
35
|
+
phone?: string;
|
|
36
|
+
};
|
|
37
|
+
items: EmoveLineItem[];
|
|
38
|
+
subtotal: string;
|
|
39
|
+
shipping: string;
|
|
40
|
+
tax: string;
|
|
41
|
+
paymentMethod: string;
|
|
42
|
+
shippingMethod?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface EmoveLineItem {
|
|
45
|
+
sku: string;
|
|
46
|
+
name: string;
|
|
47
|
+
quantity: number;
|
|
48
|
+
price: string;
|
|
49
|
+
total: string;
|
|
50
|
+
}
|
|
51
|
+
export interface EmoveProduct {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
sku?: string;
|
|
55
|
+
price?: string;
|
|
56
|
+
url: string;
|
|
57
|
+
imageUrl?: string;
|
|
58
|
+
stockStatus?: string;
|
|
59
|
+
category?: string;
|
|
60
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skuterzone Pro Client with Playwright
|
|
3
|
+
*
|
|
4
|
+
* Uses real browser automation for robust scraping
|
|
5
|
+
*/
|
|
6
|
+
import { SkuterzoneConfig, SkuterzoneOrder, SkuterzoneOrderDetails, SkuterzoneProduct, LoginResult } from './skuterzone-types.js';
|
|
7
|
+
export declare class SkuterzonePlaywrightClient {
|
|
8
|
+
private config;
|
|
9
|
+
private browser?;
|
|
10
|
+
private page?;
|
|
11
|
+
constructor(config: SkuterzoneConfig);
|
|
12
|
+
static fromConfig(): SkuterzonePlaywrightClient;
|
|
13
|
+
private ensureBrowser;
|
|
14
|
+
login(): Promise<LoginResult>;
|
|
15
|
+
listOrders(): Promise<SkuterzoneOrder[]>;
|
|
16
|
+
getOrder(orderId: string): Promise<SkuterzoneOrderDetails>;
|
|
17
|
+
downloadInvoice(orderId: string, savePath: string): Promise<boolean>;
|
|
18
|
+
searchProducts(query: string): Promise<SkuterzoneProduct[]>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export default SkuterzonePlaywrightClient;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skuterzone Pro Client with Playwright
|
|
3
|
+
*
|
|
4
|
+
* Uses real browser automation for robust scraping
|
|
5
|
+
*/
|
|
6
|
+
import { chromium } from 'playwright';
|
|
7
|
+
import { getConfig } from '../../commands/config.js';
|
|
8
|
+
export class SkuterzonePlaywrightClient {
|
|
9
|
+
config;
|
|
10
|
+
browser;
|
|
11
|
+
page;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
}
|
|
15
|
+
static fromConfig() {
|
|
16
|
+
const config = getConfig();
|
|
17
|
+
const env = process.env.HUSKY_ENV || 'PROD';
|
|
18
|
+
const skuterzoneConfig = {
|
|
19
|
+
username: process.env[`${env}_SKUTERZONE_USERNAME`] ||
|
|
20
|
+
process.env.SKUTERZONE_USERNAME ||
|
|
21
|
+
config.skuterzoneUsername || '',
|
|
22
|
+
password: process.env[`${env}_SKUTERZONE_PASSWORD`] ||
|
|
23
|
+
process.env.SKUTERZONE_PASSWORD ||
|
|
24
|
+
config.skuterzonePassword || '',
|
|
25
|
+
baseUrl: process.env[`${env}_SKUTERZONE_BASE_URL`] ||
|
|
26
|
+
process.env.SKUTERZONE_BASE_URL ||
|
|
27
|
+
config.skuterzoneBaseUrl ||
|
|
28
|
+
'https://skuterzonepro.com',
|
|
29
|
+
};
|
|
30
|
+
if (!skuterzoneConfig.username || !skuterzoneConfig.password) {
|
|
31
|
+
throw new Error('Missing Skuterzone credentials. Configure with:\n' +
|
|
32
|
+
' husky config set skuterzone-username <username>\n' +
|
|
33
|
+
' husky config set skuterzone-password <password>');
|
|
34
|
+
}
|
|
35
|
+
return new SkuterzonePlaywrightClient(skuterzoneConfig);
|
|
36
|
+
}
|
|
37
|
+
async ensureBrowser() {
|
|
38
|
+
if (!this.browser) {
|
|
39
|
+
this.browser = await chromium.launch({ headless: true });
|
|
40
|
+
this.page = await this.browser.newPage();
|
|
41
|
+
}
|
|
42
|
+
return this.page;
|
|
43
|
+
}
|
|
44
|
+
async login() {
|
|
45
|
+
try {
|
|
46
|
+
const page = await this.ensureBrowser();
|
|
47
|
+
// Navigate to login page
|
|
48
|
+
await page.goto(`${this.config.baseUrl}/my-account/`);
|
|
49
|
+
await page.waitForLoadState('networkidle');
|
|
50
|
+
// Wait for the WooCommerce login form (use id instead of name to get visible form)
|
|
51
|
+
await page.waitForSelector('input#username', { state: 'visible', timeout: 10000 });
|
|
52
|
+
// Fill in login form (use IDs for the visible WooCommerce form)
|
|
53
|
+
await page.fill('input#username', this.config.username);
|
|
54
|
+
await page.fill('input#password', this.config.password);
|
|
55
|
+
// Submit form
|
|
56
|
+
await page.click('button[name="login"]');
|
|
57
|
+
// Wait for navigation
|
|
58
|
+
await page.waitForLoadState('networkidle');
|
|
59
|
+
// Check if logged in by looking for logout link or my-account elements
|
|
60
|
+
const isLoggedIn = await page.locator('body.logged-in').count() > 0;
|
|
61
|
+
if (!isLoggedIn) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
cookies: '',
|
|
65
|
+
error: 'Login failed - check credentials'
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// Extract cookies
|
|
69
|
+
const cookies = await page.context().cookies();
|
|
70
|
+
const cookieString = cookies.map(c => `${c.name}=${c.value}`).join('; ');
|
|
71
|
+
return {
|
|
72
|
+
success: true,
|
|
73
|
+
cookies: cookieString
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
cookies: '',
|
|
80
|
+
error: `Login error: ${error.message}`
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async listOrders() {
|
|
85
|
+
const page = await this.ensureBrowser();
|
|
86
|
+
// Navigate to orders page
|
|
87
|
+
await page.goto(`${this.config.baseUrl}/my-account/orders/`);
|
|
88
|
+
await page.waitForLoadState('networkidle');
|
|
89
|
+
// Check if we need to login
|
|
90
|
+
const needsLogin = await page.locator('form.woocommerce-form-login').count() > 0;
|
|
91
|
+
if (needsLogin) {
|
|
92
|
+
await this.login();
|
|
93
|
+
await page.goto(`${this.config.baseUrl}/my-account/orders/`);
|
|
94
|
+
await page.waitForLoadState('networkidle');
|
|
95
|
+
}
|
|
96
|
+
const orders = [];
|
|
97
|
+
// Find all order rows
|
|
98
|
+
const orderRows = page.locator('table.woocommerce-orders-table tr.woocommerce-orders-table__row');
|
|
99
|
+
const count = await orderRows.count();
|
|
100
|
+
for (let i = 0; i < count; i++) {
|
|
101
|
+
const row = orderRows.nth(i);
|
|
102
|
+
const orderNumberEl = row.locator('.woocommerce-orders-table__cell-order-number a');
|
|
103
|
+
const orderNumber = await orderNumberEl.textContent() || '';
|
|
104
|
+
const orderLink = await orderNumberEl.getAttribute('href') || '';
|
|
105
|
+
const orderId = orderLink.match(/view-order\/(\d+)/)?.[1] || '';
|
|
106
|
+
const date = await row.locator('.woocommerce-orders-table__cell-order-date').textContent() || '';
|
|
107
|
+
const status = await row.locator('.woocommerce-orders-table__cell-order-status').textContent() || '';
|
|
108
|
+
const total = await row.locator('.woocommerce-orders-table__cell-order-total').textContent() || '';
|
|
109
|
+
orders.push({
|
|
110
|
+
id: orderId,
|
|
111
|
+
orderNumber: orderNumber.trim(),
|
|
112
|
+
date: date.trim(),
|
|
113
|
+
status: status.trim(),
|
|
114
|
+
total: total.trim(),
|
|
115
|
+
itemCount: 0,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return orders;
|
|
119
|
+
}
|
|
120
|
+
async getOrder(orderId) {
|
|
121
|
+
const page = await this.ensureBrowser();
|
|
122
|
+
await page.goto(`${this.config.baseUrl}/my-account/view-order/${orderId}/`);
|
|
123
|
+
await page.waitForLoadState('networkidle');
|
|
124
|
+
// Check if we need to login
|
|
125
|
+
const needsLogin = await page.locator('form.woocommerce-form-login').count() > 0;
|
|
126
|
+
if (needsLogin) {
|
|
127
|
+
await this.login();
|
|
128
|
+
await page.goto(`${this.config.baseUrl}/my-account/view-order/${orderId}/`);
|
|
129
|
+
await page.waitForLoadState('networkidle');
|
|
130
|
+
}
|
|
131
|
+
// Extract order number, date, and status from the paragraph
|
|
132
|
+
const orderNumber = await page.locator('mark.order-number').textContent().catch(() => orderId) || orderId;
|
|
133
|
+
const orderDate = await page.locator('mark.order-date').textContent().catch(() => '') || '';
|
|
134
|
+
const orderStatus = await page.locator('mark.order-status').textContent().catch(() => '') || '';
|
|
135
|
+
// Extract customer info
|
|
136
|
+
let customerName = '';
|
|
137
|
+
let customerAddress = '';
|
|
138
|
+
let customerEmail = '';
|
|
139
|
+
let customerPhone = '';
|
|
140
|
+
try {
|
|
141
|
+
const addressBlock = page.locator('.woocommerce-customer-details address').first();
|
|
142
|
+
const addressText = await addressBlock.textContent() || '';
|
|
143
|
+
const lines = addressText.split('\n').map(l => l.trim()).filter(l => l);
|
|
144
|
+
// Parse address lines
|
|
145
|
+
if (lines.length > 0)
|
|
146
|
+
customerName = lines[0];
|
|
147
|
+
if (lines.length > 1)
|
|
148
|
+
customerAddress = lines.slice(1).filter(l => !l.includes('@') && !l.startsWith('+')).join(', ');
|
|
149
|
+
// Extract email and phone separately
|
|
150
|
+
customerEmail = await page.locator('.woocommerce-customer-details--email').first().textContent().catch(() => '') || '';
|
|
151
|
+
customerPhone = await page.locator('.woocommerce-customer-details--phone').first().textContent().catch(() => '') || '';
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// Customer details not available
|
|
155
|
+
}
|
|
156
|
+
// Extract line items
|
|
157
|
+
const items = [];
|
|
158
|
+
const itemRows = page.locator('table.woocommerce-table--order-details tbody tr.woocommerce-table__line-item');
|
|
159
|
+
const itemCount = await itemRows.count();
|
|
160
|
+
for (let i = 0; i < itemCount; i++) {
|
|
161
|
+
const itemRow = itemRows.nth(i);
|
|
162
|
+
const name = await itemRow.locator('.woocommerce-table__product-name').textContent() || '';
|
|
163
|
+
const qty = await itemRow.locator('.product-quantity').textContent() || '1';
|
|
164
|
+
const total = await itemRow.locator('.woocommerce-table__product-total').textContent() || '';
|
|
165
|
+
items.push({
|
|
166
|
+
sku: '',
|
|
167
|
+
name: name.trim(),
|
|
168
|
+
quantity: parseInt(qty.replace(/\D/g, ''), 10) || 1,
|
|
169
|
+
price: '',
|
|
170
|
+
total: total.trim(),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// Look for invoice link (search for links containing "invoice" or with "Bill" text)
|
|
174
|
+
const invoiceLink = await page.locator('a.print-invoice').first().getAttribute('href').catch(() => '');
|
|
175
|
+
// Extract total from the table footer
|
|
176
|
+
const totalText = await page.locator('table.woocommerce-table--order-details tfoot tr:last-child .woocommerce-Price-amount').textContent().catch(() => '');
|
|
177
|
+
return {
|
|
178
|
+
id: orderId,
|
|
179
|
+
orderNumber: orderNumber?.trim() || orderId,
|
|
180
|
+
date: orderDate?.trim() || '',
|
|
181
|
+
status: orderStatus?.trim() || '',
|
|
182
|
+
total: totalText?.trim() || '',
|
|
183
|
+
itemCount: items.length,
|
|
184
|
+
invoiceUrl: invoiceLink || undefined,
|
|
185
|
+
customer: {
|
|
186
|
+
name: customerName.trim(),
|
|
187
|
+
address: customerAddress.trim(),
|
|
188
|
+
city: '',
|
|
189
|
+
postcode: '',
|
|
190
|
+
email: customerEmail.trim(),
|
|
191
|
+
phone: customerPhone.trim(),
|
|
192
|
+
},
|
|
193
|
+
items,
|
|
194
|
+
subtotal: '',
|
|
195
|
+
shipping: '',
|
|
196
|
+
tax: '',
|
|
197
|
+
paymentMethod: '',
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
async downloadInvoice(orderId, savePath) {
|
|
201
|
+
try {
|
|
202
|
+
const order = await this.getOrder(orderId);
|
|
203
|
+
if (!order.invoiceUrl) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const page = await this.ensureBrowser();
|
|
207
|
+
// Construct full URL
|
|
208
|
+
const invoiceUrl = order.invoiceUrl.startsWith('http')
|
|
209
|
+
? order.invoiceUrl
|
|
210
|
+
: `${this.config.baseUrl}${order.invoiceUrl}`;
|
|
211
|
+
// Set up download listener before navigating
|
|
212
|
+
const downloadPromise = page.waitForEvent('download', { timeout: 30000 });
|
|
213
|
+
// Navigate to invoice URL (this triggers the download)
|
|
214
|
+
await page.goto(invoiceUrl, { waitUntil: 'commit' }).catch(() => {
|
|
215
|
+
// Ignore navigation error since download starts immediately
|
|
216
|
+
});
|
|
217
|
+
// Wait for the download to start
|
|
218
|
+
const download = await downloadPromise;
|
|
219
|
+
// Save the downloaded file
|
|
220
|
+
await download.saveAs(savePath);
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
console.error('Invoice download error:', error);
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async searchProducts(query) {
|
|
229
|
+
const page = await this.ensureBrowser();
|
|
230
|
+
const searchUrl = `${this.config.baseUrl}/?s=${encodeURIComponent(query)}&post_type=product`;
|
|
231
|
+
await page.goto(searchUrl);
|
|
232
|
+
await page.waitForLoadState('networkidle');
|
|
233
|
+
// Check if we need to login to see prices
|
|
234
|
+
const needsLogin = await page.locator('form.woocommerce-form-login').count() > 0;
|
|
235
|
+
if (needsLogin) {
|
|
236
|
+
await this.login();
|
|
237
|
+
await page.goto(searchUrl);
|
|
238
|
+
await page.waitForLoadState('networkidle');
|
|
239
|
+
}
|
|
240
|
+
const products = [];
|
|
241
|
+
// Find all product items
|
|
242
|
+
const productItems = page.locator('li.product, .product-item');
|
|
243
|
+
const count = await productItems.count();
|
|
244
|
+
for (let i = 0; i < count; i++) {
|
|
245
|
+
const item = productItems.nth(i);
|
|
246
|
+
const link = item.locator('a').first();
|
|
247
|
+
const url = await link.getAttribute('href') || '';
|
|
248
|
+
const name = await item.locator('h2, h3, .woocommerce-loop-product__title').first().textContent() || '';
|
|
249
|
+
const price = await item.locator('.price').first().textContent().catch(() => undefined);
|
|
250
|
+
const img = await item.locator('img').first().getAttribute('src');
|
|
251
|
+
if (name && url) {
|
|
252
|
+
products.push({
|
|
253
|
+
id: '',
|
|
254
|
+
name: name.trim(),
|
|
255
|
+
url,
|
|
256
|
+
price: price?.trim(),
|
|
257
|
+
imageUrl: img || undefined,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return products;
|
|
262
|
+
}
|
|
263
|
+
async close() {
|
|
264
|
+
if (this.browser) {
|
|
265
|
+
await this.browser.close();
|
|
266
|
+
this.browser = undefined;
|
|
267
|
+
this.page = undefined;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
export default SkuterzonePlaywrightClient;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skuterzone Pro Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for skuterzonepro.com integration
|
|
5
|
+
* WooCommerce-based B2B parts supplier
|
|
6
|
+
*/
|
|
7
|
+
export interface SkuterzoneConfig {
|
|
8
|
+
username: string;
|
|
9
|
+
password: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LoginResult {
|
|
13
|
+
success: boolean;
|
|
14
|
+
cookies: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SkuterzoneOrder {
|
|
18
|
+
id: string;
|
|
19
|
+
orderNumber: string;
|
|
20
|
+
date: string;
|
|
21
|
+
status: string;
|
|
22
|
+
total: string;
|
|
23
|
+
itemCount: number;
|
|
24
|
+
}
|
|
25
|
+
export interface SkuterzoneOrderDetails extends SkuterzoneOrder {
|
|
26
|
+
invoiceUrl?: string;
|
|
27
|
+
trackingNumber?: string;
|
|
28
|
+
customer: {
|
|
29
|
+
name: string;
|
|
30
|
+
company?: string;
|
|
31
|
+
address: string;
|
|
32
|
+
city: string;
|
|
33
|
+
postcode: string;
|
|
34
|
+
email: string;
|
|
35
|
+
phone?: string;
|
|
36
|
+
};
|
|
37
|
+
items: SkuterzoneLineItem[];
|
|
38
|
+
subtotal: string;
|
|
39
|
+
shipping: string;
|
|
40
|
+
tax: string;
|
|
41
|
+
paymentMethod: string;
|
|
42
|
+
shippingMethod?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface SkuterzoneLineItem {
|
|
45
|
+
sku: string;
|
|
46
|
+
name: string;
|
|
47
|
+
quantity: number;
|
|
48
|
+
price: string;
|
|
49
|
+
total: string;
|
|
50
|
+
}
|
|
51
|
+
export interface SkuterzoneProduct {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
sku?: string;
|
|
55
|
+
price?: string;
|
|
56
|
+
url: string;
|
|
57
|
+
imageUrl?: string;
|
|
58
|
+
stockStatus?: string;
|
|
59
|
+
category?: string;
|
|
60
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wattiz Client with Playwright
|
|
3
|
+
*
|
|
4
|
+
* Uses real browser automation for PrestaShop platform
|
|
5
|
+
*/
|
|
6
|
+
import { WattizConfig, WattizOrder, WattizOrderDetails, WattizProduct, WattizLoginResult } from './wattiz-types.js';
|
|
7
|
+
export declare class WattizPlaywrightClient {
|
|
8
|
+
private config;
|
|
9
|
+
private browser?;
|
|
10
|
+
private page?;
|
|
11
|
+
private contextCreated;
|
|
12
|
+
constructor(config: WattizConfig);
|
|
13
|
+
static fromConfig(): WattizPlaywrightClient;
|
|
14
|
+
private ensureBrowser;
|
|
15
|
+
login(): Promise<WattizLoginResult>;
|
|
16
|
+
listOrders(): Promise<WattizOrder[]>;
|
|
17
|
+
getOrder(orderId: string): Promise<WattizOrderDetails>;
|
|
18
|
+
downloadInvoice(orderId: string, savePath: string): Promise<boolean>;
|
|
19
|
+
searchProducts(query: string): Promise<WattizProduct[]>;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export default WattizPlaywrightClient;
|