appwrite-utils-cli 1.7.8 → 1.8.1
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/CHANGELOG.md +14 -199
- package/README.md +87 -30
- package/dist/adapters/AdapterFactory.js +5 -25
- package/dist/adapters/DatabaseAdapter.d.ts +17 -2
- package/dist/adapters/LegacyAdapter.d.ts +2 -1
- package/dist/adapters/LegacyAdapter.js +212 -16
- package/dist/adapters/TablesDBAdapter.d.ts +2 -12
- package/dist/adapters/TablesDBAdapter.js +261 -57
- package/dist/cli/commands/databaseCommands.js +10 -10
- package/dist/cli/commands/functionCommands.js +17 -8
- package/dist/collections/attributes.js +447 -125
- package/dist/collections/methods.js +197 -186
- package/dist/collections/tableOperations.d.ts +86 -0
- package/dist/collections/tableOperations.js +434 -0
- package/dist/collections/transferOperations.d.ts +3 -2
- package/dist/collections/transferOperations.js +93 -12
- package/dist/config/services/ConfigLoaderService.d.ts +7 -0
- package/dist/config/services/ConfigLoaderService.js +47 -1
- package/dist/config/yamlConfig.d.ts +221 -88
- package/dist/examples/yamlTerminologyExample.d.ts +1 -1
- package/dist/examples/yamlTerminologyExample.js +6 -3
- package/dist/functions/deployments.js +5 -23
- package/dist/functions/fnConfigDiscovery.d.ts +3 -0
- package/dist/functions/fnConfigDiscovery.js +108 -0
- package/dist/functions/methods.js +4 -2
- package/dist/functions/pathResolution.d.ts +37 -0
- package/dist/functions/pathResolution.js +185 -0
- package/dist/functions/templates/count-docs-in-collection/README.md +54 -0
- package/dist/functions/templates/count-docs-in-collection/package.json +25 -0
- package/dist/functions/templates/count-docs-in-collection/src/main.ts +159 -0
- package/dist/functions/templates/count-docs-in-collection/src/request.ts +9 -0
- package/dist/functions/templates/count-docs-in-collection/tsconfig.json +28 -0
- package/dist/functions/templates/hono-typescript/README.md +286 -0
- package/dist/functions/templates/hono-typescript/package.json +26 -0
- package/dist/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/dist/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/dist/functions/templates/hono-typescript/src/app.ts +180 -0
- package/dist/functions/templates/hono-typescript/src/context.ts +103 -0
- package/dist/functions/templates/hono-typescript/src/index.ts +54 -0
- package/dist/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/dist/functions/templates/hono-typescript/tsconfig.json +20 -0
- package/dist/functions/templates/typescript-node/README.md +32 -0
- package/dist/functions/templates/typescript-node/package.json +25 -0
- package/dist/functions/templates/typescript-node/src/context.ts +103 -0
- package/dist/functions/templates/typescript-node/src/index.ts +29 -0
- package/dist/functions/templates/typescript-node/tsconfig.json +28 -0
- package/dist/functions/templates/uv/README.md +31 -0
- package/dist/functions/templates/uv/pyproject.toml +30 -0
- package/dist/functions/templates/uv/src/__init__.py +0 -0
- package/dist/functions/templates/uv/src/context.py +125 -0
- package/dist/functions/templates/uv/src/index.py +46 -0
- package/dist/interactiveCLI.js +18 -15
- package/dist/main.js +219 -81
- package/dist/migrations/appwriteToX.d.ts +88 -23
- package/dist/migrations/comprehensiveTransfer.d.ts +2 -0
- package/dist/migrations/comprehensiveTransfer.js +83 -6
- package/dist/migrations/dataLoader.d.ts +227 -69
- package/dist/migrations/dataLoader.js +3 -3
- package/dist/migrations/importController.js +3 -3
- package/dist/migrations/relationships.d.ts +8 -2
- package/dist/migrations/services/ImportOrchestrator.js +3 -3
- package/dist/migrations/transfer.js +159 -37
- package/dist/shared/attributeMapper.d.ts +20 -0
- package/dist/shared/attributeMapper.js +203 -0
- package/dist/shared/selectionDialogs.d.ts +1 -1
- package/dist/shared/selectionDialogs.js +39 -11
- package/dist/storage/schemas.d.ts +354 -92
- package/dist/utils/configDiscovery.js +4 -3
- package/dist/utils/versionDetection.d.ts +0 -4
- package/dist/utils/versionDetection.js +41 -173
- package/dist/utils/yamlConverter.js +89 -16
- package/dist/utils/yamlLoader.d.ts +1 -1
- package/dist/utils/yamlLoader.js +6 -2
- package/dist/utilsController.d.ts +2 -1
- package/dist/utilsController.js +151 -22
- package/package.json +7 -5
- package/scripts/copy-templates.ts +23 -0
- package/src/adapters/AdapterFactory.ts +119 -143
- package/src/adapters/DatabaseAdapter.ts +18 -3
- package/src/adapters/LegacyAdapter.ts +236 -105
- package/src/adapters/TablesDBAdapter.ts +773 -643
- package/src/cli/commands/databaseCommands.ts +19 -19
- package/src/cli/commands/functionCommands.ts +23 -14
- package/src/collections/attributes.ts +2054 -1611
- package/src/collections/methods.ts +208 -293
- package/src/collections/tableOperations.ts +506 -0
- package/src/collections/transferOperations.ts +218 -144
- package/src/config/services/ConfigLoaderService.ts +62 -1
- package/src/examples/yamlTerminologyExample.ts +10 -5
- package/src/functions/deployments.ts +10 -35
- package/src/functions/fnConfigDiscovery.ts +103 -0
- package/src/functions/methods.ts +4 -2
- package/src/functions/pathResolution.ts +227 -0
- package/src/interactiveCLI.ts +25 -20
- package/src/main.ts +557 -202
- package/src/migrations/comprehensiveTransfer.ts +126 -50
- package/src/migrations/dataLoader.ts +3 -3
- package/src/migrations/importController.ts +3 -3
- package/src/migrations/services/ImportOrchestrator.ts +3 -3
- package/src/migrations/transfer.ts +148 -131
- package/src/shared/attributeMapper.ts +229 -0
- package/src/shared/selectionDialogs.ts +65 -32
- package/src/utils/configDiscovery.ts +9 -3
- package/src/utils/versionDetection.ts +74 -228
- package/src/utils/yamlConverter.ts +94 -17
- package/src/utils/yamlLoader.ts +11 -4
- package/src/utilsController.ts +202 -36
- package/dist/utils/schemaStrings.d.ts +0 -14
- package/dist/utils/schemaStrings.js +0 -428
- package/dist/utils/sessionPreservationExample.d.ts +0 -1666
- package/dist/utils/sessionPreservationExample.js +0 -101
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* 3. Fallback: Default to legacy mode for safety
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { logger } from '../shared/logging.js';
|
|
14
|
-
import { MessageFormatter } from '../shared/messageFormatter.js';
|
|
13
|
+
import { logger } from '../shared/logging.js';
|
|
14
|
+
import { MessageFormatter } from '../shared/messageFormatter.js';
|
|
15
|
+
import { Client, Databases, TablesDB, Query } from 'node-appwrite';
|
|
15
16
|
|
|
16
17
|
export type ApiMode = 'legacy' | 'tablesdb';
|
|
17
18
|
|
|
@@ -30,11 +31,11 @@ export interface VersionDetectionResult {
|
|
|
30
31
|
* @param apiKey - API key for authentication
|
|
31
32
|
* @returns Promise resolving to version detection result
|
|
32
33
|
*/
|
|
33
|
-
export async function detectAppwriteVersion(
|
|
34
|
-
endpoint: string,
|
|
35
|
-
project: string,
|
|
36
|
-
apiKey: string
|
|
37
|
-
): Promise<VersionDetectionResult> {
|
|
34
|
+
export async function detectAppwriteVersion(
|
|
35
|
+
endpoint: string,
|
|
36
|
+
project: string,
|
|
37
|
+
apiKey: string
|
|
38
|
+
): Promise<VersionDetectionResult> {
|
|
38
39
|
const startTime = Date.now();
|
|
39
40
|
// Clean endpoint URL
|
|
40
41
|
const cleanEndpoint = endpoint.replace(/\/$/, '');
|
|
@@ -62,199 +63,72 @@ export async function detectAppwriteVersion(
|
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
// STEP 2:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
logger.
|
|
102
|
-
endpoint: cleanEndpoint,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// Fallback to legacy mode
|
|
132
|
-
const fallbackResult = {
|
|
133
|
-
apiMode: 'legacy' as ApiMode,
|
|
134
|
-
detectionMethod: 'fallback' as const,
|
|
135
|
-
confidence: 'low' as const
|
|
136
|
-
};
|
|
66
|
+
// STEP 2: If version is unknown, use SDK probe (no fake HTTP endpoints)
|
|
67
|
+
try {
|
|
68
|
+
logger.debug('Attempting SDK-based TablesDB probe', {
|
|
69
|
+
endpoint: cleanEndpoint,
|
|
70
|
+
operation: 'detectAppwriteVersion'
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const client = new Client().setEndpoint(cleanEndpoint).setProject(project);
|
|
74
|
+
if (apiKey && apiKey.trim().length > 0) client.setKey(apiKey);
|
|
75
|
+
|
|
76
|
+
const databases = new Databases(client);
|
|
77
|
+
// Try to get a database id to probe tables listing
|
|
78
|
+
let dbId: string | undefined;
|
|
79
|
+
try {
|
|
80
|
+
const dbList: any = await databases.list([Query.limit(1)]);
|
|
81
|
+
dbId = dbList?.databases?.[0]?.$id || dbList?.databases?.[0]?.id || dbList?.[0]?.$id;
|
|
82
|
+
} catch (e) {
|
|
83
|
+
// Ignore, we'll still attempt a conservative probe
|
|
84
|
+
logger.debug('Databases.list probe failed or returned no items', { operation: 'detectAppwriteVersion' });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const tables = new TablesDB(client);
|
|
88
|
+
if (dbId) {
|
|
89
|
+
// Probe listTables for the first database (limit 1)
|
|
90
|
+
await tables.listTables({ databaseId: dbId, queries: [Query.limit(1)] });
|
|
91
|
+
} else {
|
|
92
|
+
// No databases to probe; assume TablesDB available (cannot falsify-positively without a db)
|
|
93
|
+
logger.debug('No databases found to probe tables; assuming TablesDB if SDK available', { operation: 'detectAppwriteVersion' });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const result: VersionDetectionResult = {
|
|
97
|
+
apiMode: 'tablesdb',
|
|
98
|
+
detectionMethod: 'endpoint_probe', // repurpose label for SDK probe
|
|
99
|
+
confidence: 'medium',
|
|
100
|
+
serverVersion: serverVersion || undefined
|
|
101
|
+
};
|
|
102
|
+
logger.info('TablesDB detected via SDK probe', {
|
|
103
|
+
endpoint: cleanEndpoint,
|
|
104
|
+
result,
|
|
105
|
+
operation: 'detectAppwriteVersion'
|
|
106
|
+
});
|
|
107
|
+
return result;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
110
|
+
logger.warn('SDK TablesDB probe failed; defaulting conservatively', {
|
|
111
|
+
endpoint: cleanEndpoint,
|
|
112
|
+
error: errorMessage,
|
|
113
|
+
operation: 'detectAppwriteVersion'
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Final fallback: default to tablesdb for modern environments when version unknown
|
|
118
|
+
const fallbackResult: VersionDetectionResult = {
|
|
119
|
+
apiMode: 'tablesdb',
|
|
120
|
+
detectionMethod: 'fallback',
|
|
121
|
+
confidence: 'low',
|
|
122
|
+
serverVersion: serverVersion || undefined
|
|
123
|
+
};
|
|
124
|
+
logger.info('Defaulting to TablesDB mode (fallback)', {
|
|
125
|
+
endpoint: cleanEndpoint,
|
|
126
|
+
result: fallbackResult,
|
|
127
|
+
operation: 'detectAppwriteVersion'
|
|
128
|
+
});
|
|
129
|
+
return fallbackResult;
|
|
130
|
+
}
|
|
137
131
|
|
|
138
|
-
logger.info('Falling back to legacy mode', {
|
|
139
|
-
endpoint: cleanEndpoint,
|
|
140
|
-
totalDuration: Date.now() - startTime,
|
|
141
|
-
result: fallbackResult,
|
|
142
|
-
operation: 'detectAppwriteVersion'
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
return fallbackResult;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Test TablesDB endpoint availability - most reliable detection method
|
|
150
|
-
*/
|
|
151
|
-
async function probeTablesDbEndpoint(
|
|
152
|
-
endpoint: string,
|
|
153
|
-
project: string,
|
|
154
|
-
apiKey: string
|
|
155
|
-
): Promise<VersionDetectionResult> {
|
|
156
|
-
const startTime = Date.now();
|
|
157
|
-
const url = `${endpoint}/tablesdb/`;
|
|
158
|
-
|
|
159
|
-
logger.debug('Probing TablesDB endpoint', {
|
|
160
|
-
url,
|
|
161
|
-
project,
|
|
162
|
-
operation: 'probeTablesDbEndpoint'
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
const response = await fetch(url, {
|
|
166
|
-
method: 'GET',
|
|
167
|
-
headers: {
|
|
168
|
-
'Content-Type': 'application/json',
|
|
169
|
-
'X-Appwrite-Project': project,
|
|
170
|
-
'X-Appwrite-Key': apiKey
|
|
171
|
-
},
|
|
172
|
-
// Short timeout for faster detection
|
|
173
|
-
signal: AbortSignal.timeout(5000)
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const duration = Date.now() - startTime;
|
|
177
|
-
|
|
178
|
-
logger.debug('TablesDB endpoint response received', {
|
|
179
|
-
url,
|
|
180
|
-
status: response.status,
|
|
181
|
-
statusText: response.statusText,
|
|
182
|
-
duration,
|
|
183
|
-
operation: 'probeTablesDbEndpoint'
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
if (response.ok) {
|
|
187
|
-
// ONLY 200 OK means TablesDB available
|
|
188
|
-
// 404 means endpoint doesn't exist (server < 1.8.0)
|
|
189
|
-
const result = {
|
|
190
|
-
apiMode: 'tablesdb' as ApiMode,
|
|
191
|
-
detectionMethod: 'endpoint_probe' as const,
|
|
192
|
-
confidence: 'high' as const
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
logger.info('TablesDB endpoint probe successful', {
|
|
196
|
-
url,
|
|
197
|
-
status: response.status,
|
|
198
|
-
result,
|
|
199
|
-
duration,
|
|
200
|
-
operation: 'probeTablesDbEndpoint'
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// 501 Not Implemented or other errors = no TablesDB support
|
|
207
|
-
const error = new Error(`TablesDB endpoint returned ${response.status}: ${response.statusText}`);
|
|
208
|
-
logger.debug('TablesDB endpoint probe failed', {
|
|
209
|
-
url,
|
|
210
|
-
status: response.status,
|
|
211
|
-
statusText: response.statusText,
|
|
212
|
-
duration,
|
|
213
|
-
operation: 'probeTablesDbEndpoint'
|
|
214
|
-
});
|
|
215
|
-
throw error;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* SDK capability detection as secondary method
|
|
220
|
-
*/
|
|
221
|
-
async function probeSdkCapabilities(): Promise<VersionDetectionResult> {
|
|
222
|
-
try {
|
|
223
|
-
// Try to import TablesDB SDK
|
|
224
|
-
let TablesDBModule;
|
|
225
|
-
try {
|
|
226
|
-
TablesDBModule = await import('node-appwrite-tablesdb');
|
|
227
|
-
} catch (importError) {
|
|
228
|
-
// TablesDB SDK not available, will fall back to legacy
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (TablesDBModule?.TablesDB) {
|
|
232
|
-
return {
|
|
233
|
-
apiMode: 'tablesdb',
|
|
234
|
-
detectionMethod: 'endpoint_probe',
|
|
235
|
-
confidence: 'medium'
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
} catch (error) {
|
|
239
|
-
// TablesDB SDK not available, assume legacy
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Check for legacy SDK availability
|
|
243
|
-
try {
|
|
244
|
-
const { Databases } = await import('node-appwrite');
|
|
245
|
-
if (Databases) {
|
|
246
|
-
return {
|
|
247
|
-
apiMode: 'legacy',
|
|
248
|
-
detectionMethod: 'endpoint_probe',
|
|
249
|
-
confidence: 'medium'
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
} catch (error) {
|
|
253
|
-
throw new Error('No Appwrite SDK available');
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
throw new Error('Unable to determine SDK capabilities');
|
|
257
|
-
}
|
|
258
132
|
|
|
259
133
|
/**
|
|
260
134
|
* Cached version detection to avoid repeated API calls
|
|
@@ -381,35 +255,7 @@ export function isCloudAppwriteEndpoint(endpoint: string): boolean {
|
|
|
381
255
|
* SDK feature detection as a fallback method
|
|
382
256
|
* Attempts to dynamically import TablesDB to check availability
|
|
383
257
|
*/
|
|
384
|
-
|
|
385
|
-
tablesDbAvailable: boolean;
|
|
386
|
-
legacyAvailable: boolean;
|
|
387
|
-
}> {
|
|
388
|
-
const result = {
|
|
389
|
-
tablesDbAvailable: false,
|
|
390
|
-
legacyAvailable: false
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
// Test TablesDB SDK availability
|
|
394
|
-
try {
|
|
395
|
-
const tablesModule = await import('node-appwrite-tablesdb');
|
|
396
|
-
if (tablesModule) {
|
|
397
|
-
result.tablesDbAvailable = true;
|
|
398
|
-
}
|
|
399
|
-
} catch (error) {
|
|
400
|
-
// TablesDB SDK not available
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// Test legacy SDK availability
|
|
404
|
-
try {
|
|
405
|
-
await import('node-appwrite');
|
|
406
|
-
result.legacyAvailable = true;
|
|
407
|
-
} catch (error) {
|
|
408
|
-
// Legacy SDK not available
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return result;
|
|
412
|
-
}
|
|
258
|
+
// Removed dynamic SDK capability checks to avoid confusion and side effects.
|
|
413
259
|
|
|
414
260
|
/**
|
|
415
261
|
* Clear version detection cache (useful for testing)
|
|
@@ -1,8 +1,95 @@
|
|
|
1
1
|
import yaml from "js-yaml";
|
|
2
2
|
import type { Collection, CollectionCreate } from "appwrite-utils";
|
|
3
|
+
import { Decimal } from "decimal.js";
|
|
3
4
|
|
|
4
|
-
//
|
|
5
|
-
const
|
|
5
|
+
// Extreme values that Appwrite may return, which should be treated as undefined
|
|
6
|
+
const EXTREME_MIN_INTEGER = -9223372036854776000;
|
|
7
|
+
const EXTREME_MAX_INTEGER = 9223372036854776000;
|
|
8
|
+
const EXTREME_MIN_FLOAT = -1.7976931348623157e+308;
|
|
9
|
+
const EXTREME_MAX_FLOAT = 1.7976931348623157e+308;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Type guard to check if an attribute has min/max properties
|
|
13
|
+
*/
|
|
14
|
+
const hasMinMaxProperties = (yamlAttr: any): boolean => {
|
|
15
|
+
return yamlAttr.type === 'integer' || yamlAttr.type === 'double' || yamlAttr.type === 'float';
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes min/max values for integer and float attributes using Decimal.js for precision
|
|
20
|
+
* Validates that min < max and handles extreme database values
|
|
21
|
+
*/
|
|
22
|
+
const normalizeMinMaxValues = (yamlAttr: any): { min?: number; max?: number } => {
|
|
23
|
+
if (!hasMinMaxProperties(yamlAttr)) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const { type, min, max, key } = yamlAttr;
|
|
28
|
+
let normalizedMin = min;
|
|
29
|
+
let normalizedMax = max;
|
|
30
|
+
|
|
31
|
+
// Handle min value - only filter out extreme database values
|
|
32
|
+
if (normalizedMin !== undefined && normalizedMin !== null) {
|
|
33
|
+
const minValue = Number(normalizedMin);
|
|
34
|
+
const originalMin = normalizedMin;
|
|
35
|
+
|
|
36
|
+
// Check if it's an extreme database value (but don't filter out large numbers)
|
|
37
|
+
if (type === 'integer') {
|
|
38
|
+
if (minValue === EXTREME_MIN_INTEGER) {
|
|
39
|
+
console.debug(`Min value normalized to undefined for attribute '${yamlAttr.key}': extreme database value`);
|
|
40
|
+
normalizedMin = undefined;
|
|
41
|
+
}
|
|
42
|
+
} else { // float/double
|
|
43
|
+
if (minValue === EXTREME_MIN_FLOAT) {
|
|
44
|
+
console.debug(`Min value normalized to undefined for attribute '${yamlAttr.key}': extreme database value`);
|
|
45
|
+
normalizedMin = undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Handle max value - only filter out extreme database values
|
|
51
|
+
if (normalizedMax !== undefined && normalizedMax !== null) {
|
|
52
|
+
const maxValue = Number(normalizedMax);
|
|
53
|
+
const originalMax = normalizedMax;
|
|
54
|
+
|
|
55
|
+
// Check if it's an extreme database value (but don't filter out large numbers)
|
|
56
|
+
if (type === 'integer') {
|
|
57
|
+
if (maxValue === EXTREME_MAX_INTEGER) {
|
|
58
|
+
console.debug(`Max value normalized to undefined for attribute '${yamlAttr.key}': extreme database value`);
|
|
59
|
+
normalizedMax = undefined;
|
|
60
|
+
}
|
|
61
|
+
} else { // float/double
|
|
62
|
+
if (maxValue === EXTREME_MAX_FLOAT) {
|
|
63
|
+
console.debug(`Max value normalized to undefined for attribute '${yamlAttr.key}': extreme database value`);
|
|
64
|
+
normalizedMax = undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate that min < max using Decimal.js for safe comparison
|
|
70
|
+
if (normalizedMin !== undefined && normalizedMax !== undefined &&
|
|
71
|
+
normalizedMin !== null && normalizedMax !== null) {
|
|
72
|
+
try {
|
|
73
|
+
const minDecimal = new Decimal(normalizedMin.toString());
|
|
74
|
+
const maxDecimal = new Decimal(normalizedMax.toString());
|
|
75
|
+
|
|
76
|
+
if (minDecimal.greaterThanOrEqualTo(maxDecimal)) {
|
|
77
|
+
// Swap values to ensure min < max (graceful handling)
|
|
78
|
+
console.warn(`Swapping min/max values for attribute '${yamlAttr.key}' to fix validation: min (${normalizedMin}) must be less than max (${normalizedMax})`);
|
|
79
|
+
const temp = normalizedMin;
|
|
80
|
+
normalizedMin = normalizedMax;
|
|
81
|
+
normalizedMax = temp;
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error(`Error comparing min/max values for attribute '${yamlAttr.key}':`, error);
|
|
85
|
+
// If Decimal comparison fails, set both to undefined to avoid API errors
|
|
86
|
+
normalizedMin = undefined;
|
|
87
|
+
normalizedMax = undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { min: normalizedMin, max: normalizedMax };
|
|
92
|
+
};
|
|
6
93
|
|
|
7
94
|
export interface YamlCollectionData {
|
|
8
95
|
name: string;
|
|
@@ -125,21 +212,11 @@ export function collectionToYaml(
|
|
|
125
212
|
|
|
126
213
|
if ('xdefault' in attr && attr.xdefault !== undefined) yamlAttr.default = attr.xdefault;
|
|
127
214
|
|
|
128
|
-
// Normalize min/max values
|
|
129
|
-
if ('min' in attr
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
yamlAttr.min = attr.min;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if ('max' in attr && attr.max !== undefined) {
|
|
138
|
-
const maxValue = Number(attr.max);
|
|
139
|
-
// Only include max if it's within reasonable range (< 1 trillion)
|
|
140
|
-
if (Math.abs(maxValue) < MIN_MAX_THRESHOLD) {
|
|
141
|
-
yamlAttr.max = attr.max;
|
|
142
|
-
}
|
|
215
|
+
// Normalize min/max values using Decimal.js precision
|
|
216
|
+
if ('min' in attr || 'max' in attr) {
|
|
217
|
+
const { min, max } = normalizeMinMaxValues(attr);
|
|
218
|
+
if (min !== undefined) yamlAttr.min = min;
|
|
219
|
+
if (max !== undefined) yamlAttr.max = max;
|
|
143
220
|
}
|
|
144
221
|
if ('elements' in attr && attr.elements !== undefined) yamlAttr.elements = attr.elements;
|
|
145
222
|
if ('relatedCollection' in attr && attr.relatedCollection !== undefined) yamlAttr.relatedCollection = attr.relatedCollection;
|
package/src/utils/yamlLoader.ts
CHANGED
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
type YamlCollectionData,
|
|
10
10
|
type YamlTerminologyConfig
|
|
11
11
|
} from "./yamlConverter.js";
|
|
12
|
-
import
|
|
12
|
+
import {
|
|
13
|
+
CollectionCreateSchema,
|
|
14
|
+
type CollectionCreate,
|
|
15
|
+
} from "appwrite-utils";
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Enhanced YAML loader with dual terminology support
|
|
@@ -149,7 +152,7 @@ export class YamlLoader {
|
|
|
149
152
|
// Always normalize to ensure consistent attribute terminology
|
|
150
153
|
const normalized = normalizeYamlData(yamlData);
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
const collectionInput: CollectionCreate = {
|
|
153
156
|
name: normalized.name,
|
|
154
157
|
$id: normalized.id || normalized.name.toLowerCase().replace(/\s+/g, '_'),
|
|
155
158
|
enabled: normalized.enabled !== false,
|
|
@@ -173,7 +176,9 @@ export class YamlLoader {
|
|
|
173
176
|
twoWay: attr.twoWay,
|
|
174
177
|
twoWayKey: attr.twoWayKey,
|
|
175
178
|
onDelete: attr.onDelete as any,
|
|
176
|
-
side: attr.side as any
|
|
179
|
+
side: attr.side as any,
|
|
180
|
+
encrypt: (attr as any).encrypt,
|
|
181
|
+
format: (attr as any).format
|
|
177
182
|
})) || [],
|
|
178
183
|
indexes: normalized.indexes?.map(idx => ({
|
|
179
184
|
key: idx.key,
|
|
@@ -183,6 +188,8 @@ export class YamlLoader {
|
|
|
183
188
|
})) || [],
|
|
184
189
|
importDefs: normalized.importDefs || []
|
|
185
190
|
};
|
|
191
|
+
|
|
192
|
+
return CollectionCreateSchema.parse(collectionInput);
|
|
186
193
|
}
|
|
187
194
|
|
|
188
195
|
/**
|
|
@@ -361,4 +368,4 @@ export class YamlLoader {
|
|
|
361
368
|
*/
|
|
362
369
|
export function createYamlLoader(baseDirectory: string): YamlLoader {
|
|
363
370
|
return new YamlLoader(baseDirectory);
|
|
364
|
-
}
|
|
371
|
+
}
|