s3db.js 19.4.2 → 19.4.4-next.033a879c

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.
Files changed (42) hide show
  1. package/dist/cli/index.js +12 -44
  2. package/dist/connection-string.class.js +13 -24
  3. package/dist/database.class.js +1 -9
  4. package/dist/plugins/api/middlewares/validator.js +4 -7
  5. package/dist/plugins/backup/multi-backup-driver.class.js +0 -8
  6. package/dist/plugins/identity/concerns/interactive-wizard.js +71 -68
  7. package/dist/plugins/identity/concerns/onboarding-manager.js +3 -3
  8. package/dist/plugins/identity/index.js +1 -9
  9. package/dist/plugins/identity/ui/layouts/base.js +0 -1
  10. package/dist/plugins/metrics.plugin.js +4 -10
  11. package/dist/plugins/puppeteer.plugin.js +0 -11
  12. package/dist/s3db-lite.cjs +16 -43
  13. package/dist/s3db-lite.d.ts +0 -5
  14. package/dist/s3db-lite.es.js +16 -43
  15. package/dist/s3db.cjs +47852 -317
  16. package/dist/s3db.d.ts +1 -18
  17. package/dist/s3db.es.js +47855 -321
  18. package/dist/schema.class.js +0 -8
  19. package/dist/types/database.class.d.ts +0 -2
  20. package/dist/types/plugins/api/middlewares/validator.d.ts +10 -10
  21. package/dist/types/plugins/backup/multi-backup-driver.class.d.ts +0 -1
  22. package/dist/types/plugins/identity/concerns/interactive-wizard.d.ts +2 -4
  23. package/dist/types/plugins/identity/index.d.ts +1 -4
  24. package/dist/types/plugins/identity/ui/layouts/base.d.ts +0 -1
  25. package/dist/types/plugins/metrics.plugin.d.ts +0 -5
  26. package/dist/types/plugins/puppeteer.plugin.d.ts +0 -3
  27. package/dist/types/schema.class.d.ts +0 -3
  28. package/mcp/tools/debugging.js +5 -6
  29. package/mcp/tools/debugging.ts +4 -5
  30. package/package.json +2 -7
  31. package/src/cli/index.ts +26 -44
  32. package/src/connection-string.class.ts +12 -24
  33. package/src/database.class.ts +1 -14
  34. package/src/plugins/api/middlewares/validator.ts +10 -18
  35. package/src/plugins/backup/multi-backup-driver.class.ts +0 -12
  36. package/src/plugins/identity/concerns/interactive-wizard.ts +74 -81
  37. package/src/plugins/identity/concerns/onboarding-manager.ts +3 -3
  38. package/src/plugins/identity/index.ts +2 -13
  39. package/src/plugins/identity/ui/layouts/base.ts +0 -2
  40. package/src/plugins/metrics.plugin.ts +3 -19
  41. package/src/plugins/puppeteer.plugin.ts +0 -15
  42. package/src/schema.class.ts +0 -9
package/dist/cli/index.js CHANGED
@@ -8,8 +8,8 @@
8
8
  * terminal interaction and formatted output using tuiuiu.js components.
9
9
  */
10
10
  import { createCLI } from 'cli-args-parser';
11
- import inquirer from 'inquirer';
12
11
  import { Table, Spinner, red, green, yellow, cyan, gray, bold, dim, c, } from './components/index.js';
12
+ import { prompt } from 'tuiuiu.js';
13
13
  import { S3db } from '../database.class.js';
14
14
  import fs from 'fs/promises';
15
15
  import path from 'path';
