@private.me/xbind 3.0.0 → 3.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 +55 -7
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1920 -1
- package/dist-standalone/_deps/shared/cjs/errors.js +729 -1
- package/dist-standalone/_deps/shared/cjs/index.js +463 -1
- package/dist-standalone/_deps/shared/cjs/types.js +315 -1
- package/dist-standalone/_deps/shared/errors.js +244 -1
- package/dist-standalone/_deps/shared/index.js +72 -1
- package/dist-standalone/_deps/shared/types.js +86 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
- package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/search.js +1 -1
- package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
- package/dist-standalone/_deps/xchange/errors.js +1 -1
- package/dist-standalone/_deps/xchange/index.js +1 -1
- package/dist-standalone/_deps/xchange/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/xchange.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
- package/dist-standalone/_deps/xregistry/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/index.js +1 -1
- package/dist-standalone/_deps/xregistry/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/types.js +1 -1
- package/dist-standalone/agent-call.js +659 -1
- package/dist-standalone/agent-sdk.js +328 -1
- package/dist-standalone/agent.js +1800 -1
- package/dist-standalone/approval.js +193 -1
- package/dist-standalone/async-iterators.js +382 -1
- package/dist-standalone/auth.js +219 -1
- package/dist-standalone/auto-accept.js +229 -1
- package/dist-standalone/backup-config.js +201 -1
- package/dist-standalone/backup.js +326 -1
- package/dist-standalone/batch-operations.js +388 -1
- package/dist-standalone/cancellation.js +477 -1
- package/dist-standalone/checkpoint.js +186 -1
- package/dist-standalone/circuit-breaker.js +468 -1
- package/dist-standalone/cjs/agent-call.js +701 -1
- package/dist-standalone/cjs/agent-sdk.js +332 -1
- package/dist-standalone/cjs/agent.js +1837 -1
- package/dist-standalone/cjs/approval.js +199 -1
- package/dist-standalone/cjs/async-iterators.js +392 -1
- package/dist-standalone/cjs/auth.js +225 -1
- package/dist-standalone/cjs/auto-accept.js +233 -1
- package/dist-standalone/cjs/backup-config.js +207 -1
- package/dist-standalone/cjs/backup.js +330 -1
- package/dist-standalone/cjs/batch-operations.js +397 -1
- package/dist-standalone/cjs/cancellation.js +490 -1
- package/dist-standalone/cjs/checkpoint.js +193 -1
- package/dist-standalone/cjs/circuit-breaker.js +476 -1
- package/dist-standalone/cjs/cli/init.js +492 -1
- package/dist-standalone/cjs/config-validation.js +522 -1
- package/dist-standalone/cjs/connect.js +312 -1
- package/dist-standalone/cjs/connection-pool.js +506 -1
- package/dist-standalone/cjs/correlation-id.js +339 -1
- package/dist-standalone/cjs/crypto-utils.js +176 -1
- package/dist-standalone/cjs/debug-mode.js +534 -1
- package/dist-standalone/cjs/did-document.js +101 -1
- package/dist-standalone/cjs/did-privateme.js +130 -1
- package/dist-standalone/cjs/did-web.js +201 -1
- package/dist-standalone/cjs/discovery.js +462 -1
- package/dist-standalone/cjs/dual-mode.js +251 -1
- package/dist-standalone/cjs/email-templates.js +313 -1
- package/dist-standalone/cjs/email-transport.js +239 -1
- package/dist-standalone/cjs/envelope.js +538 -1
- package/dist-standalone/cjs/errors.js +913 -1
- package/dist-standalone/cjs/event-emitter.js +461 -1
- package/dist-standalone/cjs/gateway-state.js +55 -1
- package/dist-standalone/cjs/gateway-transport.js +120 -1
- package/dist-standalone/cjs/graceful-degradation.js +403 -1
- package/dist-standalone/cjs/guardrails.js +223 -1
- package/dist-standalone/cjs/health-check.js +336 -1
- package/dist-standalone/cjs/http-compat.js +272 -1
- package/dist-standalone/cjs/http-status-map.js +571 -1
- package/dist-standalone/cjs/identity.js +645 -1
- package/dist-standalone/cjs/index.js +406 -1
- package/dist-standalone/cjs/invitation.js +421 -1
- package/dist-standalone/cjs/invite.js +328 -1
- package/dist-standalone/cjs/key-agreement.js +335 -1
- package/dist-standalone/cjs/lazy-init.js +300 -1
- package/dist-standalone/cjs/logger.js +291 -1
- package/dist-standalone/cjs/mdns-discovery.js +202 -1
- package/dist-standalone/cjs/nonce-store.js +80 -1
- package/dist-standalone/cjs/pairing-manager.js +223 -1
- package/dist-standalone/cjs/plugin-system.js +264 -1
- package/dist-standalone/cjs/plugins/logging.js +168 -1
- package/dist-standalone/cjs/plugins/metrics.js +181 -1
- package/dist-standalone/cjs/plugins/validation.js +302 -1
- package/dist-standalone/cjs/policy.js +320 -1
- package/dist-standalone/cjs/progress-callbacks.js +583 -1
- package/dist-standalone/cjs/redis-nonce-store.js +76 -1
- package/dist-standalone/cjs/registry-middleware.js +50 -1
- package/dist-standalone/cjs/retry-strategies.js +544 -1
- package/dist-standalone/cjs/retry-transport.js +102 -1
- package/dist-standalone/cjs/runtime/browser.js +533 -1
- package/dist-standalone/cjs/runtime/edge.js +526 -1
- package/dist-standalone/cjs/runtime/react-native.js +394 -1
- package/dist-standalone/cjs/security-policy.js +245 -1
- package/dist-standalone/cjs/serialization.js +1040 -1
- package/dist-standalone/cjs/split-channel.js +225 -1
- package/dist-standalone/cjs/subscription-proof.js +230 -1
- package/dist-standalone/cjs/succession.js +148 -1
- package/dist-standalone/cjs/timeouts.js +412 -1
- package/dist-standalone/cjs/trace-context.js +424 -1
- package/dist-standalone/cjs/trace-spans.js +495 -1
- package/dist-standalone/cjs/transport.js +63 -1
- package/dist-standalone/cjs/trust-registry.js +991 -1
- package/dist-standalone/cjs/types/error-response.js +56 -1
- package/dist-standalone/cjs/vault-auth.js +178 -1
- package/dist-standalone/cjs/vault-store-loader.js +194 -1
- package/dist-standalone/cjs/verify.js +25 -1
- package/dist-standalone/cjs/version-info.js +543 -1
- package/dist-standalone/cjs/xfetch.js +340 -1
- package/dist-standalone/cli/init.js +455 -1
- package/dist-standalone/cli/setup.js +514 -1
- package/dist-standalone/cli/types.js +27 -1
- package/dist-standalone/cli/xbind.js +148 -1
- package/dist-standalone/config-validation.js +513 -1
- package/dist-standalone/connect.js +274 -1
- package/dist-standalone/connection-pool.js +500 -1
- package/dist-standalone/correlation-id.js +326 -1
- package/dist-standalone/crypto-utils.js +157 -1
- package/dist-standalone/debug-mode.js +510 -1
- package/dist-standalone/did-document.js +96 -1
- package/dist-standalone/did-privateme.js +121 -1
- package/dist-standalone/did-web.js +196 -1
- package/dist-standalone/discovery.js +458 -1
- package/dist-standalone/dual-mode.js +247 -1
- package/dist-standalone/email-templates.js +309 -1
- package/dist-standalone/email-transport.js +232 -1
- package/dist-standalone/envelope.js +525 -1
- package/dist-standalone/errors.js +896 -1
- package/dist-standalone/event-emitter.js +456 -1
- package/dist-standalone/gateway-state.js +51 -1
- package/dist-standalone/gateway-transport.js +116 -1
- package/dist-standalone/graceful-degradation.js +396 -1
- package/dist-standalone/guardrails.js +216 -1
- package/dist-standalone/health-check.js +332 -1
- package/dist-standalone/http-compat.js +267 -1
- package/dist-standalone/http-status-map.js +561 -1
- package/dist-standalone/identity.js +619 -1
- package/dist-standalone/index.js +78 -1
- package/dist-standalone/invitation.js +415 -1
- package/dist-standalone/invite.js +324 -1
- package/dist-standalone/key-agreement.js +325 -1
- package/dist-standalone/lazy-init.js +295 -1
- package/dist-standalone/logger.js +285 -1
- package/dist-standalone/mdns-discovery.js +195 -1
- package/dist-standalone/nonce-store.js +76 -1
- package/dist-standalone/pairing-manager.js +219 -1
- package/dist-standalone/plugin-system.js +257 -1
- package/dist-standalone/plugins/logging.d.ts +84 -0
- package/dist-standalone/plugins/logging.js +163 -0
- package/dist-standalone/plugins/metrics.d.ts +111 -0
- package/dist-standalone/plugins/metrics.js +176 -0
- package/dist-standalone/plugins/validation.d.ts +104 -0
- package/dist-standalone/plugins/validation.js +297 -0
- package/dist-standalone/policy.js +315 -1
- package/dist-standalone/progress-callbacks.js +576 -1
- package/dist-standalone/redis-nonce-store.js +72 -1
- package/dist-standalone/registry-middleware.js +47 -1
- package/dist-standalone/retry-strategies.js +534 -1
- package/dist-standalone/retry-transport.js +98 -1
- package/dist-standalone/runtime/browser.d.ts +311 -0
- package/dist-standalone/runtime/browser.js +516 -0
- package/dist-standalone/runtime/edge.d.ts +282 -0
- package/dist-standalone/runtime/edge.js +511 -0
- package/dist-standalone/runtime/react-native.d.ts +157 -0
- package/dist-standalone/runtime/react-native.js +383 -0
- package/dist-standalone/security-policy.js +239 -1
- package/dist-standalone/serialization.js +1031 -1
- package/dist-standalone/split-channel.js +219 -1
- package/dist-standalone/subscription-proof.js +224 -1
- package/dist-standalone/succession.js +142 -1
- package/dist-standalone/timeouts.js +398 -1
- package/dist-standalone/trace-context.js +414 -1
- package/dist-standalone/trace-spans.js +488 -1
- package/dist-standalone/transport.js +59 -1
- package/dist-standalone/trust-registry.js +950 -1
- package/dist-standalone/types/error-response.d.ts +209 -0
- package/dist-standalone/types/error-response.js +52 -0
- package/dist-standalone/vault-auth.js +174 -1
- package/dist-standalone/vault-store-loader.js +187 -1
- package/dist-standalone/verify.js +16 -1
- package/dist-standalone/version-info.js +530 -1
- package/dist-standalone/xfetch.js +335 -1
- package/package.json +4 -10
- package/share1.dat +0 -0
- package/dist-standalone/_deps/mldsa-wasm/LICENSE +0 -24
- package/dist-standalone/_deps/mldsa-wasm/package.json +0 -46
- package/dist-standalone/_deps/shared/cjs/package.json +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/package.json +0 -1
- package/dist-standalone/_deps/xchange/cjs/package.json +0 -1
- package/dist-standalone/_deps/xregistry/cjs/package.json +0 -1
- package/dist-standalone/cjs/package.json +0 -3
- package/dist-standalone/package.json +0 -10
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module plugins/metrics
|
|
3
|
+
* Metrics plugin for xBind plugin system
|
|
4
|
+
*
|
|
5
|
+
* Tracks performance metrics, message counts, and error rates.
|
|
6
|
+
*/
|
|
7
|
+
import { ok } from"../_deps/shared/index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Metrics plugin implementation.
|
|
10
|
+
* Tracks performance and usage statistics for all operations.
|
|
11
|
+
*/
|
|
12
|
+
export class MetricsPlugin {
|
|
13
|
+
name = 'metrics';
|
|
14
|
+
version = '1.0.0';
|
|
15
|
+
description = 'Tracks performance metrics, message counts, and error rates';
|
|
16
|
+
priority = 20; // Execute after logging
|
|
17
|
+
options;
|
|
18
|
+
metrics = [];
|
|
19
|
+
startTime = Date.now();
|
|
20
|
+
exportTimer;
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.options = {
|
|
23
|
+
highResolution: options.highResolution ?? true,
|
|
24
|
+
maxMetrics: options.maxMetrics ?? 10000,
|
|
25
|
+
onExport: options.onExport ?? (() => { }),
|
|
26
|
+
exportInterval: options.exportInterval ?? 60000,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
onInit() {
|
|
30
|
+
this.startTime = Date.now();
|
|
31
|
+
if (this.options.exportInterval > 0) {
|
|
32
|
+
this.exportTimer = setInterval(() => {
|
|
33
|
+
this.export();
|
|
34
|
+
}, this.options.exportInterval);
|
|
35
|
+
}
|
|
36
|
+
return ok(undefined);
|
|
37
|
+
}
|
|
38
|
+
onDestroy() {
|
|
39
|
+
if (this.exportTimer) {
|
|
40
|
+
clearInterval(this.exportTimer);
|
|
41
|
+
this.exportTimer = undefined;
|
|
42
|
+
}
|
|
43
|
+
this.export();
|
|
44
|
+
this.metrics = [];
|
|
45
|
+
return ok(undefined);
|
|
46
|
+
}
|
|
47
|
+
onSend(payload, context) {
|
|
48
|
+
const start = this.now();
|
|
49
|
+
// Simulate processing
|
|
50
|
+
const end = this.now();
|
|
51
|
+
this.record({
|
|
52
|
+
timestamp: Date.now(),
|
|
53
|
+
event: 'send',
|
|
54
|
+
duration: end - start,
|
|
55
|
+
success: true,
|
|
56
|
+
});
|
|
57
|
+
return ok({ payload });
|
|
58
|
+
}
|
|
59
|
+
onReceive(envelope, context) {
|
|
60
|
+
const start = this.now();
|
|
61
|
+
// Simulate processing
|
|
62
|
+
const end = this.now();
|
|
63
|
+
this.record({
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
event: 'receive',
|
|
66
|
+
duration: end - start,
|
|
67
|
+
success: true,
|
|
68
|
+
});
|
|
69
|
+
return ok({ payload: envelope });
|
|
70
|
+
}
|
|
71
|
+
onEncrypt(plaintext, context) {
|
|
72
|
+
const start = this.now();
|
|
73
|
+
// Simulate processing
|
|
74
|
+
const end = this.now();
|
|
75
|
+
this.record({
|
|
76
|
+
timestamp: Date.now(),
|
|
77
|
+
event: 'encrypt',
|
|
78
|
+
duration: end - start,
|
|
79
|
+
success: true,
|
|
80
|
+
});
|
|
81
|
+
return ok({ payload: plaintext });
|
|
82
|
+
}
|
|
83
|
+
onDecrypt(plaintext, context) {
|
|
84
|
+
const start = this.now();
|
|
85
|
+
// Simulate processing
|
|
86
|
+
const end = this.now();
|
|
87
|
+
this.record({
|
|
88
|
+
timestamp: Date.now(),
|
|
89
|
+
event: 'decrypt',
|
|
90
|
+
duration: end - start,
|
|
91
|
+
success: true,
|
|
92
|
+
});
|
|
93
|
+
return ok({ payload: plaintext });
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get all captured metrics.
|
|
97
|
+
*/
|
|
98
|
+
getMetrics() {
|
|
99
|
+
return [...this.metrics];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get aggregated metrics summary.
|
|
103
|
+
*/
|
|
104
|
+
getAggregatedMetrics() {
|
|
105
|
+
const sends = this.metrics.filter((m) => m.event === 'send');
|
|
106
|
+
const receives = this.metrics.filter((m) => m.event === 'receive');
|
|
107
|
+
const encrypts = this.metrics.filter((m) => m.event === 'encrypt');
|
|
108
|
+
const decrypts = this.metrics.filter((m) => m.event === 'decrypt');
|
|
109
|
+
const errors = this.metrics.filter((m) => !m.success);
|
|
110
|
+
const avgDuration = (items) => {
|
|
111
|
+
if (items.length === 0)
|
|
112
|
+
return 0;
|
|
113
|
+
return items.reduce((sum, m) => sum + m.duration, 0) / items.length;
|
|
114
|
+
};
|
|
115
|
+
const total = this.metrics.length;
|
|
116
|
+
return {
|
|
117
|
+
totalSends: sends.length,
|
|
118
|
+
totalReceives: receives.length,
|
|
119
|
+
totalEncrypts: encrypts.length,
|
|
120
|
+
totalDecrypts: decrypts.length,
|
|
121
|
+
avgSendDuration: avgDuration(sends),
|
|
122
|
+
avgReceiveDuration: avgDuration(receives),
|
|
123
|
+
avgEncryptDuration: avgDuration(encrypts),
|
|
124
|
+
avgDecryptDuration: avgDuration(decrypts),
|
|
125
|
+
totalErrors: errors.length,
|
|
126
|
+
errorRate: total > 0 ? errors.length / total : 0,
|
|
127
|
+
uptime: Date.now() - this.startTime,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Clear all metrics.
|
|
132
|
+
*/
|
|
133
|
+
clearMetrics() {
|
|
134
|
+
this.metrics = [];
|
|
135
|
+
this.startTime = Date.now();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Export metrics using the configured callback.
|
|
139
|
+
*/
|
|
140
|
+
export() {
|
|
141
|
+
if (this.metrics.length > 0) {
|
|
142
|
+
this.options.onExport([...this.metrics]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get metrics for a specific time range.
|
|
147
|
+
*/
|
|
148
|
+
getMetricsInRange(startTime, endTime) {
|
|
149
|
+
return this.metrics.filter((m) => m.timestamp >= startTime && m.timestamp <= endTime);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get metrics for a specific event type.
|
|
153
|
+
*/
|
|
154
|
+
getMetricsByEvent(event) {
|
|
155
|
+
return this.metrics.filter((m) => m.event === event);
|
|
156
|
+
}
|
|
157
|
+
record(entry) {
|
|
158
|
+
this.metrics.push(entry);
|
|
159
|
+
// Enforce max metrics limit
|
|
160
|
+
if (this.metrics.length > this.options.maxMetrics) {
|
|
161
|
+
this.metrics.shift(); // Remove oldest
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
now() {
|
|
165
|
+
if (this.options.highResolution && typeof performance !== 'undefined') {
|
|
166
|
+
return performance.now();
|
|
167
|
+
}
|
|
168
|
+
return Date.now();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Create a metrics plugin with options.
|
|
173
|
+
*/
|
|
174
|
+
export function createMetricsPlugin(options) {
|
|
175
|
+
return new MetricsPlugin(options);
|
|
176
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module plugins/validation
|
|
3
|
+
* Validation plugin for xBind plugin system
|
|
4
|
+
*
|
|
5
|
+
* Validates payloads, envelopes, and ensures data integrity before processing.
|
|
6
|
+
*/
|
|
7
|
+
import type { Result } from '@private.me/shared';
|
|
8
|
+
import type { Plugin, PluginContext, HookResult } from '../plugin-system.js';
|
|
9
|
+
import type { AnyTransportEnvelope } from '../envelope.js';
|
|
10
|
+
/**
|
|
11
|
+
* Validation rule.
|
|
12
|
+
*/
|
|
13
|
+
export interface ValidationRule {
|
|
14
|
+
/** Rule name */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Validate function - returns error message on failure, null on success */
|
|
17
|
+
validate: (value: unknown, context: PluginContext) => string | null;
|
|
18
|
+
/** Apply to send operations */
|
|
19
|
+
appliesTo?: ('send' | 'receive' | 'encrypt' | 'decrypt')[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validation plugin options.
|
|
23
|
+
*/
|
|
24
|
+
export interface ValidationPluginOptions {
|
|
25
|
+
/** Maximum payload size in bytes (default: 10MB) */
|
|
26
|
+
maxPayloadSize?: number;
|
|
27
|
+
/** Maximum envelope size in bytes (default: 15MB) */
|
|
28
|
+
maxEnvelopeSize?: number;
|
|
29
|
+
/** Allowed scopes (empty = all allowed) */
|
|
30
|
+
allowedScopes?: string[];
|
|
31
|
+
/** Custom validation rules */
|
|
32
|
+
customRules?: ValidationRule[];
|
|
33
|
+
/** Strict mode - reject any validation warning (default: false) */
|
|
34
|
+
strictMode?: boolean;
|
|
35
|
+
/** Enable payload schema validation (default: false) */
|
|
36
|
+
validateSchema?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validation plugin implementation.
|
|
40
|
+
* Validates all data before processing to ensure integrity and compliance.
|
|
41
|
+
*/
|
|
42
|
+
export declare class ValidationPlugin implements Plugin {
|
|
43
|
+
readonly name = "validation";
|
|
44
|
+
readonly version = "1.0.0";
|
|
45
|
+
readonly description = "Validates payloads and envelopes before processing";
|
|
46
|
+
readonly priority = 5;
|
|
47
|
+
private options;
|
|
48
|
+
private validationErrors;
|
|
49
|
+
constructor(options?: ValidationPluginOptions);
|
|
50
|
+
onInit(): Result<void, string>;
|
|
51
|
+
onDestroy(): Result<void, string>;
|
|
52
|
+
onSend(payload: unknown, context: PluginContext): Result<HookResult<unknown>, string>;
|
|
53
|
+
onReceive(envelope: AnyTransportEnvelope, context: PluginContext): Result<HookResult<AnyTransportEnvelope>, string>;
|
|
54
|
+
onEncrypt(plaintext: Uint8Array, context: PluginContext): Result<HookResult<Uint8Array>, string>;
|
|
55
|
+
onDecrypt(plaintext: Uint8Array, context: PluginContext): Result<HookResult<Uint8Array>, string>;
|
|
56
|
+
/**
|
|
57
|
+
* Get all validation errors.
|
|
58
|
+
*/
|
|
59
|
+
getValidationErrors(): ReadonlyArray<{
|
|
60
|
+
timestamp: number;
|
|
61
|
+
rule: string;
|
|
62
|
+
error: string;
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Clear validation errors.
|
|
66
|
+
*/
|
|
67
|
+
clearErrors(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Add a custom validation rule.
|
|
70
|
+
*/
|
|
71
|
+
addRule(rule: ValidationRule): void;
|
|
72
|
+
/**
|
|
73
|
+
* Remove a custom validation rule by name.
|
|
74
|
+
*/
|
|
75
|
+
removeRule(name: string): boolean;
|
|
76
|
+
private validatePayloadSize;
|
|
77
|
+
private validateEnvelopeSize;
|
|
78
|
+
private validateScope;
|
|
79
|
+
private validatePayloadStructure;
|
|
80
|
+
private validateEnvelopeStructure;
|
|
81
|
+
private estimateSize;
|
|
82
|
+
private recordError;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create a validation plugin with options.
|
|
86
|
+
*/
|
|
87
|
+
export declare function createValidationPlugin(options?: ValidationPluginOptions): ValidationPlugin;
|
|
88
|
+
/**
|
|
89
|
+
* Common validation rules.
|
|
90
|
+
*/
|
|
91
|
+
export declare const CommonRules: {
|
|
92
|
+
/**
|
|
93
|
+
* Validate payload contains required fields.
|
|
94
|
+
*/
|
|
95
|
+
requireFields(fields: string[]): ValidationRule;
|
|
96
|
+
/**
|
|
97
|
+
* Validate DID format.
|
|
98
|
+
*/
|
|
99
|
+
validateDIDFormat(): ValidationRule;
|
|
100
|
+
/**
|
|
101
|
+
* Validate timestamp is recent.
|
|
102
|
+
*/
|
|
103
|
+
validateTimestamp(maxAgeMs?: number): ValidationRule;
|
|
104
|
+
};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module plugins/validation
|
|
3
|
+
* Validation plugin for xBind plugin system
|
|
4
|
+
*
|
|
5
|
+
* Validates payloads, envelopes, and ensures data integrity before processing.
|
|
6
|
+
*/
|
|
7
|
+
import { ok, err } from"../_deps/shared/index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Validation plugin implementation.
|
|
10
|
+
* Validates all data before processing to ensure integrity and compliance.
|
|
11
|
+
*/
|
|
12
|
+
export class ValidationPlugin {
|
|
13
|
+
name = 'validation';
|
|
14
|
+
version = '1.0.0';
|
|
15
|
+
description = 'Validates payloads and envelopes before processing';
|
|
16
|
+
priority = 5; // Execute very early
|
|
17
|
+
options;
|
|
18
|
+
validationErrors = [];
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.options = {
|
|
21
|
+
maxPayloadSize: options.maxPayloadSize ?? 10 * 1024 * 1024, // 10MB
|
|
22
|
+
maxEnvelopeSize: options.maxEnvelopeSize ?? 15 * 1024 * 1024, // 15MB
|
|
23
|
+
allowedScopes: options.allowedScopes ?? [],
|
|
24
|
+
customRules: options.customRules ?? [],
|
|
25
|
+
strictMode: options.strictMode ?? false,
|
|
26
|
+
validateSchema: options.validateSchema ?? false,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
onInit() {
|
|
30
|
+
return ok(undefined);
|
|
31
|
+
}
|
|
32
|
+
onDestroy() {
|
|
33
|
+
this.validationErrors = [];
|
|
34
|
+
return ok(undefined);
|
|
35
|
+
}
|
|
36
|
+
onSend(payload, context) {
|
|
37
|
+
// Validate payload size
|
|
38
|
+
const sizeResult = this.validatePayloadSize(payload);
|
|
39
|
+
if (sizeResult.ok === false) {
|
|
40
|
+
this.recordError('payload_size', sizeResult.error);
|
|
41
|
+
return err(sizeResult.error);
|
|
42
|
+
}
|
|
43
|
+
// Validate scope
|
|
44
|
+
if (context.scope) {
|
|
45
|
+
const scopeResult = this.validateScope(context.scope);
|
|
46
|
+
if (scopeResult.ok === false) {
|
|
47
|
+
this.recordError('scope', scopeResult.error);
|
|
48
|
+
return err(scopeResult.error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Validate payload structure
|
|
52
|
+
const structureResult = this.validatePayloadStructure(payload);
|
|
53
|
+
if (structureResult.ok === false) {
|
|
54
|
+
this.recordError('payload_structure', structureResult.error);
|
|
55
|
+
return err(structureResult.error);
|
|
56
|
+
}
|
|
57
|
+
// Apply custom rules
|
|
58
|
+
for (const rule of this.options.customRules) {
|
|
59
|
+
if (!rule.appliesTo || rule.appliesTo.includes('send')) {
|
|
60
|
+
const error = rule.validate(payload, context);
|
|
61
|
+
if (error) {
|
|
62
|
+
this.recordError(rule.name, error);
|
|
63
|
+
return err(`VALIDATION_FAILED:${rule.name}:${error}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return ok({ payload });
|
|
68
|
+
}
|
|
69
|
+
onReceive(envelope, context) {
|
|
70
|
+
// Validate envelope size
|
|
71
|
+
const sizeResult = this.validateEnvelopeSize(envelope);
|
|
72
|
+
if (sizeResult.ok === false) {
|
|
73
|
+
this.recordError('envelope_size', sizeResult.error);
|
|
74
|
+
return err(sizeResult.error);
|
|
75
|
+
}
|
|
76
|
+
// Validate envelope structure
|
|
77
|
+
const structureResult = this.validateEnvelopeStructure(envelope);
|
|
78
|
+
if (structureResult.ok === false) {
|
|
79
|
+
this.recordError('envelope_structure', structureResult.error);
|
|
80
|
+
return err(structureResult.error);
|
|
81
|
+
}
|
|
82
|
+
// Apply custom rules
|
|
83
|
+
for (const rule of this.options.customRules) {
|
|
84
|
+
if (!rule.appliesTo || rule.appliesTo.includes('receive')) {
|
|
85
|
+
const error = rule.validate(envelope, context);
|
|
86
|
+
if (error) {
|
|
87
|
+
this.recordError(rule.name, error);
|
|
88
|
+
return err(`VALIDATION_FAILED:${rule.name}:${error}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return ok({ payload: envelope });
|
|
93
|
+
}
|
|
94
|
+
onEncrypt(plaintext, context) {
|
|
95
|
+
// Validate plaintext is not empty
|
|
96
|
+
if (plaintext.length === 0) {
|
|
97
|
+
this.recordError('plaintext_empty', 'Plaintext cannot be empty');
|
|
98
|
+
return err('VALIDATION_FAILED:PLAINTEXT_EMPTY');
|
|
99
|
+
}
|
|
100
|
+
// Validate plaintext size
|
|
101
|
+
if (plaintext.length > this.options.maxPayloadSize) {
|
|
102
|
+
this.recordError('plaintext_size', `Plaintext exceeds max size: ${plaintext.length} bytes`);
|
|
103
|
+
return err(`VALIDATION_FAILED:PLAINTEXT_TOO_LARGE:${plaintext.length}`);
|
|
104
|
+
}
|
|
105
|
+
// Apply custom rules
|
|
106
|
+
for (const rule of this.options.customRules) {
|
|
107
|
+
if (!rule.appliesTo || rule.appliesTo.includes('encrypt')) {
|
|
108
|
+
const error = rule.validate(plaintext, context);
|
|
109
|
+
if (error) {
|
|
110
|
+
this.recordError(rule.name, error);
|
|
111
|
+
return err(`VALIDATION_FAILED:${rule.name}:${error}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return ok({ payload: plaintext });
|
|
116
|
+
}
|
|
117
|
+
onDecrypt(plaintext, context) {
|
|
118
|
+
// Validate plaintext is not empty
|
|
119
|
+
if (plaintext.length === 0) {
|
|
120
|
+
this.recordError('plaintext_empty', 'Decrypted plaintext is empty');
|
|
121
|
+
return err('VALIDATION_FAILED:DECRYPTED_EMPTY');
|
|
122
|
+
}
|
|
123
|
+
// Apply custom rules
|
|
124
|
+
for (const rule of this.options.customRules) {
|
|
125
|
+
if (!rule.appliesTo || rule.appliesTo.includes('decrypt')) {
|
|
126
|
+
const error = rule.validate(plaintext, context);
|
|
127
|
+
if (error) {
|
|
128
|
+
this.recordError(rule.name, error);
|
|
129
|
+
return err(`VALIDATION_FAILED:${rule.name}:${error}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return ok({ payload: plaintext });
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get all validation errors.
|
|
137
|
+
*/
|
|
138
|
+
getValidationErrors() {
|
|
139
|
+
return [...this.validationErrors];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Clear validation errors.
|
|
143
|
+
*/
|
|
144
|
+
clearErrors() {
|
|
145
|
+
this.validationErrors = [];
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Add a custom validation rule.
|
|
149
|
+
*/
|
|
150
|
+
addRule(rule) {
|
|
151
|
+
this.options.customRules.push(rule);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Remove a custom validation rule by name.
|
|
155
|
+
*/
|
|
156
|
+
removeRule(name) {
|
|
157
|
+
const index = this.options.customRules.findIndex((r) => r.name === name);
|
|
158
|
+
if (index === -1) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
this.options.customRules.splice(index, 1);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
validatePayloadSize(payload) {
|
|
165
|
+
const size = this.estimateSize(payload);
|
|
166
|
+
if (size > this.options.maxPayloadSize) {
|
|
167
|
+
return err(`VALIDATION_FAILED:PAYLOAD_TOO_LARGE:${size}>${this.options.maxPayloadSize}`);
|
|
168
|
+
}
|
|
169
|
+
return ok(undefined);
|
|
170
|
+
}
|
|
171
|
+
validateEnvelopeSize(envelope) {
|
|
172
|
+
const size = this.estimateSize(envelope);
|
|
173
|
+
if (size > this.options.maxEnvelopeSize) {
|
|
174
|
+
return err(`VALIDATION_FAILED:ENVELOPE_TOO_LARGE:${size}>${this.options.maxEnvelopeSize}`);
|
|
175
|
+
}
|
|
176
|
+
return ok(undefined);
|
|
177
|
+
}
|
|
178
|
+
validateScope(scope) {
|
|
179
|
+
if (this.options.allowedScopes.length > 0 && !this.options.allowedScopes.includes(scope)) {
|
|
180
|
+
return err(`VALIDATION_FAILED:SCOPE_NOT_ALLOWED:${scope}`);
|
|
181
|
+
}
|
|
182
|
+
return ok(undefined);
|
|
183
|
+
}
|
|
184
|
+
validatePayloadStructure(payload) {
|
|
185
|
+
if (payload === null || payload === undefined) {
|
|
186
|
+
return err('VALIDATION_FAILED:PAYLOAD_NULL_OR_UNDEFINED');
|
|
187
|
+
}
|
|
188
|
+
// Validate can be serialized to JSON
|
|
189
|
+
try {
|
|
190
|
+
JSON.stringify(payload);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
return err(`VALIDATION_FAILED:PAYLOAD_NOT_SERIALIZABLE:${String(error)}`);
|
|
194
|
+
}
|
|
195
|
+
return ok(undefined);
|
|
196
|
+
}
|
|
197
|
+
validateEnvelopeStructure(envelope) {
|
|
198
|
+
// Validate required fields
|
|
199
|
+
if (!('v' in envelope) && !('version' in envelope)) {
|
|
200
|
+
return err('VALIDATION_FAILED:ENVELOPE_MISSING_VERSION');
|
|
201
|
+
}
|
|
202
|
+
if (!envelope.sender) {
|
|
203
|
+
return err('VALIDATION_FAILED:ENVELOPE_MISSING_SENDER');
|
|
204
|
+
}
|
|
205
|
+
if (!envelope.recipient) {
|
|
206
|
+
return err('VALIDATION_FAILED:ENVELOPE_MISSING_RECIPIENT');
|
|
207
|
+
}
|
|
208
|
+
if (!('payload' in envelope) && !('ciphertext' in envelope)) {
|
|
209
|
+
return err('VALIDATION_FAILED:ENVELOPE_MISSING_CIPHERTEXT');
|
|
210
|
+
}
|
|
211
|
+
return ok(undefined);
|
|
212
|
+
}
|
|
213
|
+
estimateSize(value) {
|
|
214
|
+
if (value instanceof Uint8Array) {
|
|
215
|
+
return value.length;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
return JSON.stringify(value).length;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
recordError(rule, error) {
|
|
225
|
+
this.validationErrors.push({
|
|
226
|
+
timestamp: Date.now(),
|
|
227
|
+
rule,
|
|
228
|
+
error,
|
|
229
|
+
});
|
|
230
|
+
// Keep only last 1000 errors
|
|
231
|
+
if (this.validationErrors.length > 1000) {
|
|
232
|
+
this.validationErrors.shift();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Create a validation plugin with options.
|
|
238
|
+
*/
|
|
239
|
+
export function createValidationPlugin(options) {
|
|
240
|
+
return new ValidationPlugin(options);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Common validation rules.
|
|
244
|
+
*/
|
|
245
|
+
export const CommonRules = {
|
|
246
|
+
/**
|
|
247
|
+
* Validate payload contains required fields.
|
|
248
|
+
*/
|
|
249
|
+
requireFields(fields) {
|
|
250
|
+
return {
|
|
251
|
+
name: 'require_fields',
|
|
252
|
+
appliesTo: ['send'],
|
|
253
|
+
validate: (value) => {
|
|
254
|
+
if (typeof value !== 'object' || value === null) {
|
|
255
|
+
return 'Payload must be an object';
|
|
256
|
+
}
|
|
257
|
+
const obj = value;
|
|
258
|
+
const missing = fields.filter((field) => !(field in obj));
|
|
259
|
+
if (missing.length > 0) {
|
|
260
|
+
return `Missing required fields: ${missing.join(', ')}`;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
/**
|
|
267
|
+
* Validate DID format.
|
|
268
|
+
*/
|
|
269
|
+
validateDIDFormat() {
|
|
270
|
+
return {
|
|
271
|
+
name: 'did_format',
|
|
272
|
+
appliesTo: ['send', 'receive'],
|
|
273
|
+
validate: (value, context) => {
|
|
274
|
+
if (context.recipient && !context.recipient.startsWith('did:')) {
|
|
275
|
+
return `Invalid DID format: ${context.recipient}`;
|
|
276
|
+
}
|
|
277
|
+
return null;
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
/**
|
|
282
|
+
* Validate timestamp is recent.
|
|
283
|
+
*/
|
|
284
|
+
validateTimestamp(maxAgeMs = 300000) {
|
|
285
|
+
return {
|
|
286
|
+
name: 'timestamp_freshness',
|
|
287
|
+
appliesTo: ['receive'],
|
|
288
|
+
validate: (value, context) => {
|
|
289
|
+
const age = Date.now() - context.timestamp;
|
|
290
|
+
if (age > maxAgeMs) {
|
|
291
|
+
return `Timestamp too old: ${age}ms > ${maxAgeMs}ms`;
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
},
|
|
297
|
+
};
|