@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.
Files changed (217) hide show
  1. package/README.md +55 -7
  2. package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1920 -1
  3. package/dist-standalone/_deps/shared/cjs/errors.js +729 -1
  4. package/dist-standalone/_deps/shared/cjs/index.js +463 -1
  5. package/dist-standalone/_deps/shared/cjs/types.js +315 -1
  6. package/dist-standalone/_deps/shared/errors.js +244 -1
  7. package/dist-standalone/_deps/shared/index.js +72 -1
  8. package/dist-standalone/_deps/shared/types.js +86 -1
  9. package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
  10. package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
  11. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
  12. package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
  13. package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
  14. package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
  15. package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
  16. package/dist-standalone/_deps/ux-helpers/index.js +1 -1
  17. package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
  18. package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
  19. package/dist-standalone/_deps/ux-helpers/search.js +1 -1
  20. package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
  21. package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
  22. package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
  23. package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
  24. package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
  25. package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
  26. package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
  27. package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
  28. package/dist-standalone/_deps/xchange/errors.js +1 -1
  29. package/dist-standalone/_deps/xchange/index.js +1 -1
  30. package/dist-standalone/_deps/xchange/invite-client.js +1 -1
  31. package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
  32. package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
  33. package/dist-standalone/_deps/xchange/xchange.js +1 -1
  34. package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
  35. package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
  36. package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
  37. package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
  38. package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
  39. package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
  40. package/dist-standalone/_deps/xregistry/discovery.js +1 -1
  41. package/dist-standalone/_deps/xregistry/errors.js +1 -1
  42. package/dist-standalone/_deps/xregistry/index.js +1 -1
  43. package/dist-standalone/_deps/xregistry/registry.js +1 -1
  44. package/dist-standalone/_deps/xregistry/schema.js +1 -1
  45. package/dist-standalone/_deps/xregistry/types.js +1 -1
  46. package/dist-standalone/agent-call.js +659 -1
  47. package/dist-standalone/agent-sdk.js +328 -1
  48. package/dist-standalone/agent.js +1800 -1
  49. package/dist-standalone/approval.js +193 -1
  50. package/dist-standalone/async-iterators.js +382 -1
  51. package/dist-standalone/auth.js +219 -1
  52. package/dist-standalone/auto-accept.js +229 -1
  53. package/dist-standalone/backup-config.js +201 -1
  54. package/dist-standalone/backup.js +326 -1
  55. package/dist-standalone/batch-operations.js +388 -1
  56. package/dist-standalone/cancellation.js +477 -1
  57. package/dist-standalone/checkpoint.js +186 -1
  58. package/dist-standalone/circuit-breaker.js +468 -1
  59. package/dist-standalone/cjs/agent-call.js +701 -1
  60. package/dist-standalone/cjs/agent-sdk.js +332 -1
  61. package/dist-standalone/cjs/agent.js +1837 -1
  62. package/dist-standalone/cjs/approval.js +199 -1
  63. package/dist-standalone/cjs/async-iterators.js +392 -1
  64. package/dist-standalone/cjs/auth.js +225 -1
  65. package/dist-standalone/cjs/auto-accept.js +233 -1
  66. package/dist-standalone/cjs/backup-config.js +207 -1
  67. package/dist-standalone/cjs/backup.js +330 -1
  68. package/dist-standalone/cjs/batch-operations.js +397 -1
  69. package/dist-standalone/cjs/cancellation.js +490 -1
  70. package/dist-standalone/cjs/checkpoint.js +193 -1
  71. package/dist-standalone/cjs/circuit-breaker.js +476 -1
  72. package/dist-standalone/cjs/cli/init.js +492 -1
  73. package/dist-standalone/cjs/config-validation.js +522 -1
  74. package/dist-standalone/cjs/connect.js +312 -1
  75. package/dist-standalone/cjs/connection-pool.js +506 -1
  76. package/dist-standalone/cjs/correlation-id.js +339 -1
  77. package/dist-standalone/cjs/crypto-utils.js +176 -1
  78. package/dist-standalone/cjs/debug-mode.js +534 -1
  79. package/dist-standalone/cjs/did-document.js +101 -1
  80. package/dist-standalone/cjs/did-privateme.js +130 -1
  81. package/dist-standalone/cjs/did-web.js +201 -1
  82. package/dist-standalone/cjs/discovery.js +462 -1
  83. package/dist-standalone/cjs/dual-mode.js +251 -1
  84. package/dist-standalone/cjs/email-templates.js +313 -1
  85. package/dist-standalone/cjs/email-transport.js +239 -1
  86. package/dist-standalone/cjs/envelope.js +538 -1
  87. package/dist-standalone/cjs/errors.js +913 -1
  88. package/dist-standalone/cjs/event-emitter.js +461 -1
  89. package/dist-standalone/cjs/gateway-state.js +55 -1
  90. package/dist-standalone/cjs/gateway-transport.js +120 -1
  91. package/dist-standalone/cjs/graceful-degradation.js +403 -1
  92. package/dist-standalone/cjs/guardrails.js +223 -1
  93. package/dist-standalone/cjs/health-check.js +336 -1
  94. package/dist-standalone/cjs/http-compat.js +272 -1
  95. package/dist-standalone/cjs/http-status-map.js +571 -1
  96. package/dist-standalone/cjs/identity.js +645 -1
  97. package/dist-standalone/cjs/index.js +406 -1
  98. package/dist-standalone/cjs/invitation.js +421 -1
  99. package/dist-standalone/cjs/invite.js +328 -1
  100. package/dist-standalone/cjs/key-agreement.js +335 -1
  101. package/dist-standalone/cjs/lazy-init.js +300 -1
  102. package/dist-standalone/cjs/logger.js +291 -1
  103. package/dist-standalone/cjs/mdns-discovery.js +202 -1
  104. package/dist-standalone/cjs/nonce-store.js +80 -1
  105. package/dist-standalone/cjs/pairing-manager.js +223 -1
  106. package/dist-standalone/cjs/plugin-system.js +264 -1
  107. package/dist-standalone/cjs/plugins/logging.js +168 -1
  108. package/dist-standalone/cjs/plugins/metrics.js +181 -1
  109. package/dist-standalone/cjs/plugins/validation.js +302 -1
  110. package/dist-standalone/cjs/policy.js +320 -1
  111. package/dist-standalone/cjs/progress-callbacks.js +583 -1
  112. package/dist-standalone/cjs/redis-nonce-store.js +76 -1
  113. package/dist-standalone/cjs/registry-middleware.js +50 -1
  114. package/dist-standalone/cjs/retry-strategies.js +544 -1
  115. package/dist-standalone/cjs/retry-transport.js +102 -1
  116. package/dist-standalone/cjs/runtime/browser.js +533 -1
  117. package/dist-standalone/cjs/runtime/edge.js +526 -1
  118. package/dist-standalone/cjs/runtime/react-native.js +394 -1
  119. package/dist-standalone/cjs/security-policy.js +245 -1
  120. package/dist-standalone/cjs/serialization.js +1040 -1
  121. package/dist-standalone/cjs/split-channel.js +225 -1
  122. package/dist-standalone/cjs/subscription-proof.js +230 -1
  123. package/dist-standalone/cjs/succession.js +148 -1
  124. package/dist-standalone/cjs/timeouts.js +412 -1
  125. package/dist-standalone/cjs/trace-context.js +424 -1
  126. package/dist-standalone/cjs/trace-spans.js +495 -1
  127. package/dist-standalone/cjs/transport.js +63 -1
  128. package/dist-standalone/cjs/trust-registry.js +991 -1
  129. package/dist-standalone/cjs/types/error-response.js +56 -1
  130. package/dist-standalone/cjs/vault-auth.js +178 -1
  131. package/dist-standalone/cjs/vault-store-loader.js +194 -1
  132. package/dist-standalone/cjs/verify.js +25 -1
  133. package/dist-standalone/cjs/version-info.js +543 -1
  134. package/dist-standalone/cjs/xfetch.js +340 -1
  135. package/dist-standalone/cli/init.js +455 -1
  136. package/dist-standalone/cli/setup.js +514 -1
  137. package/dist-standalone/cli/types.js +27 -1
  138. package/dist-standalone/cli/xbind.js +148 -1
  139. package/dist-standalone/config-validation.js +513 -1
  140. package/dist-standalone/connect.js +274 -1
  141. package/dist-standalone/connection-pool.js +500 -1
  142. package/dist-standalone/correlation-id.js +326 -1
  143. package/dist-standalone/crypto-utils.js +157 -1
  144. package/dist-standalone/debug-mode.js +510 -1
  145. package/dist-standalone/did-document.js +96 -1
  146. package/dist-standalone/did-privateme.js +121 -1
  147. package/dist-standalone/did-web.js +196 -1
  148. package/dist-standalone/discovery.js +458 -1
  149. package/dist-standalone/dual-mode.js +247 -1
  150. package/dist-standalone/email-templates.js +309 -1
  151. package/dist-standalone/email-transport.js +232 -1
  152. package/dist-standalone/envelope.js +525 -1
  153. package/dist-standalone/errors.js +896 -1
  154. package/dist-standalone/event-emitter.js +456 -1
  155. package/dist-standalone/gateway-state.js +51 -1
  156. package/dist-standalone/gateway-transport.js +116 -1
  157. package/dist-standalone/graceful-degradation.js +396 -1
  158. package/dist-standalone/guardrails.js +216 -1
  159. package/dist-standalone/health-check.js +332 -1
  160. package/dist-standalone/http-compat.js +267 -1
  161. package/dist-standalone/http-status-map.js +561 -1
  162. package/dist-standalone/identity.js +619 -1
  163. package/dist-standalone/index.js +78 -1
  164. package/dist-standalone/invitation.js +415 -1
  165. package/dist-standalone/invite.js +324 -1
  166. package/dist-standalone/key-agreement.js +325 -1
  167. package/dist-standalone/lazy-init.js +295 -1
  168. package/dist-standalone/logger.js +285 -1
  169. package/dist-standalone/mdns-discovery.js +195 -1
  170. package/dist-standalone/nonce-store.js +76 -1
  171. package/dist-standalone/pairing-manager.js +219 -1
  172. package/dist-standalone/plugin-system.js +257 -1
  173. package/dist-standalone/plugins/logging.d.ts +84 -0
  174. package/dist-standalone/plugins/logging.js +163 -0
  175. package/dist-standalone/plugins/metrics.d.ts +111 -0
  176. package/dist-standalone/plugins/metrics.js +176 -0
  177. package/dist-standalone/plugins/validation.d.ts +104 -0
  178. package/dist-standalone/plugins/validation.js +297 -0
  179. package/dist-standalone/policy.js +315 -1
  180. package/dist-standalone/progress-callbacks.js +576 -1
  181. package/dist-standalone/redis-nonce-store.js +72 -1
  182. package/dist-standalone/registry-middleware.js +47 -1
  183. package/dist-standalone/retry-strategies.js +534 -1
  184. package/dist-standalone/retry-transport.js +98 -1
  185. package/dist-standalone/runtime/browser.d.ts +311 -0
  186. package/dist-standalone/runtime/browser.js +516 -0
  187. package/dist-standalone/runtime/edge.d.ts +282 -0
  188. package/dist-standalone/runtime/edge.js +511 -0
  189. package/dist-standalone/runtime/react-native.d.ts +157 -0
  190. package/dist-standalone/runtime/react-native.js +383 -0
  191. package/dist-standalone/security-policy.js +239 -1
  192. package/dist-standalone/serialization.js +1031 -1
  193. package/dist-standalone/split-channel.js +219 -1
  194. package/dist-standalone/subscription-proof.js +224 -1
  195. package/dist-standalone/succession.js +142 -1
  196. package/dist-standalone/timeouts.js +398 -1
  197. package/dist-standalone/trace-context.js +414 -1
  198. package/dist-standalone/trace-spans.js +488 -1
  199. package/dist-standalone/transport.js +59 -1
  200. package/dist-standalone/trust-registry.js +950 -1
  201. package/dist-standalone/types/error-response.d.ts +209 -0
  202. package/dist-standalone/types/error-response.js +52 -0
  203. package/dist-standalone/vault-auth.js +174 -1
  204. package/dist-standalone/vault-store-loader.js +187 -1
  205. package/dist-standalone/verify.js +16 -1
  206. package/dist-standalone/version-info.js +530 -1
  207. package/dist-standalone/xfetch.js +335 -1
  208. package/package.json +4 -10
  209. package/share1.dat +0 -0
  210. package/dist-standalone/_deps/mldsa-wasm/LICENSE +0 -24
  211. package/dist-standalone/_deps/mldsa-wasm/package.json +0 -46
  212. package/dist-standalone/_deps/shared/cjs/package.json +0 -1
  213. package/dist-standalone/_deps/ux-helpers/cjs/package.json +0 -1
  214. package/dist-standalone/_deps/xchange/cjs/package.json +0 -1
  215. package/dist-standalone/_deps/xregistry/cjs/package.json +0 -1
  216. package/dist-standalone/cjs/package.json +0 -3
  217. 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
+ };