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.
- package/dist/cli/index.js +12 -44
- package/dist/connection-string.class.js +13 -24
- package/dist/database.class.js +1 -9
- package/dist/plugins/api/middlewares/validator.js +4 -7
- package/dist/plugins/backup/multi-backup-driver.class.js +0 -8
- package/dist/plugins/identity/concerns/interactive-wizard.js +71 -68
- package/dist/plugins/identity/concerns/onboarding-manager.js +3 -3
- package/dist/plugins/identity/index.js +1 -9
- package/dist/plugins/identity/ui/layouts/base.js +0 -1
- package/dist/plugins/metrics.plugin.js +4 -10
- package/dist/plugins/puppeteer.plugin.js +0 -11
- package/dist/s3db-lite.cjs +16 -43
- package/dist/s3db-lite.d.ts +0 -5
- package/dist/s3db-lite.es.js +16 -43
- package/dist/s3db.cjs +47852 -317
- package/dist/s3db.d.ts +1 -18
- package/dist/s3db.es.js +47855 -321
- package/dist/schema.class.js +0 -8
- package/dist/types/database.class.d.ts +0 -2
- package/dist/types/plugins/api/middlewares/validator.d.ts +10 -10
- package/dist/types/plugins/backup/multi-backup-driver.class.d.ts +0 -1
- package/dist/types/plugins/identity/concerns/interactive-wizard.d.ts +2 -4
- package/dist/types/plugins/identity/index.d.ts +1 -4
- package/dist/types/plugins/identity/ui/layouts/base.d.ts +0 -1
- package/dist/types/plugins/metrics.plugin.d.ts +0 -5
- package/dist/types/plugins/puppeteer.plugin.d.ts +0 -3
- package/dist/types/schema.class.d.ts +0 -3
- package/mcp/tools/debugging.js +5 -6
- package/mcp/tools/debugging.ts +4 -5
- package/package.json +2 -7
- package/src/cli/index.ts +26 -44
- package/src/connection-string.class.ts +12 -24
- package/src/database.class.ts +1 -14
- package/src/plugins/api/middlewares/validator.ts +10 -18
- package/src/plugins/backup/multi-backup-driver.class.ts +0 -12
- package/src/plugins/identity/concerns/interactive-wizard.ts +74 -81
- package/src/plugins/identity/concerns/onboarding-manager.ts +3 -3
- package/src/plugins/identity/index.ts +2 -13
- package/src/plugins/identity/ui/layouts/base.ts +0 -2
- package/src/plugins/metrics.plugin.ts +3 -19
- package/src/plugins/puppeteer.plugin.ts +0 -15
- 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
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
186
|
-
|
|
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
|
|
200
|
-
|
|
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';
|
package/dist/database.class.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
31
|
-
|
|
32
|
-
partial: isPartial,
|
|
33
|
-
strict: !isPartial
|
|
29
|
+
const validationResult = await resource.validator.validate(data, {
|
|
30
|
+
includeId: true
|
|
34
31
|
});
|
|
35
|
-
if (!validationResult.
|
|
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
|
|
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
|
|
32
|
-
const email = await this._promptEmail(
|
|
33
|
-
const password = await this._promptPassword(
|
|
34
|
-
const name = await this._promptName(
|
|
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(
|
|
56
|
+
async _promptEmail(prompt) {
|
|
57
57
|
let attempts = 0;
|
|
58
58
|
while (attempts < this.maxEmailAttempts) {
|
|
59
59
|
attempts++;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
71
|
+
});
|
|
72
|
+
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email || '')) {
|
|
73
|
+
return email;
|
|
71
74
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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(
|
|
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
|
|
92
|
-
|
|
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.
|
|
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
|
|
127
|
-
|
|
128
|
-
name
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
|
192
|
+
async _loadTuiuiu() {
|
|
187
193
|
try {
|
|
188
|
-
const
|
|
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('
|
|
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
|
|
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('
|
|
458
|
-
throw new PluginError('Interactive mode requires "
|
|
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
|
|
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?.
|
|
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 = {},
|
|
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
|
|
39
|
-
errors: resourceNamesOption.errors
|
|
40
|
-
performance: resourceNamesOption.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);
|