s3db.js 13.6.0 → 14.0.2
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/README.md +139 -43
- package/dist/s3db.cjs +72425 -38970
- package/dist/s3db.cjs.map +1 -1
- package/dist/s3db.es.js +72177 -38764
- package/dist/s3db.es.js.map +1 -1
- package/mcp/lib/base-handler.js +157 -0
- package/mcp/lib/handlers/connection-handler.js +280 -0
- package/mcp/lib/handlers/query-handler.js +533 -0
- package/mcp/lib/handlers/resource-handler.js +428 -0
- package/mcp/lib/tool-registry.js +336 -0
- package/mcp/lib/tools/connection-tools.js +161 -0
- package/mcp/lib/tools/query-tools.js +267 -0
- package/mcp/lib/tools/resource-tools.js +404 -0
- package/package.json +94 -49
- package/src/clients/memory-client.class.js +346 -191
- package/src/clients/memory-storage.class.js +300 -84
- package/src/clients/s3-client.class.js +7 -6
- package/src/concerns/geo-encoding.js +19 -2
- package/src/concerns/ip.js +59 -9
- package/src/concerns/money.js +8 -1
- package/src/concerns/password-hashing.js +49 -8
- package/src/concerns/plugin-storage.js +186 -18
- package/src/concerns/storage-drivers/filesystem-driver.js +284 -0
- package/src/database.class.js +139 -29
- package/src/errors.js +332 -42
- package/src/plugins/api/auth/oidc-auth.js +66 -17
- package/src/plugins/api/auth/strategies/base-strategy.class.js +74 -0
- package/src/plugins/api/auth/strategies/factory.class.js +63 -0
- package/src/plugins/api/auth/strategies/global-strategy.class.js +44 -0
- package/src/plugins/api/auth/strategies/path-based-strategy.class.js +83 -0
- package/src/plugins/api/auth/strategies/path-rules-strategy.class.js +118 -0
- package/src/plugins/api/concerns/failban-manager.js +106 -57
- package/src/plugins/api/concerns/opengraph-helper.js +116 -0
- package/src/plugins/api/concerns/route-context.js +601 -0
- package/src/plugins/api/concerns/state-machine.js +288 -0
- package/src/plugins/api/index.js +180 -41
- package/src/plugins/api/routes/auth-routes.js +198 -30
- package/src/plugins/api/routes/resource-routes.js +19 -4
- package/src/plugins/api/server/health-manager.class.js +163 -0
- package/src/plugins/api/server/middleware-chain.class.js +310 -0
- package/src/plugins/api/server/router.class.js +472 -0
- package/src/plugins/api/server.js +280 -1303
- package/src/plugins/api/utils/custom-routes.js +17 -5
- package/src/plugins/api/utils/guards.js +76 -17
- package/src/plugins/api/utils/openapi-generator-cached.class.js +133 -0
- package/src/plugins/api/utils/openapi-generator.js +7 -6
- package/src/plugins/api/utils/template-engine.js +77 -3
- package/src/plugins/audit.plugin.js +30 -8
- package/src/plugins/backup.plugin.js +110 -14
- package/src/plugins/cache/cache.class.js +22 -5
- package/src/plugins/cache/filesystem-cache.class.js +116 -19
- package/src/plugins/cache/memory-cache.class.js +211 -57
- package/src/plugins/cache/multi-tier-cache.class.js +371 -0
- package/src/plugins/cache/partition-aware-filesystem-cache.class.js +168 -47
- package/src/plugins/cache/redis-cache.class.js +552 -0
- package/src/plugins/cache/s3-cache.class.js +17 -8
- package/src/plugins/cache.plugin.js +176 -61
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +60 -29
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/base-driver.js +16 -2
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +8 -1
- package/src/plugins/cloud-inventory/index.js +29 -8
- package/src/plugins/cloud-inventory/registry.js +64 -42
- package/src/plugins/cloud-inventory.plugin.js +240 -138
- package/src/plugins/concerns/plugin-dependencies.js +54 -0
- package/src/plugins/concerns/resource-names.js +100 -0
- package/src/plugins/consumers/index.js +10 -2
- package/src/plugins/consumers/sqs-consumer.js +12 -2
- package/src/plugins/cookie-farm-suite.plugin.js +278 -0
- package/src/plugins/cookie-farm.errors.js +73 -0
- package/src/plugins/cookie-farm.plugin.js +869 -0
- package/src/plugins/costs.plugin.js +7 -1
- package/src/plugins/eventual-consistency/analytics.js +94 -19
- package/src/plugins/eventual-consistency/config.js +15 -7
- package/src/plugins/eventual-consistency/consolidation.js +29 -11
- package/src/plugins/eventual-consistency/garbage-collection.js +3 -1
- package/src/plugins/eventual-consistency/helpers.js +39 -14
- package/src/plugins/eventual-consistency/install.js +21 -2
- package/src/plugins/eventual-consistency/utils.js +32 -10
- package/src/plugins/fulltext.plugin.js +38 -11
- package/src/plugins/geo.plugin.js +61 -9
- package/src/plugins/identity/concerns/config.js +61 -0
- package/src/plugins/identity/concerns/mfa-manager.js +15 -2
- package/src/plugins/identity/concerns/rate-limit.js +124 -0
- package/src/plugins/identity/concerns/resource-schemas.js +9 -1
- package/src/plugins/identity/concerns/token-generator.js +29 -4
- package/src/plugins/identity/drivers/auth-driver.interface.js +76 -0
- package/src/plugins/identity/drivers/client-credentials-driver.js +127 -0
- package/src/plugins/identity/drivers/index.js +18 -0
- package/src/plugins/identity/drivers/password-driver.js +122 -0
- package/src/plugins/identity/email-service.js +17 -2
- package/src/plugins/identity/index.js +413 -69
- package/src/plugins/identity/oauth2-server.js +413 -30
- package/src/plugins/identity/oidc-discovery.js +16 -8
- package/src/plugins/identity/rsa-keys.js +115 -35
- package/src/plugins/identity/server.js +166 -45
- package/src/plugins/identity/session-manager.js +53 -7
- package/src/plugins/identity/ui/pages/mfa-verification.js +17 -15
- package/src/plugins/identity/ui/routes.js +363 -255
- package/src/plugins/importer/index.js +153 -20
- package/src/plugins/index.js +9 -2
- package/src/plugins/kubernetes-inventory/index.js +6 -0
- package/src/plugins/kubernetes-inventory/k8s-driver.js +867 -0
- package/src/plugins/kubernetes-inventory/resource-types.js +274 -0
- package/src/plugins/kubernetes-inventory.plugin.js +980 -0
- package/src/plugins/metrics.plugin.js +64 -16
- package/src/plugins/ml/base-model.class.js +25 -15
- package/src/plugins/ml/regression-model.class.js +1 -1
- package/src/plugins/ml.errors.js +57 -25
- package/src/plugins/ml.plugin.js +28 -4
- package/src/plugins/namespace.js +210 -0
- package/src/plugins/plugin.class.js +180 -8
- package/src/plugins/puppeteer/console-monitor.js +729 -0
- package/src/plugins/puppeteer/cookie-manager.js +492 -0
- package/src/plugins/puppeteer/network-monitor.js +816 -0
- package/src/plugins/puppeteer/performance-manager.js +746 -0
- package/src/plugins/puppeteer/proxy-manager.js +478 -0
- package/src/plugins/puppeteer/stealth-manager.js +556 -0
- package/src/plugins/puppeteer.errors.js +81 -0
- package/src/plugins/puppeteer.plugin.js +1327 -0
- package/src/plugins/queue-consumer.plugin.js +69 -14
- package/src/plugins/recon/behaviors/uptime-behavior.js +691 -0
- package/src/plugins/recon/concerns/command-runner.js +148 -0
- package/src/plugins/recon/concerns/diff-detector.js +372 -0
- package/src/plugins/recon/concerns/fingerprint-builder.js +307 -0
- package/src/plugins/recon/concerns/process-manager.js +338 -0
- package/src/plugins/recon/concerns/report-generator.js +478 -0
- package/src/plugins/recon/concerns/security-analyzer.js +571 -0
- package/src/plugins/recon/concerns/target-normalizer.js +68 -0
- package/src/plugins/recon/config/defaults.js +321 -0
- package/src/plugins/recon/config/resources.js +370 -0
- package/src/plugins/recon/index.js +778 -0
- package/src/plugins/recon/managers/dependency-manager.js +174 -0
- package/src/plugins/recon/managers/scheduler-manager.js +179 -0
- package/src/plugins/recon/managers/storage-manager.js +745 -0
- package/src/plugins/recon/managers/target-manager.js +274 -0
- package/src/plugins/recon/stages/asn-stage.js +314 -0
- package/src/plugins/recon/stages/certificate-stage.js +84 -0
- package/src/plugins/recon/stages/dns-stage.js +107 -0
- package/src/plugins/recon/stages/dnsdumpster-stage.js +362 -0
- package/src/plugins/recon/stages/fingerprint-stage.js +71 -0
- package/src/plugins/recon/stages/google-dorks-stage.js +440 -0
- package/src/plugins/recon/stages/http-stage.js +89 -0
- package/src/plugins/recon/stages/latency-stage.js +148 -0
- package/src/plugins/recon/stages/massdns-stage.js +302 -0
- package/src/plugins/recon/stages/osint-stage.js +1373 -0
- package/src/plugins/recon/stages/ports-stage.js +169 -0
- package/src/plugins/recon/stages/screenshot-stage.js +94 -0
- package/src/plugins/recon/stages/secrets-stage.js +514 -0
- package/src/plugins/recon/stages/subdomains-stage.js +295 -0
- package/src/plugins/recon/stages/tls-audit-stage.js +78 -0
- package/src/plugins/recon/stages/vulnerability-stage.js +78 -0
- package/src/plugins/recon/stages/web-discovery-stage.js +113 -0
- package/src/plugins/recon/stages/whois-stage.js +349 -0
- package/src/plugins/recon.plugin.js +75 -0
- package/src/plugins/recon.plugin.js.backup +2635 -0
- package/src/plugins/relation.errors.js +87 -14
- package/src/plugins/replicator.plugin.js +514 -137
- package/src/plugins/replicators/base-replicator.class.js +89 -1
- package/src/plugins/replicators/bigquery-replicator.class.js +66 -22
- package/src/plugins/replicators/dynamodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mongodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mysql-replicator.class.js +52 -17
- package/src/plugins/replicators/planetscale-replicator.class.js +30 -4
- package/src/plugins/replicators/postgres-replicator.class.js +62 -27
- package/src/plugins/replicators/s3db-replicator.class.js +25 -18
- package/src/plugins/replicators/schema-sync.helper.js +3 -3
- package/src/plugins/replicators/sqs-replicator.class.js +8 -2
- package/src/plugins/replicators/turso-replicator.class.js +23 -3
- package/src/plugins/replicators/webhook-replicator.class.js +42 -4
- package/src/plugins/s3-queue.plugin.js +464 -65
- package/src/plugins/scheduler.plugin.js +20 -6
- package/src/plugins/state-machine.plugin.js +40 -9
- package/src/plugins/tfstate/README.md +126 -126
- package/src/plugins/tfstate/base-driver.js +28 -4
- package/src/plugins/tfstate/errors.js +65 -10
- package/src/plugins/tfstate/filesystem-driver.js +52 -8
- package/src/plugins/tfstate/index.js +163 -90
- package/src/plugins/tfstate/s3-driver.js +64 -6
- package/src/plugins/ttl.plugin.js +72 -17
- package/src/plugins/vector/distances.js +18 -12
- package/src/plugins/vector/kmeans.js +26 -4
- package/src/resource.class.js +115 -19
- package/src/testing/factory.class.js +20 -3
- package/src/testing/seeder.class.js +7 -1
- package/src/clients/memory-client.md +0 -917
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +0 -449
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConsoleMonitor - Console Message Tracking
|
|
3
|
+
*
|
|
4
|
+
* Captures all console messages from the browser:
|
|
5
|
+
* - Errors, warnings, info, debug, logs
|
|
6
|
+
* - Stack traces
|
|
7
|
+
* - Source location (file, line, column)
|
|
8
|
+
* - Uncaught exceptions
|
|
9
|
+
* - Promise rejections
|
|
10
|
+
* - Performance warnings
|
|
11
|
+
* - Deprecation warnings
|
|
12
|
+
*
|
|
13
|
+
* Use cases:
|
|
14
|
+
* - JavaScript error tracking
|
|
15
|
+
* - Debug production issues
|
|
16
|
+
* - Monitor third-party script errors
|
|
17
|
+
* - Track console.warn/console.error usage
|
|
18
|
+
* - Detect performance issues
|
|
19
|
+
* - Security monitoring (CSP violations)
|
|
20
|
+
*/
|
|
21
|
+
import tryFn from '../../concerns/try-fn.js';
|
|
22
|
+
import { PuppeteerError } from '../puppeteer.errors.js';
|
|
23
|
+
|
|
24
|
+
export class ConsoleMonitor {
|
|
25
|
+
constructor(plugin) {
|
|
26
|
+
this.plugin = plugin;
|
|
27
|
+
this.config = plugin.config.consoleMonitor || {
|
|
28
|
+
enabled: false,
|
|
29
|
+
persist: false,
|
|
30
|
+
filters: {
|
|
31
|
+
levels: null, // ['error', 'warning'] or null for all
|
|
32
|
+
excludePatterns: [], // Regex patterns to exclude
|
|
33
|
+
includeStackTraces: true,
|
|
34
|
+
includeSourceLocation: true,
|
|
35
|
+
captureNetwork: false // Also capture network errors from console
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Resources for persistence (lazy-initialized)
|
|
40
|
+
this.sessionsResource = null;
|
|
41
|
+
this.messagesResource = null;
|
|
42
|
+
this.errorsResource = null;
|
|
43
|
+
|
|
44
|
+
// Console message types
|
|
45
|
+
this.messageTypes = {
|
|
46
|
+
'log': 'log',
|
|
47
|
+
'debug': 'debug',
|
|
48
|
+
'info': 'info',
|
|
49
|
+
'error': 'error',
|
|
50
|
+
'warning': 'warning',
|
|
51
|
+
'warn': 'warning', // Alias
|
|
52
|
+
'dir': 'dir',
|
|
53
|
+
'dirxml': 'dirxml',
|
|
54
|
+
'table': 'table',
|
|
55
|
+
'trace': 'trace',
|
|
56
|
+
'clear': 'clear',
|
|
57
|
+
'startGroup': 'group',
|
|
58
|
+
'startGroupCollapsed': 'groupCollapsed',
|
|
59
|
+
'endGroup': 'groupEnd',
|
|
60
|
+
'assert': 'assert',
|
|
61
|
+
'profile': 'profile',
|
|
62
|
+
'profileEnd': 'profileEnd',
|
|
63
|
+
'count': 'count',
|
|
64
|
+
'timeEnd': 'timeEnd',
|
|
65
|
+
'verbose': 'verbose'
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Initialize console monitoring resources
|
|
71
|
+
*/
|
|
72
|
+
async initialize() {
|
|
73
|
+
if (!this.config.persist) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const resourceNames = this.plugin.resourceNames || {};
|
|
78
|
+
const sessionsName = resourceNames.consoleSessions || 'plg_puppeteer_console_sessions';
|
|
79
|
+
const messagesName = resourceNames.consoleMessages || 'plg_puppeteer_console_messages';
|
|
80
|
+
const errorsName = resourceNames.consoleErrors || 'plg_puppeteer_console_errors';
|
|
81
|
+
|
|
82
|
+
// Create sessions resource (metadata about each console session)
|
|
83
|
+
const [sessionsCreated, sessionsErr, sessionsResource] = await tryFn(() => this.plugin.database.createResource({
|
|
84
|
+
name: sessionsName,
|
|
85
|
+
attributes: {
|
|
86
|
+
sessionId: 'string|required',
|
|
87
|
+
url: 'string|required',
|
|
88
|
+
domain: 'string|required',
|
|
89
|
+
date: 'string|required', // YYYY-MM-DD
|
|
90
|
+
startTime: 'number|required',
|
|
91
|
+
endTime: 'number',
|
|
92
|
+
duration: 'number',
|
|
93
|
+
|
|
94
|
+
// Statistics
|
|
95
|
+
totalMessages: 'number',
|
|
96
|
+
errorCount: 'number',
|
|
97
|
+
warningCount: 'number',
|
|
98
|
+
logCount: 'number',
|
|
99
|
+
infoCount: 'number',
|
|
100
|
+
debugCount: 'number',
|
|
101
|
+
|
|
102
|
+
// By type breakdown
|
|
103
|
+
byType: 'object', // { error: 5, warning: 3, log: 20 }
|
|
104
|
+
|
|
105
|
+
// User agent
|
|
106
|
+
userAgent: 'string'
|
|
107
|
+
},
|
|
108
|
+
behavior: 'body-overflow',
|
|
109
|
+
timestamps: true,
|
|
110
|
+
partitions: {
|
|
111
|
+
byUrl: { fields: { url: 'string' } },
|
|
112
|
+
byDate: { fields: { date: 'string' } },
|
|
113
|
+
byDomain: { fields: { domain: 'string' } }
|
|
114
|
+
}
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
if (sessionsCreated) {
|
|
118
|
+
this.sessionsResource = sessionsResource;
|
|
119
|
+
} else if (this.plugin.database.resources?.[sessionsName]) {
|
|
120
|
+
this.sessionsResource = this.plugin.database.resources[sessionsName];
|
|
121
|
+
} else {
|
|
122
|
+
throw sessionsErr;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Create messages resource (all console messages)
|
|
126
|
+
const [messagesCreated, messagesErr, messagesResource] = await tryFn(() => this.plugin.database.createResource({
|
|
127
|
+
name: messagesName,
|
|
128
|
+
attributes: {
|
|
129
|
+
messageId: 'string|required',
|
|
130
|
+
sessionId: 'string|required',
|
|
131
|
+
timestamp: 'number|required',
|
|
132
|
+
date: 'string|required',
|
|
133
|
+
|
|
134
|
+
// Message details
|
|
135
|
+
type: 'string|required', // error, warning, log, info, debug, etc.
|
|
136
|
+
text: 'string|required',
|
|
137
|
+
args: 'array', // Console.log arguments
|
|
138
|
+
|
|
139
|
+
// Source location
|
|
140
|
+
source: 'object', // { url, lineNumber, columnNumber }
|
|
141
|
+
|
|
142
|
+
// Stack trace (for errors)
|
|
143
|
+
stackTrace: 'object',
|
|
144
|
+
|
|
145
|
+
// Context
|
|
146
|
+
url: 'string', // Page URL when message occurred
|
|
147
|
+
domain: 'string'
|
|
148
|
+
},
|
|
149
|
+
behavior: 'body-overflow',
|
|
150
|
+
timestamps: true,
|
|
151
|
+
partitions: {
|
|
152
|
+
bySession: { fields: { sessionId: 'string' } },
|
|
153
|
+
byType: { fields: { type: 'string' } },
|
|
154
|
+
byDate: { fields: { date: 'string' } },
|
|
155
|
+
byDomain: { fields: { domain: 'string' } }
|
|
156
|
+
}
|
|
157
|
+
}));
|
|
158
|
+
|
|
159
|
+
if (messagesCreated) {
|
|
160
|
+
this.messagesResource = messagesResource;
|
|
161
|
+
} else if (this.plugin.database.resources?.[messagesName]) {
|
|
162
|
+
this.messagesResource = this.plugin.database.resources[messagesName];
|
|
163
|
+
} else {
|
|
164
|
+
throw messagesErr;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Create errors resource (errors and exceptions only)
|
|
168
|
+
const [errorsCreated, errorsErr, errorsResource] = await tryFn(() => this.plugin.database.createResource({
|
|
169
|
+
name: errorsName,
|
|
170
|
+
attributes: {
|
|
171
|
+
errorId: 'string|required',
|
|
172
|
+
sessionId: 'string|required',
|
|
173
|
+
messageId: 'string|required',
|
|
174
|
+
timestamp: 'number|required',
|
|
175
|
+
date: 'string|required',
|
|
176
|
+
|
|
177
|
+
// Error details
|
|
178
|
+
errorType: 'string', // TypeError, ReferenceError, etc.
|
|
179
|
+
message: 'string|required',
|
|
180
|
+
stackTrace: 'object',
|
|
181
|
+
|
|
182
|
+
// Source location
|
|
183
|
+
url: 'string', // Script URL
|
|
184
|
+
lineNumber: 'number',
|
|
185
|
+
columnNumber: 'number',
|
|
186
|
+
|
|
187
|
+
// Context
|
|
188
|
+
pageUrl: 'string', // Page URL when error occurred
|
|
189
|
+
domain: 'string',
|
|
190
|
+
|
|
191
|
+
// Classification
|
|
192
|
+
isUncaught: 'boolean',
|
|
193
|
+
isPromiseRejection: 'boolean',
|
|
194
|
+
isNetworkError: 'boolean',
|
|
195
|
+
isSyntaxError: 'boolean'
|
|
196
|
+
},
|
|
197
|
+
behavior: 'body-overflow',
|
|
198
|
+
timestamps: true,
|
|
199
|
+
partitions: {
|
|
200
|
+
bySession: { fields: { sessionId: 'string' } },
|
|
201
|
+
byErrorType: { fields: { errorType: 'string' } },
|
|
202
|
+
byDate: { fields: { date: 'string' } },
|
|
203
|
+
byDomain: { fields: { domain: 'string' } }
|
|
204
|
+
}
|
|
205
|
+
}));
|
|
206
|
+
|
|
207
|
+
if (errorsCreated) {
|
|
208
|
+
this.errorsResource = errorsResource;
|
|
209
|
+
} else if (this.plugin.database.resources?.[errorsName]) {
|
|
210
|
+
this.errorsResource = this.plugin.database.resources[errorsName];
|
|
211
|
+
} else {
|
|
212
|
+
throw errorsErr;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.plugin.emit('consoleMonitor.initialized', {
|
|
216
|
+
persist: this.config.persist
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Start monitoring console messages for a page
|
|
222
|
+
* @param {Page} page - Puppeteer page
|
|
223
|
+
* @param {Object} options - Monitoring options
|
|
224
|
+
* @returns {Object} Session object
|
|
225
|
+
*/
|
|
226
|
+
async startMonitoring(page, options = {}) {
|
|
227
|
+
const {
|
|
228
|
+
sessionId = `console_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
229
|
+
persist = this.config.persist,
|
|
230
|
+
filters = this.config.filters
|
|
231
|
+
} = options;
|
|
232
|
+
|
|
233
|
+
const session = {
|
|
234
|
+
sessionId,
|
|
235
|
+
url: page.url(),
|
|
236
|
+
domain: this._extractDomain(page.url()),
|
|
237
|
+
date: new Date().toISOString().split('T')[0],
|
|
238
|
+
startTime: Date.now(),
|
|
239
|
+
endTime: null,
|
|
240
|
+
duration: null,
|
|
241
|
+
|
|
242
|
+
// Tracked data
|
|
243
|
+
messages: [],
|
|
244
|
+
errors: [],
|
|
245
|
+
exceptions: [],
|
|
246
|
+
promiseRejections: [],
|
|
247
|
+
|
|
248
|
+
// Statistics
|
|
249
|
+
stats: {
|
|
250
|
+
totalMessages: 0,
|
|
251
|
+
errorCount: 0,
|
|
252
|
+
warningCount: 0,
|
|
253
|
+
logCount: 0,
|
|
254
|
+
infoCount: 0,
|
|
255
|
+
debugCount: 0,
|
|
256
|
+
byType: {}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// Console message handler
|
|
261
|
+
const consoleHandler = (msg) => {
|
|
262
|
+
const type = this.messageTypes[msg.type()] || msg.type();
|
|
263
|
+
|
|
264
|
+
// Apply filters
|
|
265
|
+
if (filters.levels && !filters.levels.includes(type)) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const text = msg.text();
|
|
270
|
+
|
|
271
|
+
// Exclude patterns
|
|
272
|
+
if (filters.excludePatterns && filters.excludePatterns.length > 0) {
|
|
273
|
+
for (const pattern of filters.excludePatterns) {
|
|
274
|
+
if (new RegExp(pattern).test(text)) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const message = {
|
|
281
|
+
messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
282
|
+
timestamp: Date.now(),
|
|
283
|
+
type,
|
|
284
|
+
text,
|
|
285
|
+
args: msg.args().length > 0 ? msg.args().map(arg => this._serializeArg(arg)) : [],
|
|
286
|
+
url: page.url()
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Get source location if enabled
|
|
290
|
+
if (filters.includeSourceLocation && msg.location()) {
|
|
291
|
+
message.source = {
|
|
292
|
+
url: msg.location().url,
|
|
293
|
+
lineNumber: msg.location().lineNumber,
|
|
294
|
+
columnNumber: msg.location().columnNumber
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Get stack trace for errors if enabled
|
|
299
|
+
if (filters.includeStackTraces && (type === 'error' || type === 'warning')) {
|
|
300
|
+
message.stackTrace = msg.stackTrace();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
session.messages.push(message);
|
|
304
|
+
session.stats.totalMessages++;
|
|
305
|
+
|
|
306
|
+
// Update type counters
|
|
307
|
+
if (!session.stats.byType[type]) {
|
|
308
|
+
session.stats.byType[type] = 0;
|
|
309
|
+
}
|
|
310
|
+
session.stats.byType[type]++;
|
|
311
|
+
|
|
312
|
+
// Update specific counters
|
|
313
|
+
switch (type) {
|
|
314
|
+
case 'error':
|
|
315
|
+
session.stats.errorCount++;
|
|
316
|
+
session.errors.push(message);
|
|
317
|
+
break;
|
|
318
|
+
case 'warning':
|
|
319
|
+
session.stats.warningCount++;
|
|
320
|
+
break;
|
|
321
|
+
case 'log':
|
|
322
|
+
session.stats.logCount++;
|
|
323
|
+
break;
|
|
324
|
+
case 'info':
|
|
325
|
+
session.stats.infoCount++;
|
|
326
|
+
break;
|
|
327
|
+
case 'debug':
|
|
328
|
+
session.stats.debugCount++;
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// Page error handler (uncaught exceptions)
|
|
334
|
+
const pageErrorHandler = (error) => {
|
|
335
|
+
const errorData = {
|
|
336
|
+
errorId: `error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
337
|
+
timestamp: Date.now(),
|
|
338
|
+
errorType: error.name || 'Error',
|
|
339
|
+
message: error.message,
|
|
340
|
+
stack: error.stack,
|
|
341
|
+
isUncaught: true,
|
|
342
|
+
url: page.url()
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
session.exceptions.push(errorData);
|
|
346
|
+
session.stats.errorCount++;
|
|
347
|
+
|
|
348
|
+
// Also add to messages for unified view
|
|
349
|
+
session.messages.push({
|
|
350
|
+
messageId: errorData.errorId,
|
|
351
|
+
timestamp: errorData.timestamp,
|
|
352
|
+
type: 'error',
|
|
353
|
+
text: `Uncaught: ${error.message}`,
|
|
354
|
+
stackTrace: error.stack,
|
|
355
|
+
url: page.url()
|
|
356
|
+
});
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// Promise rejection handler
|
|
360
|
+
const pageerrorHandler = (error) => {
|
|
361
|
+
const errorData = {
|
|
362
|
+
errorId: `rejection_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
363
|
+
timestamp: Date.now(),
|
|
364
|
+
errorType: 'UnhandledPromiseRejection',
|
|
365
|
+
message: error.message || String(error),
|
|
366
|
+
stack: error.stack,
|
|
367
|
+
isPromiseRejection: true,
|
|
368
|
+
url: page.url()
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
session.promiseRejections.push(errorData);
|
|
372
|
+
session.stats.errorCount++;
|
|
373
|
+
|
|
374
|
+
// Also add to messages
|
|
375
|
+
session.messages.push({
|
|
376
|
+
messageId: errorData.errorId,
|
|
377
|
+
timestamp: errorData.timestamp,
|
|
378
|
+
type: 'error',
|
|
379
|
+
text: `Unhandled Promise Rejection: ${errorData.message}`,
|
|
380
|
+
stackTrace: error.stack,
|
|
381
|
+
url: page.url()
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// Attach handlers
|
|
386
|
+
page.on('console', consoleHandler);
|
|
387
|
+
page.on('pageerror', pageErrorHandler);
|
|
388
|
+
page.on('pageerror', pageerrorHandler); // Some versions use different event
|
|
389
|
+
|
|
390
|
+
// Store handlers for cleanup
|
|
391
|
+
session._handlers = {
|
|
392
|
+
console: consoleHandler,
|
|
393
|
+
pageerror: pageErrorHandler,
|
|
394
|
+
pageerror2: pageerrorHandler
|
|
395
|
+
};
|
|
396
|
+
session._page = page;
|
|
397
|
+
session._persist = persist;
|
|
398
|
+
|
|
399
|
+
this.plugin.emit('consoleMonitor.sessionStarted', {
|
|
400
|
+
sessionId,
|
|
401
|
+
url: page.url()
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return session;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Stop monitoring and optionally persist data
|
|
409
|
+
* @param {Object} session - Session object from startMonitoring
|
|
410
|
+
* @param {Object} options - Stop options
|
|
411
|
+
* @returns {Object} Final session data
|
|
412
|
+
*/
|
|
413
|
+
async stopMonitoring(session, options = {}) {
|
|
414
|
+
const { persist = session._persist } = options;
|
|
415
|
+
|
|
416
|
+
session.endTime = Date.now();
|
|
417
|
+
session.duration = session.endTime - session.startTime;
|
|
418
|
+
|
|
419
|
+
// Remove handlers
|
|
420
|
+
if (session._page && session._handlers) {
|
|
421
|
+
session._page.off('console', session._handlers.console);
|
|
422
|
+
session._page.off('pageerror', session._handlers.pageerror);
|
|
423
|
+
session._page.off('pageerror', session._handlers.pageerror2);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Persist to S3DB if enabled
|
|
427
|
+
if (persist && this.sessionsResource) {
|
|
428
|
+
try {
|
|
429
|
+
await this._persistSession(session);
|
|
430
|
+
} catch (err) {
|
|
431
|
+
this.plugin.emit('consoleMonitor.persistFailed', {
|
|
432
|
+
sessionId: session.sessionId,
|
|
433
|
+
error: err.message
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
this.plugin.emit('consoleMonitor.sessionStopped', {
|
|
439
|
+
sessionId: session.sessionId,
|
|
440
|
+
duration: session.duration,
|
|
441
|
+
totalMessages: session.stats.totalMessages,
|
|
442
|
+
errorCount: session.stats.errorCount
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// Clean up references
|
|
446
|
+
delete session._handlers;
|
|
447
|
+
delete session._page;
|
|
448
|
+
|
|
449
|
+
return session;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Persist session data to S3DB
|
|
454
|
+
* @private
|
|
455
|
+
*/
|
|
456
|
+
async _persistSession(session) {
|
|
457
|
+
const startPersist = Date.now();
|
|
458
|
+
|
|
459
|
+
// Save session metadata
|
|
460
|
+
await this.sessionsResource.insert({
|
|
461
|
+
sessionId: session.sessionId,
|
|
462
|
+
url: session.url,
|
|
463
|
+
domain: session.domain,
|
|
464
|
+
date: session.date,
|
|
465
|
+
startTime: session.startTime,
|
|
466
|
+
endTime: session.endTime,
|
|
467
|
+
duration: session.duration,
|
|
468
|
+
totalMessages: session.stats.totalMessages,
|
|
469
|
+
errorCount: session.stats.errorCount,
|
|
470
|
+
warningCount: session.stats.warningCount,
|
|
471
|
+
logCount: session.stats.logCount,
|
|
472
|
+
infoCount: session.stats.infoCount,
|
|
473
|
+
debugCount: session.stats.debugCount,
|
|
474
|
+
byType: session.stats.byType,
|
|
475
|
+
userAgent: session._page?._userAgent || null
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Save messages (batch)
|
|
479
|
+
if (session.messages.length > 0) {
|
|
480
|
+
for (const msg of session.messages) {
|
|
481
|
+
await this.messagesResource.insert({
|
|
482
|
+
messageId: msg.messageId,
|
|
483
|
+
sessionId: session.sessionId,
|
|
484
|
+
timestamp: msg.timestamp,
|
|
485
|
+
date: session.date,
|
|
486
|
+
type: msg.type,
|
|
487
|
+
text: msg.text,
|
|
488
|
+
args: msg.args,
|
|
489
|
+
source: msg.source || null,
|
|
490
|
+
stackTrace: msg.stackTrace || null,
|
|
491
|
+
url: msg.url,
|
|
492
|
+
domain: this._extractDomain(msg.url)
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Save errors separately (uncaught exceptions + promise rejections)
|
|
498
|
+
const allErrors = [
|
|
499
|
+
...session.exceptions.map(e => ({ ...e, isUncaught: true })),
|
|
500
|
+
...session.promiseRejections.map(e => ({ ...e, isPromiseRejection: true }))
|
|
501
|
+
];
|
|
502
|
+
|
|
503
|
+
if (allErrors.length > 0) {
|
|
504
|
+
for (const error of allErrors) {
|
|
505
|
+
// Parse error type from message or use generic
|
|
506
|
+
const errorType = this._extractErrorType(error.message);
|
|
507
|
+
|
|
508
|
+
// Parse source location from stack if available
|
|
509
|
+
const sourceLocation = this._parseStackTrace(error.stack);
|
|
510
|
+
|
|
511
|
+
await this.errorsResource.insert({
|
|
512
|
+
errorId: error.errorId,
|
|
513
|
+
sessionId: session.sessionId,
|
|
514
|
+
messageId: error.errorId,
|
|
515
|
+
timestamp: error.timestamp,
|
|
516
|
+
date: session.date,
|
|
517
|
+
errorType: error.errorType || errorType,
|
|
518
|
+
message: error.message,
|
|
519
|
+
stackTrace: this._formatStackTrace(error.stack),
|
|
520
|
+
url: sourceLocation?.url || null,
|
|
521
|
+
lineNumber: sourceLocation?.lineNumber || null,
|
|
522
|
+
columnNumber: sourceLocation?.columnNumber || null,
|
|
523
|
+
pageUrl: error.url,
|
|
524
|
+
domain: this._extractDomain(error.url),
|
|
525
|
+
isUncaught: error.isUncaught || false,
|
|
526
|
+
isPromiseRejection: error.isPromiseRejection || false,
|
|
527
|
+
isNetworkError: this._isNetworkError(error.message),
|
|
528
|
+
isSyntaxError: errorType === 'SyntaxError'
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const persistDuration = Date.now() - startPersist;
|
|
534
|
+
|
|
535
|
+
this.plugin.emit('consoleMonitor.persisted', {
|
|
536
|
+
sessionId: session.sessionId,
|
|
537
|
+
messages: session.messages.length,
|
|
538
|
+
errors: allErrors.length,
|
|
539
|
+
duration: persistDuration
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Extract domain from URL
|
|
545
|
+
* @private
|
|
546
|
+
*/
|
|
547
|
+
_extractDomain(url) {
|
|
548
|
+
try {
|
|
549
|
+
return new URL(url).hostname;
|
|
550
|
+
} catch {
|
|
551
|
+
return 'unknown';
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Serialize console.log argument
|
|
557
|
+
* @private
|
|
558
|
+
*/
|
|
559
|
+
_serializeArg(arg) {
|
|
560
|
+
try {
|
|
561
|
+
return arg.toString();
|
|
562
|
+
} catch {
|
|
563
|
+
return '[Object]';
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Extract error type from message
|
|
569
|
+
* @private
|
|
570
|
+
*/
|
|
571
|
+
_extractErrorType(message) {
|
|
572
|
+
const errorTypes = [
|
|
573
|
+
'TypeError',
|
|
574
|
+
'ReferenceError',
|
|
575
|
+
'SyntaxError',
|
|
576
|
+
'RangeError',
|
|
577
|
+
'URIError',
|
|
578
|
+
'EvalError',
|
|
579
|
+
'SecurityError',
|
|
580
|
+
'NetworkError'
|
|
581
|
+
];
|
|
582
|
+
|
|
583
|
+
for (const type of errorTypes) {
|
|
584
|
+
if (message.includes(type)) {
|
|
585
|
+
return type;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return 'Error';
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Parse stack trace to extract source location
|
|
594
|
+
* @private
|
|
595
|
+
*/
|
|
596
|
+
_parseStackTrace(stack) {
|
|
597
|
+
if (!stack) return null;
|
|
598
|
+
|
|
599
|
+
try {
|
|
600
|
+
// Parse first line of stack trace
|
|
601
|
+
const lines = stack.split('\n');
|
|
602
|
+
const firstLine = lines[0] || '';
|
|
603
|
+
|
|
604
|
+
// Match various stack trace formats
|
|
605
|
+
const match = firstLine.match(/at\s+(.+?):(\d+):(\d+)/) ||
|
|
606
|
+
firstLine.match(/(.+?):(\d+):(\d+)/) ||
|
|
607
|
+
firstLine.match(/@(.+?):(\d+):(\d+)/);
|
|
608
|
+
|
|
609
|
+
if (match) {
|
|
610
|
+
return {
|
|
611
|
+
url: match[1],
|
|
612
|
+
lineNumber: parseInt(match[2], 10),
|
|
613
|
+
columnNumber: parseInt(match[3], 10)
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
} catch {
|
|
617
|
+
// Ignore parse errors
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Format stack trace for storage
|
|
625
|
+
* @private
|
|
626
|
+
*/
|
|
627
|
+
_formatStackTrace(stack) {
|
|
628
|
+
if (!stack) return null;
|
|
629
|
+
|
|
630
|
+
try {
|
|
631
|
+
const lines = stack.split('\n').slice(0, 10); // Keep first 10 lines
|
|
632
|
+
return {
|
|
633
|
+
raw: stack.substring(0, 2000), // Limit to 2KB
|
|
634
|
+
frames: lines.map(line => line.trim())
|
|
635
|
+
};
|
|
636
|
+
} catch {
|
|
637
|
+
return { raw: stack.substring(0, 2000) };
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Check if error is network-related
|
|
643
|
+
* @private
|
|
644
|
+
*/
|
|
645
|
+
_isNetworkError(message) {
|
|
646
|
+
const networkKeywords = [
|
|
647
|
+
'net::ERR_',
|
|
648
|
+
'NetworkError',
|
|
649
|
+
'Failed to fetch',
|
|
650
|
+
'fetch failed',
|
|
651
|
+
'XMLHttpRequest',
|
|
652
|
+
'CORS',
|
|
653
|
+
'Network request failed'
|
|
654
|
+
];
|
|
655
|
+
|
|
656
|
+
return networkKeywords.some(keyword => message.includes(keyword));
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Get session statistics
|
|
661
|
+
* @param {string} sessionId - Session ID
|
|
662
|
+
* @returns {Promise<Object>} Statistics
|
|
663
|
+
*/
|
|
664
|
+
async getSessionStats(sessionId) {
|
|
665
|
+
if (!this.sessionsResource) {
|
|
666
|
+
throw new PuppeteerError('Console monitoring persistence not enabled', {
|
|
667
|
+
operation: 'getSessionStats',
|
|
668
|
+
retriable: false,
|
|
669
|
+
suggestion: 'Enable persistence in ConsoleMonitor configuration to query stored sessions.'
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return await this.sessionsResource.get(sessionId);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Query messages for a session
|
|
678
|
+
* @param {string} sessionId - Session ID
|
|
679
|
+
* @param {Object} filters - Query filters
|
|
680
|
+
* @returns {Promise<Array>} Messages
|
|
681
|
+
*/
|
|
682
|
+
async getSessionMessages(sessionId, filters = {}) {
|
|
683
|
+
if (!this.messagesResource) {
|
|
684
|
+
throw new PuppeteerError('Console monitoring persistence not enabled', {
|
|
685
|
+
operation: 'getSessionMessages',
|
|
686
|
+
retriable: false,
|
|
687
|
+
suggestion: 'Enable persistence in ConsoleMonitor configuration to query stored messages.'
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return await this.messagesResource.listPartition('bySession', { sessionId }, filters);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Query errors for a session
|
|
696
|
+
* @param {string} sessionId - Session ID
|
|
697
|
+
* @returns {Promise<Array>} Errors
|
|
698
|
+
*/
|
|
699
|
+
async getSessionErrors(sessionId) {
|
|
700
|
+
if (!this.errorsResource) {
|
|
701
|
+
throw new PuppeteerError('Console monitoring persistence not enabled', {
|
|
702
|
+
operation: 'getSessionErrors',
|
|
703
|
+
retriable: false,
|
|
704
|
+
suggestion: 'Enable persistence in ConsoleMonitor configuration to query stored errors.'
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return await this.errorsResource.listPartition('bySession', { sessionId });
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Query all errors by type
|
|
713
|
+
* @param {string} errorType - Error type
|
|
714
|
+
* @returns {Promise<Array>} Errors
|
|
715
|
+
*/
|
|
716
|
+
async getErrorsByType(errorType) {
|
|
717
|
+
if (!this.errorsResource) {
|
|
718
|
+
throw new PuppeteerError('Console monitoring persistence not enabled', {
|
|
719
|
+
operation: 'getErrorsByType',
|
|
720
|
+
retriable: false,
|
|
721
|
+
suggestion: 'Enable persistence in ConsoleMonitor configuration to query stored errors.'
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return await this.errorsResource.listPartition('byErrorType', { errorType });
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
export default ConsoleMonitor;
|