@@ -176,22 +176,11 @@ const cli = createCLI({
176
176
  configure: {
177
177
  description: 'Configure S3DB connection',
178
178
  handler: async () => {
179
- const answers = await inquirer.prompt([
180
- {
181
- type: 'input',
182
- name: 'connection',
183
- message: 'Enter S3 connection string:',
184
- default: 's3://KEY:SECRET@bucket/database'
185
- },
186
- {
187
- type: 'list',
188
- name: 'defaultBehavior',
189
- message: 'Default behavior for resources:',
190
- choices: ['user-managed', 'enforce-limits', 'body-overflow', 'body-only', 'truncate-data'],
191
- default: 'user-managed'
192
- }
193
- ]);
194
- await saveConfig(answers);
179
+ const connection = await prompt.input('Enter S3 connection string:', {
180
+ default: 's3://KEY:SECRET@bucket/database'
181
+ });
182
+ const defaultBehavior = await prompt.select('Default behavior for resources:', ['user-managed', 'enforce-limits', 'body-overflow', 'body-only', 'truncate-data'], { default: 'user-managed' });
183
+ await saveConfig({ connection, defaultBehavior });
195
184
  console.log(green('✓ Configuration saved to ~/.s3db/config.json'));
196
185
  }
197
186
  },
@@ -406,15 +395,8 @@ const cli = createCLI({
406
395
  const opts = result.options;
407
396
  const pos = result.positional;
408
397
  if (!opts.force) {
409
- const { confirm } = await inquirer.prompt([
410
- {
411
- type: 'confirm',
412
- name: 'confirm',
413
- message: `Are you sure you want to delete ${pos.id} from ${pos.resource}?`,
414
- default: false
415
- }
416
- ]);
417
- if (!confirm) {
398
+ const confirmed = await prompt.confirm(`Are you sure you want to delete ${pos.id} from ${pos.resource}?`, { default: false });
399
+ if (!confirmed) {
418
400
  console.log(yellow('Cancelled'));
419
401
  return;
420
402
  }
@@ -1081,15 +1063,8 @@ const cli = createCLI({
1081
1063
  const opts = result.options;
1082
1064
  const pos = result.positional;
1083
1065
  if (!opts.force) {
1084
- const { confirm } = await inquirer.prompt([
1085
- {
1086
- type: 'confirm',
1087
- name: 'confirm',
1088
- message: `This will delete ALL data from ${pos.resource}. Continue?`,
1089
- default: false
1090
- }
1091
- ]);
1092
- if (!confirm) {
1066
+ const confirmed = await prompt.confirm(`This will delete ALL data from ${pos.resource}. Continue?`, { default: false });
1067
+ if (!confirmed) {
1093
1068
  console.log(yellow('Cancelled'));
1094
1069
  return;
1095
1070
  }
@@ -1238,15 +1213,8 @@ const cli = createCLI({
1238
1213
  handler: async (result) => {
1239
1214
  const opts = result.options;
1240
1215
  if (!opts.force) {
1241
- const { confirm } = await inquirer.prompt([
1242
- {
1243
- type: 'confirm',
1244
- name: 'confirm',
1245
- message: 'This will rollback ALL migrations. Continue?',
1246
- default: false
1247
- }
1248
- ]);
1249
- if (!confirm) {
1216
+ const confirmed = await prompt.confirm('This will rollback ALL migrations. Continue?', { default: false });
1217
+ if (!confirmed) {
1250
1218
  console.log(yellow('Cancelled'));
1251
1219
  return;
1252
1220
  }
@@ -158,11 +158,18 @@ export class ConnectionString {
158
158
  this.secretAccessKey = undefined;
159
159
  // Parse pathname
160
160
  let pathname = uri.pathname || '';
161
+ let isRelativePath = false;
161
162
  // Handle Windows paths (file:///C:/path/to/data)
162
163
  if (uri.hostname && uri.hostname.match(/^[a-zA-Z]$/)) {
163
164
  // Windows drive letter in hostname (file://C:/path)
164
165
  pathname = `${uri.hostname}:${pathname}`;
165
166
  }
167
+ else if (uri.hostname === '.' || uri.hostname === '..') {
168
+ // Relative path: file://./path or file://../path
169
+ // URL parser puts . or .. in hostname, reconstruct the relative path
170
+ pathname = `${uri.hostname}${pathname}`;
171
+ isRelativePath = true;
172
+ }
166
173
  else if (uri.hostname && uri.hostname !== 'localhost') {
167
174
  // UNC path (file://server/share/path)
168
175
  pathname = `//${uri.hostname}${pathname}`;
@@ -182,37 +189,19 @@ export class ConnectionString {
182
189
  suggestion: 'Use file:///absolute/path or file://./relative/path'
183
190
  });
184
191
  }
185
- // Parse path segments: /basePath/bucket/keyPrefix
186
- const segments = decodedPath.split('/').filter(Boolean);
187
- if (segments.length === 0) {
188
- throw new ConnectionStringError('file:// connection string requires a path', {
189
- input: uri.href,
190
- suggestion: 'Use file:///absolute/path or file://./relative/path'
191
- });
192
- }
193
- // For relative paths starting with ./ or ../
194
- if (decodedPath.startsWith('./') || decodedPath.startsWith('../')) {
192
+ // For relative paths (detected from hostname or starting with ./ or ../)
193
+ if (isRelativePath || decodedPath.startsWith('./') || decodedPath.startsWith('../')) {
195
194
  this.basePath = path.resolve(decodedPath);
196
195
  this.bucket = 's3db';
197
196
  this.keyPrefix = '';
198
197
  }
199
- else if (segments.length === 1) {
200
- this.basePath = path.resolve('/', segments[0]);
198
+ else {
199
+ // Absolute path: use the entire path as basePath
200
+ // This is the intuitive behavior - file:///path/to/data means "use /path/to/data"
201
+ this.basePath = path.resolve(decodedPath);
201
202
  this.bucket = 's3db';
202
203
  this.keyPrefix = '';
203
204
  }
204
- else if (segments.length === 2) {
205
- const [baseSegment, bucketSegment] = segments;
206
- this.basePath = path.resolve('/', baseSegment);
207
- this.bucket = bucketSegment;
208
- this.keyPrefix = '';
209
- }
210
- else {
211
- const [baseSegment, bucketSegment, ...prefixSegments] = segments;
212
- this.basePath = path.resolve('/', baseSegment);
213
- this.bucket = bucketSegment;
214
- this.keyPrefix = prefixSegments.join('/');
215
- }
216
205
  // Set synthetic endpoint for compatibility
217
206
  this.endpoint = `file://${this.basePath}`;
218
207
  this.region = 'local';
@@ -90,7 +90,7 @@ export class Database extends SafeEventEmitter {
90
90
  });
91
91
  this.savedMetadata = null;
92
92
  this.databaseOptions = options;
93
- const executorPoolConfig = options?.executorPool ?? options?.operationsPool;
93
+ const executorPoolConfig = options?.executorPool;
94
94
  this._parallelism = this._normalizeParallelism(options?.parallelism ?? executorPoolConfig?.concurrency, 10);
95
95
  this.logLevel = options.logLevel || options.loggerOptions?.level || 'info';
96
96
  const loggerOptions = { ...(options.loggerOptions || {}) };
@@ -111,11 +111,6 @@ export class Database extends SafeEventEmitter {
111
111
  });
112
112
  }
113
113
  this._childLoggerLevels = options.loggerOptions?.childLevels || {};
114
- if (options?.operationsPool && !options?.executorPool) {
115
- this.logger.warn('⚠️ "operationsPool" is deprecated in s3db.js v16.x. ' +
116
- 'Use "executorPool" instead. ' +
117
- 'Migration: https://s3db.js/docs/migration/v16-to-v17');
118
- }
119
114
  this.executorPool = this._normalizeOperationsPool(executorPoolConfig, this._parallelism);
120
115
  if (options?.taskExecutorMonitoring) {
121
116
  this.executorPool.monitoring = this._deepMerge(this.executorPool.monitoring || {}, options.taskExecutorMonitoring);
@@ -276,9 +271,6 @@ export class Database extends SafeEventEmitter {
276
271
  this.executorPool.concurrency = normalized;
277
272
  }
278
273
  }
279
- get operationsPool() {
280
- return this.executorPool;
281
- }
282
274
  get config() {
283
275
  return {
284
276
  version: this.version,
@@ -8,8 +8,7 @@ export function createValidationMiddleware(resource, options = {}) {
8
8
  return cached.middleware;
9
9
  }
10
10
  }
11
- const { validateOnInsert = true, validateOnUpdate = true, partial = true } = options;
12
- const schema = resource.schema;
11
+ const { validateOnInsert = true, validateOnUpdate = true } = options;
13
12
  const middleware = async (c, next) => {
14
13
  const method = c.req.method;
15
14
  const shouldValidate = (method === 'POST' && validateOnInsert) ||
@@ -27,12 +26,10 @@ export function createValidationMiddleware(resource, options = {}) {
27
26
  ]);
28
27
  return c.json(response, response._status);
29
28
  }
30
- const isPartial = method === 'PATCH' && partial;
31
- const validationResult = schema.validate(data, {
32
- partial: isPartial,
33
- strict: !isPartial
29
+ const validationResult = await resource.validator.validate(data, {
30
+ includeId: true
34
31
  });
35
- if (!validationResult.valid) {
32
+ if (!validationResult.isValid) {
36
33
  const errors = (validationResult.errors || []).map((err) => ({
37
34
  field: err.field || err.attribute || 'unknown',
38
35
  message: err.message,
@@ -57,14 +57,6 @@ export default class MultiBackupDriver extends BaseBackupDriver {
57
57
  });
58
58
  }
59
59
  }
60
- if (this.config.requireAll !== undefined) {
61
- this.logger.warn('[MultiBackupDriver] DEPRECATED: The "requireAll" option is deprecated. ' +
62
- 'Use "strategy" instead: strategy: "any" (instead of requireAll: false) or strategy: "all" (instead of requireAll: true). ' +
63
- 'This will be removed in v17.0.');
64
- if (this.config.requireAll === false) {
65
- this.config.strategy = 'any';
66
- }
67
- }
68
60
  this.log(`Initialized with ${this.drivers.length} destinations, strategy: ${this.config.strategy}`);
69
61
  }
70
62
  async upload(filePath, backupId, manifest) {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Interactive Onboarding Wizard - CLI prompts for admin account creation
3
3
  *
4
- * Uses enquirer for beautiful CLI prompts (lazy-loaded peer dependency)
4
+ * Uses tuiuiu.js for CLI prompts (zero-dependency)
5
5
  * Only works in TTY environments (development)
6
6
  *
7
7
  * Security:
@@ -28,10 +28,10 @@ export class InteractiveWizard {
28
28
  }
29
29
  async run() {
30
30
  this._printBanner();
31
- const { Input, Password } = await this._loadEnquirer();
32
- const email = await this._promptEmail(Input);
33
- const password = await this._promptPassword(Password);
34
- const name = await this._promptName(Input);
31
+ const prompt = await this._loadTuiuiu();
32
+ const email = await this._promptEmail(prompt);
33
+ const password = await this._promptPassword(prompt);
34
+ const name = await this._promptName(prompt);
35
35
  this._printSuccess(email);
36
36
  return { email, password, name };
37
37
  }
@@ -53,28 +53,38 @@ export class InteractiveWizard {
53
53
  console.log(` Login URL: ${this.config.issuer || 'http://localhost:4000'}/login`);
54
54
  console.log('');
55
55
  }
56
- async _promptEmail(Input) {
56
+ async _promptEmail(prompt) {
57
57
  let attempts = 0;
58
58
  while (attempts < this.maxEmailAttempts) {
59
59
  attempts++;
60
- const prompt = new Input({
61
- name: 'email',
62
- message: '👤 Admin Email:',
63
- validate: (value) => {
64
- if (!value || !value.trim()) {
65
- return 'Email is required';
66
- }
67
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
68
- return 'Please enter a valid email address';
60
+ try {
61
+ const email = await prompt.input('👤 Admin Email:', {
62
+ validate: (value) => {
63
+ if (!value || !value.trim()) {
64
+ return 'Email is required';
65
+ }
66
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
67
+ return 'Please enter a valid email address';
68
+ }
69
+ return true;
69
70
  }
70
- return true;
71
+ });
72
+ if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email || '')) {
73
+ return email;
71
74
  }
72
- });
73
- const email = await prompt.run();
74
- if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email || '')) {
75
- return email;
75
+ console.log('❌ Invalid email address. Please try again.\n');
76
+ }
77
+ catch (error) {
78
+ if (error.message?.includes('canceled') || error.message?.includes('aborted')) {
79
+ throw new PluginError('Onboarding canceled by user', {
80
+ pluginName: 'IdentityPlugin',
81
+ operation: 'runInteractiveMode',
82
+ statusCode: 400,
83
+ retriable: false
84
+ });
85
+ }
86
+ throw error;
76
87
  }
77
- console.log('❌ Invalid email address. Please try again.\n');
78
88
  }
79
89
  throw new PluginError(`Max email attempts (${this.maxEmailAttempts}) exceeded`, {
80
90
  pluginName: 'IdentityPlugin',
@@ -83,13 +93,27 @@ export class InteractiveWizard {
83
93
  retriable: false
84
94
  });
85
95
  }
86
- async _promptPassword(Password) {
96
+ async _promptPassword(prompt) {
87
97
  let attempts = 0;
88
98
  while (attempts < this.maxPasswordAttempts) {
89
99
  attempts++;
90
100
  try {
91
- const password = await this._promptPasswordOnce(Password);
92
- const confirmPassword = await this._promptPasswordConfirm(Password);
101
+ const password = await prompt.password('🔒 Admin Password:', {
102
+ validate: (value) => {
103
+ if (!value || !value.trim()) {
104
+ return 'Password is required';
105
+ }
106
+ return true;
107
+ }
108
+ });
109
+ const confirmPassword = await prompt.password('🔒 Confirm Password:', {
110
+ validate: (value) => {
111
+ if (!value || !value.trim()) {
112
+ return 'Password confirmation is required';
113
+ }
114
+ return true;
115
+ }
116
+ });
93
117
  if (password !== confirmPassword) {
94
118
  console.log('❌ Passwords do not match. Please try again.\n');
95
119
  continue;
@@ -104,7 +128,7 @@ export class InteractiveWizard {
104
128
  return password;
105
129
  }
106
130
  catch (error) {
107
- if (error.name === 'ExitPromptError') {
131
+ if (error.message?.includes('canceled') || error.message?.includes('aborted')) {
108
132
  throw new PluginError('Onboarding canceled by user', {
109
133
  pluginName: 'IdentityPlugin',
110
134
  operation: 'runInteractiveMode',
@@ -123,42 +147,24 @@ export class InteractiveWizard {
123
147
  suggestion: 'Use env or config mode for automated setup'
124
148
  });
125
149
  }
126
- async _promptPasswordOnce(Password) {
127
- const prompt = new Password({
128
- name: 'password',
129
- message: '🔒 Admin Password:',
130
- mask: '*',
131
- validate: (value) => {
132
- if (!value || !value.trim()) {
133
- return 'Password is required';
134
- }
135
- return true;
136
- }
137
- });
138
- return prompt.run();
139
- }
140
- async _promptPasswordConfirm(Password) {
141
- const prompt = new Password({
142
- name: 'confirmPassword',
143
- message: '🔒 Confirm Password:',
144
- mask: '*',
145
- validate: (value) => {
146
- if (!value || !value.trim()) {
147
- return 'Password confirmation is required';
148
- }
149
- return true;
150
+ async _promptName(prompt) {
151
+ try {
152
+ const name = await prompt.input('📝 Display Name (optional):', {
153
+ default: 'Administrator'
154
+ });
155
+ return name || 'Administrator';
156
+ }
157
+ catch (error) {
158
+ if (error.message?.includes('canceled') || error.message?.includes('aborted')) {
159
+ throw new PluginError('Onboarding canceled by user', {
160
+ pluginName: 'IdentityPlugin',
161
+ operation: 'runInteractiveMode',
162
+ statusCode: 400,
163
+ retriable: false
164
+ });
150
165
  }
151
- });
152
- return prompt.run();
153
- }
154
- async _promptName(Input) {
155
- const prompt = new Input({
156
- name: 'name',
157
- message: '📝 Display Name (optional):',
158
- initial: 'Administrator'
159
- });
160
- const name = await prompt.run();
161
- return name || 'Administrator';
166
+ throw error;
167
+ }
162
168
  }
163
169
  _validatePassword(password) {
164
170
  const errors = [];
@@ -183,21 +189,18 @@ export class InteractiveWizard {
183
189
  errors
184
190
  };
185
191
  }
186
- async _loadEnquirer() {
192
+ async _loadTuiuiu() {
187
193
  try {
188
- const enquirer = await import('enquirer');
189
- return {
190
- Input: enquirer.Input,
191
- Password: enquirer.Password
192
- };
194
+ const tuiuiu = await import('tuiuiu.js');
195
+ return tuiuiu.prompt;
193
196
  }
194
197
  catch (error) {
195
- throw new PluginError('enquirer package is required for interactive mode. Install with: npm install enquirer', {
198
+ throw new PluginError('tuiuiu.js package is required for interactive mode. Install with: npm install tuiuiu.js', {
196
199
  pluginName: 'IdentityPlugin',
197
200
  operation: 'runInteractiveMode',
198
201
  cause: error,
199
202
  retriable: false,
200
- suggestion: 'Run: npm install enquirer@^2.4.1'
203
+ suggestion: 'Run: npm install tuiuiu.js'
201
204
  });
202
205
  }
203
206
  }
@@ -454,13 +454,13 @@ export class OnboardingManager {
454
454
  return admin;
455
455
  }
456
456
  catch (error) {
457
- if (error.code === 'ERR_MODULE_NOT_FOUND' || error.message.includes('enquirer')) {
458
- throw new PluginError('Interactive mode requires "enquirer" package. Install with: npm install enquirer', {
457
+ if (error.code === 'ERR_MODULE_NOT_FOUND' || error.message.includes('tuiuiu')) {
458
+ throw new PluginError('Interactive mode requires "tuiuiu.js" package. Install with: npm install tuiuiu.js', {
459
459
  pluginName: 'IdentityPlugin',
460
460
  operation: 'runInteractiveMode',
461
461
  cause: error,
462
462
  retriable: false,
463
- suggestion: 'Run: npm install enquirer, or use env/config mode instead'
463
+ suggestion: 'Run: npm install tuiuiu.js, or use env/config mode instead'
464
464
  });
465
465
  }
466
466
  throw error;
@@ -181,14 +181,6 @@ export class IdentityPlugin extends Plugin {
181
181
  tagline: options.ui?.tagline || 'Secure Identity & Access Management',
182
182
  welcomeMessage: options.ui?.welcomeMessage || 'Welcome back!',
183
183
  logoUrl: options.ui?.logoUrl || null,
184
- logo: (() => {
185
- if (options.ui?.logo) {
186
- this.logger?.warn('[IdentityPlugin] DEPRECATED: The "logo" field is deprecated. ' +
187
- 'Use "logoUrl" instead: { ui: { logoUrl: "..." } }. ' +
188
- 'This will be removed in v17.0.');
189
- }
190
- return options.ui?.logo || null;
191
- })(),
192
184
  favicon: options.ui?.favicon || null,
193
185
  primaryColor: options.ui?.primaryColor || '#007bff',
194
186
  secondaryColor: options.ui?.secondaryColor || '#6c757d',
@@ -233,7 +225,7 @@ export class IdentityPlugin extends Plugin {
233
225
  templates: {
234
226
  baseUrl: options.email?.templates?.baseUrl || options.ui?.baseUrl || `http://localhost:${options.port || 4000}`,
235
227
  brandName: options.email?.templates?.brandName || options.ui?.title || 'S3DB Identity',
236
- brandLogo: options.email?.templates?.brandLogo || options.ui?.logo || null,
228
+ brandLogo: options.email?.templates?.brandLogo || options.ui?.logoUrl || null,
237
229
  brandColor: options.email?.templates?.brandColor || options.ui?.primaryColor || '#007bff',
238
230
  supportEmail: options.email?.templates?.supportEmail || options.email?.replyTo || null,
239
231
  customFooter: options.email?.templates?.customFooter || null
@@ -39,7 +39,6 @@ export function BaseLayout(props) {
39
39
  const { title = 'Identity Provider', content = '', user = null, config = {}, error = null, success = null } = props;
40
40
  const theme = {
41
41
  title: config.title || 'S3DB Identity',
42
- logo: config.logo || null,
43
42
  logoUrl: config.logoUrl || null,
44
43
  favicon: config.favicon || null,
45
44
  registrationEnabled: config.registrationEnabled !== false,
@@ -25,19 +25,13 @@ export class MetricsPlugin extends Plugin {
25
25
  this.logger = createLogger({ name: 'MetricsPlugin', level: logLevel });
26
26
  }
27
27
  const metricsOptions = this.options;
28
- const { resourceNames = {}, resources = {}, collectPerformance, collectErrors, collectUsage, retentionDays, flushInterval, prometheus = {}, ...rest } = metricsOptions;
28
+ const { resourceNames = {}, collectPerformance, collectErrors, collectUsage, retentionDays, flushInterval, prometheus = {}, ...rest } = metricsOptions;
29
29
  const resourceNamesOption = resourceNames || {};
30
- const legacyResourceOption = resources || {};
31
30
  const prometheusConfig = prometheus || {};
32
- if (Object.keys(legacyResourceOption).length > 0) {
33
- this.logger.warn({}, '[MetricsPlugin] DEPRECATED: The "resources" option is deprecated. ' +
34
- 'Use "resourceNames" instead: { resourceNames: { metrics: "...", errors: "...", performance: "..." } }. ' +
35
- 'This will be removed in v17.0.');
36
- }
37
31
  const resourceOverrides = {
38
- metrics: resourceNamesOption.metrics ?? legacyResourceOption.metrics,
39
- errors: resourceNamesOption.errors ?? legacyResourceOption.errors,
40
- performance: resourceNamesOption.performance ?? legacyResourceOption.performance
32
+ metrics: resourceNamesOption.metrics,
33
+ errors: resourceNamesOption.errors,
34
+ performance: resourceNamesOption.performance
41
35
  };
42
36
  this._resourceDescriptors = {
43
37
  metrics: {
@@ -198,9 +198,6 @@ export class PuppeteerPlugin extends Plugin {
198
198
  timeout: 10000,
199
199
  successRateThreshold: 0.3
200
200
  },
201
- server: null,
202
- username: null,
203
- password: null,
204
201
  ...options.proxy
205
202
  },
206
203
  retries: {
@@ -284,11 +281,6 @@ export class PuppeteerPlugin extends Plugin {
284
281
  message: 'pool.reuseTab is not supported yet and will be ignored.'
285
282
  });
286
283
  }
287
- if (options.proxy?.server || options.proxy?.username || options.proxy?.password) {
288
- this.logger.warn('[PuppeteerPlugin] DEPRECATED: The single proxy config (server, username, password) is deprecated. ' +
289
- 'Use the proxy.list array with proxy objects instead. Example: proxy: { list: [{ proxy: "http://host:port", username: "user", password: "pass" }] }. ' +
290
- 'This will be removed in v17.0.');
291
- }
292
284
  }
293
285
  _resolveResourceNames() {
294
286
  return resolveResourceNames('puppeteer', this._resourceDescriptors, {
@@ -475,9 +467,6 @@ export class PuppeteerPlugin extends Plugin {
475
467
  const proxyArgs = this.proxyManager.getProxyLaunchArgs(proxy);
476
468
  launchOptions.args.push(...proxyArgs);
477
469
  }
478
- else if (this.config.proxy.enabled && this.config.proxy.server) {
479
- launchOptions.args.push(`--proxy-server=${this.config.proxy.server}`);
480
- }
481
470
  const browser = await this.puppeteer.launch(launchOptions);
482
471
  if (!proxy && this.config.pool.enabled) {
483
472
  this.browserPool.push(browser);