@0xobelisk/react 1.2.0-pre.100

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.
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Utility Functions for Dubhe Configuration Management
3
+ *
4
+ * Features:
5
+ * - Configuration validation and error handling
6
+ * - Smart configuration merging with proper type safety
7
+ * - Type-safe configuration validation
8
+ */
9
+
10
+ import type { DubheConfig } from './types';
11
+
12
+ /**
13
+ * Merge multiple configuration objects with proper deep merging
14
+ * Later configurations override earlier ones
15
+ *
16
+ * @param baseConfig - Base configuration (usually defaults)
17
+ * @param overrideConfig - Override configuration (user provided)
18
+ * @returns Merged configuration
19
+ */
20
+ export function mergeConfigurations(
21
+ baseConfig: Partial<DubheConfig>,
22
+ overrideConfig?: Partial<DubheConfig>
23
+ ): Partial<DubheConfig> {
24
+ if (!overrideConfig) {
25
+ return { ...baseConfig };
26
+ }
27
+
28
+ const result: Partial<DubheConfig> = { ...baseConfig };
29
+
30
+ // Merge top-level properties
31
+ Object.assign(result, overrideConfig);
32
+
33
+ // Deep merge nested objects
34
+ if (overrideConfig.credentials || baseConfig.credentials) {
35
+ result.credentials = {
36
+ ...baseConfig.credentials,
37
+ ...overrideConfig.credentials
38
+ };
39
+ }
40
+
41
+ if (overrideConfig.endpoints || baseConfig.endpoints) {
42
+ result.endpoints = {
43
+ ...baseConfig.endpoints,
44
+ ...overrideConfig.endpoints
45
+ };
46
+ }
47
+
48
+ if (overrideConfig.options || baseConfig.options) {
49
+ result.options = {
50
+ ...baseConfig.options,
51
+ ...overrideConfig.options
52
+ };
53
+ }
54
+
55
+ return result;
56
+ }
57
+
58
+ /**
59
+ * Validate configuration and ensure required fields are present
60
+ * Throws descriptive errors for missing required fields
61
+ *
62
+ * @param config - Configuration to validate
63
+ * @returns Validated and typed configuration
64
+ * @throws Error if required fields are missing or invalid
65
+ */
66
+ export function validateConfig(config: Partial<DubheConfig>): DubheConfig {
67
+ const errors: string[] = [];
68
+
69
+ // Check required fields
70
+ if (!config.network) {
71
+ errors.push('network is required');
72
+ }
73
+
74
+ if (!config.packageId) {
75
+ errors.push('packageId is required');
76
+ }
77
+
78
+ if (!config.metadata) {
79
+ errors.push('metadata is required');
80
+ } else {
81
+ // Basic metadata validation
82
+ if (typeof config.metadata !== 'object') {
83
+ errors.push('metadata must be an object');
84
+ } else if (Object.keys(config.metadata).length === 0) {
85
+ errors.push('metadata cannot be empty');
86
+ }
87
+ }
88
+
89
+ // Validate network type
90
+ if (config.network && !['mainnet', 'testnet', 'devnet', 'localnet'].includes(config.network)) {
91
+ errors.push(
92
+ `invalid network: ${config.network}. Must be one of: mainnet, testnet, devnet, localnet`
93
+ );
94
+ }
95
+
96
+ // Validate package ID format (enhanced check)
97
+ if (config.packageId) {
98
+ if (!config.packageId.startsWith('0x')) {
99
+ errors.push('packageId must start with 0x');
100
+ } else if (config.packageId.length < 3) {
101
+ errors.push('packageId must be longer than 0x');
102
+ } else if (!/^0x[a-fA-F0-9]+$/.test(config.packageId)) {
103
+ errors.push('packageId must contain only hexadecimal characters after 0x');
104
+ }
105
+ }
106
+
107
+ // Validate dubheMetadata if provided
108
+ if (config.dubheMetadata !== undefined) {
109
+ if (typeof config.dubheMetadata !== 'object' || config.dubheMetadata === null) {
110
+ errors.push('dubheMetadata must be an object');
111
+ } else if (!config.dubheMetadata.components && !config.dubheMetadata.resources) {
112
+ errors.push('dubheMetadata must contain components or resources');
113
+ }
114
+ }
115
+
116
+ // Validate credentials if provided
117
+ if (config.credentials) {
118
+ if (config.credentials.secretKey && typeof config.credentials.secretKey !== 'string') {
119
+ errors.push('credentials.secretKey must be a string');
120
+ }
121
+ if (config.credentials.mnemonics && typeof config.credentials.mnemonics !== 'string') {
122
+ errors.push('credentials.mnemonics must be a string');
123
+ }
124
+ }
125
+
126
+ // Validate URLs if provided
127
+ if (config.endpoints?.graphql && !isValidUrl(config.endpoints.graphql)) {
128
+ errors.push('endpoints.graphql must be a valid URL');
129
+ }
130
+
131
+ if (config.endpoints?.websocket && !isValidUrl(config.endpoints.websocket)) {
132
+ errors.push('endpoints.websocket must be a valid URL');
133
+ }
134
+
135
+ if (config.endpoints?.grpc && !isValidUrl(config.endpoints.grpc)) {
136
+ errors.push('endpoints.grpc must be a valid URL');
137
+ }
138
+
139
+ // Validate fullnodeUrls if provided
140
+ if (config.endpoints?.fullnodeUrls) {
141
+ if (!Array.isArray(config.endpoints.fullnodeUrls)) {
142
+ errors.push('endpoints.fullnodeUrls must be an array');
143
+ } else if (config.endpoints.fullnodeUrls.length === 0) {
144
+ errors.push('endpoints.fullnodeUrls cannot be empty if provided');
145
+ } else {
146
+ config.endpoints.fullnodeUrls.forEach((url, index) => {
147
+ if (!isValidUrl(url)) {
148
+ errors.push(`endpoints.fullnodeUrls[${index}] must be a valid URL: ${url}`);
149
+ }
150
+ });
151
+ }
152
+ }
153
+
154
+ // Validate numeric options
155
+ if (
156
+ config.options?.cacheTimeout !== undefined &&
157
+ (typeof config.options.cacheTimeout !== 'number' || config.options.cacheTimeout < 0)
158
+ ) {
159
+ errors.push('options.cacheTimeout must be a non-negative number');
160
+ }
161
+
162
+ if (
163
+ config.options?.debounceMs !== undefined &&
164
+ (typeof config.options.debounceMs !== 'number' || config.options.debounceMs < 0)
165
+ ) {
166
+ errors.push('options.debounceMs must be a non-negative number');
167
+ }
168
+
169
+ if (errors.length > 0) {
170
+ const errorMessage = `Invalid Dubhe configuration (${errors.length} error${
171
+ errors.length > 1 ? 's' : ''
172
+ }):\n${errors.map((e) => `- ${e}`).join('\n')}`;
173
+ console.error('Configuration validation failed:', { errors, config });
174
+ throw new Error(errorMessage);
175
+ }
176
+
177
+ return config as DubheConfig;
178
+ }
179
+
180
+ /**
181
+ * Simple URL validation helper
182
+ *
183
+ * @param url - URL string to validate
184
+ * @returns true if URL is valid, false otherwise
185
+ */
186
+ function isValidUrl(url: string): boolean {
187
+ try {
188
+ new URL(url);
189
+ return true;
190
+ } catch {
191
+ return false;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Generate a configuration summary for debugging
197
+ * Hides sensitive information like private keys
198
+ *
199
+ * @param config - Configuration to summarize
200
+ * @returns Safe configuration summary
201
+ */
202
+ export function getConfigSummary(config: DubheConfig): object {
203
+ return {
204
+ network: config.network,
205
+ packageId: config.packageId,
206
+ dubheSchemaId: config.dubheSchemaId,
207
+ hasMetadata: !!config.metadata,
208
+ hasDubheMetadata: !!config.dubheMetadata,
209
+ hasCredentials: !!config.credentials?.secretKey,
210
+ endpoints: config.endpoints,
211
+ options: config.options
212
+ };
213
+ }