appwrite-utils-cli 1.5.2 → 1.6.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 +199 -0
- package/README.md +251 -29
- package/dist/adapters/AdapterFactory.d.ts +10 -3
- package/dist/adapters/AdapterFactory.js +213 -17
- package/dist/adapters/TablesDBAdapter.js +60 -17
- package/dist/backups/operations/bucketBackup.d.ts +19 -0
- package/dist/backups/operations/bucketBackup.js +197 -0
- package/dist/backups/operations/collectionBackup.d.ts +30 -0
- package/dist/backups/operations/collectionBackup.js +201 -0
- package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
- package/dist/backups/operations/comprehensiveBackup.js +238 -0
- package/dist/backups/schemas/bucketManifest.d.ts +93 -0
- package/dist/backups/schemas/bucketManifest.js +33 -0
- package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
- package/dist/backups/schemas/comprehensiveManifest.js +32 -0
- package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
- package/dist/backups/tracking/centralizedTracking.js +274 -0
- package/dist/cli/commands/configCommands.d.ts +8 -0
- package/dist/cli/commands/configCommands.js +160 -0
- package/dist/cli/commands/databaseCommands.d.ts +13 -0
- package/dist/cli/commands/databaseCommands.js +479 -0
- package/dist/cli/commands/functionCommands.d.ts +7 -0
- package/dist/cli/commands/functionCommands.js +289 -0
- package/dist/cli/commands/schemaCommands.d.ts +7 -0
- package/dist/cli/commands/schemaCommands.js +134 -0
- package/dist/cli/commands/transferCommands.d.ts +5 -0
- package/dist/cli/commands/transferCommands.js +384 -0
- package/dist/collections/attributes.d.ts +5 -4
- package/dist/collections/attributes.js +539 -246
- package/dist/collections/indexes.js +39 -37
- package/dist/collections/methods.d.ts +2 -16
- package/dist/collections/methods.js +90 -538
- package/dist/collections/transferOperations.d.ts +7 -0
- package/dist/collections/transferOperations.js +331 -0
- package/dist/collections/wipeOperations.d.ts +16 -0
- package/dist/collections/wipeOperations.js +328 -0
- package/dist/config/configMigration.d.ts +87 -0
- package/dist/config/configMigration.js +390 -0
- package/dist/config/configValidation.d.ts +66 -0
- package/dist/config/configValidation.js +358 -0
- package/dist/config/yamlConfig.d.ts +455 -1
- package/dist/config/yamlConfig.js +145 -52
- package/dist/databases/methods.js +3 -2
- package/dist/databases/setup.d.ts +1 -2
- package/dist/databases/setup.js +9 -87
- package/dist/examples/yamlTerminologyExample.d.ts +42 -0
- package/dist/examples/yamlTerminologyExample.js +269 -0
- package/dist/functions/deployments.js +11 -10
- package/dist/functions/methods.d.ts +1 -1
- package/dist/functions/methods.js +5 -4
- package/dist/init.js +9 -9
- package/dist/interactiveCLI.d.ts +8 -17
- package/dist/interactiveCLI.js +209 -1172
- package/dist/main.js +364 -21
- package/dist/migrations/afterImportActions.js +22 -30
- package/dist/migrations/appwriteToX.js +71 -25
- package/dist/migrations/dataLoader.js +35 -26
- package/dist/migrations/importController.js +29 -30
- package/dist/migrations/relationships.js +13 -12
- package/dist/migrations/services/ImportOrchestrator.js +16 -19
- package/dist/migrations/transfer.js +46 -46
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +3 -1
- package/dist/migrations/yaml/YamlImportConfigLoader.js +6 -3
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +9 -3
- package/dist/migrations/yaml/YamlImportIntegration.js +22 -11
- package/dist/migrations/yaml/generateImportSchemas.d.ts +14 -1
- package/dist/migrations/yaml/generateImportSchemas.js +736 -7
- package/dist/schemas/authUser.d.ts +1 -1
- package/dist/setupController.js +3 -2
- package/dist/shared/backupMetadataSchema.d.ts +94 -0
- package/dist/shared/backupMetadataSchema.js +38 -0
- package/dist/shared/backupTracking.d.ts +18 -0
- package/dist/shared/backupTracking.js +176 -0
- package/dist/shared/confirmationDialogs.js +15 -15
- package/dist/shared/errorUtils.d.ts +54 -0
- package/dist/shared/errorUtils.js +95 -0
- package/dist/shared/functionManager.js +20 -19
- package/dist/shared/indexManager.js +12 -11
- package/dist/shared/jsonSchemaGenerator.js +10 -26
- package/dist/shared/logging.d.ts +51 -0
- package/dist/shared/logging.js +70 -0
- package/dist/shared/messageFormatter.d.ts +2 -0
- package/dist/shared/messageFormatter.js +10 -0
- package/dist/shared/migrationHelpers.d.ts +6 -16
- package/dist/shared/migrationHelpers.js +24 -21
- package/dist/shared/operationLogger.d.ts +8 -1
- package/dist/shared/operationLogger.js +11 -24
- package/dist/shared/operationQueue.d.ts +28 -1
- package/dist/shared/operationQueue.js +268 -66
- package/dist/shared/operationsTable.d.ts +26 -0
- package/dist/shared/operationsTable.js +286 -0
- package/dist/shared/operationsTableSchema.d.ts +48 -0
- package/dist/shared/operationsTableSchema.js +35 -0
- package/dist/shared/relationshipExtractor.d.ts +56 -0
- package/dist/shared/relationshipExtractor.js +138 -0
- package/dist/shared/schemaGenerator.d.ts +19 -1
- package/dist/shared/schemaGenerator.js +56 -75
- package/dist/storage/backupCompression.d.ts +20 -0
- package/dist/storage/backupCompression.js +67 -0
- package/dist/storage/methods.d.ts +16 -2
- package/dist/storage/methods.js +98 -14
- package/dist/users/methods.js +9 -8
- package/dist/utils/configDiscovery.d.ts +78 -0
- package/dist/utils/configDiscovery.js +430 -0
- package/dist/utils/directoryUtils.d.ts +22 -0
- package/dist/utils/directoryUtils.js +59 -0
- package/dist/utils/getClientFromConfig.d.ts +17 -8
- package/dist/utils/getClientFromConfig.js +162 -17
- package/dist/utils/helperFunctions.d.ts +16 -2
- package/dist/utils/helperFunctions.js +19 -5
- package/dist/utils/loadConfigs.d.ts +34 -9
- package/dist/utils/loadConfigs.js +236 -316
- package/dist/utils/pathResolvers.d.ts +53 -0
- package/dist/utils/pathResolvers.js +72 -0
- package/dist/utils/projectConfig.d.ts +119 -0
- package/dist/utils/projectConfig.js +171 -0
- package/dist/utils/retryFailedPromises.js +4 -2
- package/dist/utils/sessionAuth.d.ts +48 -0
- package/dist/utils/sessionAuth.js +164 -0
- package/dist/utils/sessionPreservationExample.d.ts +1666 -0
- package/dist/utils/sessionPreservationExample.js +101 -0
- package/dist/utils/setupFiles.js +301 -41
- package/dist/utils/typeGuards.d.ts +35 -0
- package/dist/utils/typeGuards.js +57 -0
- package/dist/utils/versionDetection.js +145 -9
- package/dist/utils/yamlConverter.d.ts +53 -3
- package/dist/utils/yamlConverter.js +232 -13
- package/dist/utils/yamlLoader.d.ts +70 -0
- package/dist/utils/yamlLoader.js +263 -0
- package/dist/utilsController.d.ts +36 -3
- package/dist/utilsController.js +186 -56
- package/package.json +12 -2
- package/src/adapters/AdapterFactory.ts +263 -35
- package/src/adapters/TablesDBAdapter.ts +225 -36
- package/src/backups/operations/bucketBackup.ts +277 -0
- package/src/backups/operations/collectionBackup.ts +310 -0
- package/src/backups/operations/comprehensiveBackup.ts +342 -0
- package/src/backups/schemas/bucketManifest.ts +78 -0
- package/src/backups/schemas/comprehensiveManifest.ts +76 -0
- package/src/backups/tracking/centralizedTracking.ts +352 -0
- package/src/cli/commands/configCommands.ts +194 -0
- package/src/cli/commands/databaseCommands.ts +635 -0
- package/src/cli/commands/functionCommands.ts +379 -0
- package/src/cli/commands/schemaCommands.ts +163 -0
- package/src/cli/commands/transferCommands.ts +457 -0
- package/src/collections/attributes.ts +900 -621
- package/src/collections/attributes.ts.backup +1555 -0
- package/src/collections/indexes.ts +116 -114
- package/src/collections/methods.ts +295 -968
- package/src/collections/transferOperations.ts +516 -0
- package/src/collections/wipeOperations.ts +501 -0
- package/src/config/README.md +274 -0
- package/src/config/configMigration.ts +575 -0
- package/src/config/configValidation.ts +445 -0
- package/src/config/yamlConfig.ts +168 -55
- package/src/databases/methods.ts +3 -2
- package/src/databases/setup.ts +11 -138
- package/src/examples/yamlTerminologyExample.ts +341 -0
- package/src/functions/deployments.ts +14 -12
- package/src/functions/methods.ts +11 -11
- package/src/functions/templates/hono-typescript/README.md +286 -0
- package/src/functions/templates/hono-typescript/package.json +26 -0
- package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/src/functions/templates/hono-typescript/src/app.ts +180 -0
- package/src/functions/templates/hono-typescript/src/context.ts +103 -0
- package/src/functions/templates/hono-typescript/src/index.ts +54 -0
- package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/src/functions/templates/hono-typescript/tsconfig.json +20 -0
- package/src/functions/templates/typescript-node/package.json +2 -1
- package/src/functions/templates/typescript-node/src/context.ts +103 -0
- package/src/functions/templates/typescript-node/src/index.ts +18 -12
- package/src/functions/templates/uv/pyproject.toml +1 -0
- package/src/functions/templates/uv/src/context.py +125 -0
- package/src/functions/templates/uv/src/index.py +35 -5
- package/src/init.ts +9 -11
- package/src/interactiveCLI.ts +274 -1563
- package/src/main.ts +418 -24
- package/src/migrations/afterImportActions.ts +71 -44
- package/src/migrations/appwriteToX.ts +100 -34
- package/src/migrations/dataLoader.ts +48 -34
- package/src/migrations/importController.ts +44 -39
- package/src/migrations/relationships.ts +28 -18
- package/src/migrations/services/ImportOrchestrator.ts +24 -27
- package/src/migrations/transfer.ts +159 -121
- package/src/migrations/yaml/YamlImportConfigLoader.ts +11 -4
- package/src/migrations/yaml/YamlImportIntegration.ts +47 -20
- package/src/migrations/yaml/generateImportSchemas.ts +751 -12
- package/src/setupController.ts +3 -2
- package/src/shared/backupMetadataSchema.ts +93 -0
- package/src/shared/backupTracking.ts +211 -0
- package/src/shared/confirmationDialogs.ts +19 -19
- package/src/shared/errorUtils.ts +110 -0
- package/src/shared/functionManager.ts +21 -20
- package/src/shared/indexManager.ts +12 -11
- package/src/shared/jsonSchemaGenerator.ts +38 -52
- package/src/shared/logging.ts +75 -0
- package/src/shared/messageFormatter.ts +14 -1
- package/src/shared/migrationHelpers.ts +45 -38
- package/src/shared/operationLogger.ts +11 -36
- package/src/shared/operationQueue.ts +322 -93
- package/src/shared/operationsTable.ts +338 -0
- package/src/shared/operationsTableSchema.ts +60 -0
- package/src/shared/relationshipExtractor.ts +214 -0
- package/src/shared/schemaGenerator.ts +179 -219
- package/src/storage/backupCompression.ts +88 -0
- package/src/storage/methods.ts +131 -34
- package/src/users/methods.ts +11 -9
- package/src/utils/configDiscovery.ts +502 -0
- package/src/utils/directoryUtils.ts +61 -0
- package/src/utils/getClientFromConfig.ts +205 -22
- package/src/utils/helperFunctions.ts +23 -5
- package/src/utils/loadConfigs.ts +313 -345
- package/src/utils/pathResolvers.ts +81 -0
- package/src/utils/projectConfig.ts +299 -0
- package/src/utils/retryFailedPromises.ts +4 -2
- package/src/utils/sessionAuth.ts +230 -0
- package/src/utils/setupFiles.ts +322 -54
- package/src/utils/typeGuards.ts +65 -0
- package/src/utils/versionDetection.ts +218 -64
- package/src/utils/yamlConverter.ts +296 -13
- package/src/utils/yamlLoader.ts +364 -0
- package/src/utilsController.ts +314 -110
- package/tests/README.md +497 -0
- package/tests/adapters/AdapterFactory.test.ts +277 -0
- package/tests/integration/syncOperations.test.ts +463 -0
- package/tests/jest.config.js +25 -0
- package/tests/migration/configMigration.test.ts +546 -0
- package/tests/setup.ts +62 -0
- package/tests/testUtils.ts +340 -0
- package/tests/utils/loadConfigs.test.ts +350 -0
- package/tests/validation/configValidation.test.ts +412 -0
- package/src/utils/schemaStrings.ts +0 -517
@@ -1,3 +1,10 @@
|
|
1
1
|
import type { Databases, Models } from "node-appwrite";
|
2
2
|
import type { OperationCreate } from "../storage/schemas.js";
|
3
|
-
|
3
|
+
/**
|
4
|
+
* Legacy operation logger - deprecated
|
5
|
+
* This function is maintained for backward compatibility but no longer performs any logging.
|
6
|
+
* The operations table system has been refactored to use the dynamic adapter pattern.
|
7
|
+
*
|
8
|
+
* @deprecated This function will be removed in a future version
|
9
|
+
*/
|
10
|
+
export declare const logOperation: (db: Databases, dbId: string, operationDetails: OperationCreate, operationId?: string) => Promise<Models.Document | null>;
|
@@ -1,25 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
operation = await tryAwaitWithRetry(async () => await db.updateDocument("migrations", "currentOperations", operationId, operationDetails));
|
13
|
-
}
|
14
|
-
else {
|
15
|
-
// Create new operation log
|
16
|
-
operation = await db.createDocument("migrations", "currentOperations", ulid(), operationDetails);
|
17
|
-
}
|
18
|
-
console.log(`Operation logged: ${operation.$id}`);
|
19
|
-
return operation;
|
20
|
-
}
|
21
|
-
catch (error) {
|
22
|
-
console.error(`Error logging operation: ${error}`);
|
23
|
-
throw error;
|
24
|
-
}
|
1
|
+
/**
|
2
|
+
* Legacy operation logger - deprecated
|
3
|
+
* This function is maintained for backward compatibility but no longer performs any logging.
|
4
|
+
* The operations table system has been refactored to use the dynamic adapter pattern.
|
5
|
+
*
|
6
|
+
* @deprecated This function will be removed in a future version
|
7
|
+
*/
|
8
|
+
export const logOperation = async (db, dbId, operationDetails, operationId) => {
|
9
|
+
// No-op: Operations logging has been moved to the new operations table system
|
10
|
+
// Callers should migrate to using operationsTable.ts functions directly
|
11
|
+
return null;
|
25
12
|
};
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { type Databases, type Models } from "node-appwrite";
|
2
2
|
import type { Attribute } from "appwrite-utils";
|
3
|
+
import type { DatabaseAdapter } from "../adapters/DatabaseAdapter.js";
|
3
4
|
export interface QueuedOperation {
|
4
5
|
type: "attribute";
|
5
6
|
collectionId?: string;
|
@@ -9,5 +10,31 @@ export interface QueuedOperation {
|
|
9
10
|
}
|
10
11
|
export declare const queuedOperations: QueuedOperation[];
|
11
12
|
export declare const nameToIdMapping: Map<string, string>;
|
13
|
+
export declare const processedCollections: Set<string>;
|
14
|
+
export declare const processedAttributes: Set<string>;
|
12
15
|
export declare const enqueueOperation: (operation: QueuedOperation) => void;
|
13
|
-
|
16
|
+
/**
|
17
|
+
* Clear all caches and processing state - use between operations
|
18
|
+
*/
|
19
|
+
export declare const clearProcessingState: () => void;
|
20
|
+
/**
|
21
|
+
* Check if a collection has already been fully processed
|
22
|
+
*/
|
23
|
+
export declare const isCollectionProcessed: (collectionId: string) => boolean;
|
24
|
+
/**
|
25
|
+
* Mark a collection as fully processed
|
26
|
+
*/
|
27
|
+
export declare const markCollectionProcessed: (collectionId: string, collectionName?: string) => void;
|
28
|
+
/**
|
29
|
+
* Check if a specific attribute has been processed
|
30
|
+
*/
|
31
|
+
export declare const isAttributeProcessed: (collectionId: string, attributeKey: string) => boolean;
|
32
|
+
/**
|
33
|
+
* Mark a specific attribute as processed
|
34
|
+
*/
|
35
|
+
export declare const markAttributeProcessed: (collectionId: string, attributeKey: string) => void;
|
36
|
+
/**
|
37
|
+
* Process only specific attributes in the queue, not entire collections
|
38
|
+
* This prevents triggering full collection re-processing cycles
|
39
|
+
*/
|
40
|
+
export declare const processQueue: (db: Databases | DatabaseAdapter, dbId: string) => Promise<void>;
|
@@ -2,100 +2,302 @@ import { Query } from "node-appwrite";
|
|
2
2
|
import { createOrUpdateAttributeWithStatusCheck } from "../collections/attributes.js";
|
3
3
|
import { fetchAndCacheCollectionByName } from "../collections/methods.js";
|
4
4
|
import { tryAwaitWithRetry } from "../utils/helperFunctions.js";
|
5
|
-
import
|
5
|
+
import { logger } from "../shared/logging.js";
|
6
|
+
import { MessageFormatter } from "../shared/messageFormatter.js";
|
7
|
+
// Global state management
|
6
8
|
export const queuedOperations = [];
|
7
9
|
export const nameToIdMapping = new Map();
|
10
|
+
export const processedCollections = new Set();
|
11
|
+
export const processedAttributes = new Set(); // format: "collectionId:attributeKey"
|
8
12
|
export const enqueueOperation = (operation) => {
|
13
|
+
// Avoid duplicate queue entries for same attribute
|
14
|
+
const attributeKey = operation.attribute?.key;
|
15
|
+
const collectionId = operation.collectionId;
|
16
|
+
logger.info('Enqueueing operation', {
|
17
|
+
type: operation.type,
|
18
|
+
attributeKey,
|
19
|
+
collectionId,
|
20
|
+
dependencies: operation.dependencies,
|
21
|
+
queueSizeBefore: queuedOperations.length,
|
22
|
+
operation: 'enqueueOperation'
|
23
|
+
});
|
24
|
+
if (attributeKey && collectionId) {
|
25
|
+
const duplicateIndex = queuedOperations.findIndex((op) => op.collectionId === collectionId && op.attribute?.key === attributeKey);
|
26
|
+
if (duplicateIndex !== -1) {
|
27
|
+
MessageFormatter.info(`Replacing existing queue entry for attribute: ${attributeKey}`);
|
28
|
+
logger.info('Replacing duplicate queue entry', {
|
29
|
+
attributeKey,
|
30
|
+
collectionId,
|
31
|
+
duplicateIndex,
|
32
|
+
operation: 'enqueueOperation'
|
33
|
+
});
|
34
|
+
queuedOperations[duplicateIndex] = operation;
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
}
|
9
38
|
queuedOperations.push(operation);
|
39
|
+
logger.debug('Operation enqueued successfully', {
|
40
|
+
attributeKey,
|
41
|
+
collectionId,
|
42
|
+
queueSizeAfter: queuedOperations.length,
|
43
|
+
operation: 'enqueueOperation'
|
44
|
+
});
|
45
|
+
};
|
46
|
+
/**
|
47
|
+
* Clear all caches and processing state - use between operations
|
48
|
+
*/
|
49
|
+
export const clearProcessingState = () => {
|
50
|
+
const sizeBefore = {
|
51
|
+
collections: processedCollections.size,
|
52
|
+
attributes: processedAttributes.size,
|
53
|
+
nameMapping: nameToIdMapping.size
|
54
|
+
};
|
55
|
+
processedCollections.clear();
|
56
|
+
processedAttributes.clear();
|
57
|
+
nameToIdMapping.clear();
|
58
|
+
MessageFormatter.success("Cleared processing state caches");
|
59
|
+
logger.info('Processing state cleared', {
|
60
|
+
sizeBefore,
|
61
|
+
operation: 'clearProcessingState'
|
62
|
+
});
|
63
|
+
};
|
64
|
+
/**
|
65
|
+
* Check if a collection has already been fully processed
|
66
|
+
*/
|
67
|
+
export const isCollectionProcessed = (collectionId) => {
|
68
|
+
return processedCollections.has(collectionId);
|
69
|
+
};
|
70
|
+
/**
|
71
|
+
* Mark a collection as fully processed
|
72
|
+
*/
|
73
|
+
export const markCollectionProcessed = (collectionId, collectionName) => {
|
74
|
+
processedCollections.add(collectionId);
|
75
|
+
const logData = {
|
76
|
+
collectionId,
|
77
|
+
collectionName,
|
78
|
+
totalProcessedCollections: processedCollections.size,
|
79
|
+
operation: 'markCollectionProcessed'
|
80
|
+
};
|
81
|
+
if (collectionName) {
|
82
|
+
MessageFormatter.success(`Marked collection '${collectionName}' (${collectionId}) as processed`);
|
83
|
+
}
|
84
|
+
logger.info('Collection marked as processed', logData);
|
85
|
+
};
|
86
|
+
/**
|
87
|
+
* Check if a specific attribute has been processed
|
88
|
+
*/
|
89
|
+
export const isAttributeProcessed = (collectionId, attributeKey) => {
|
90
|
+
return processedAttributes.has(`${collectionId}:${attributeKey}`);
|
10
91
|
};
|
92
|
+
/**
|
93
|
+
* Mark a specific attribute as processed
|
94
|
+
*/
|
95
|
+
export const markAttributeProcessed = (collectionId, attributeKey) => {
|
96
|
+
const identifier = `${collectionId}:${attributeKey}`;
|
97
|
+
processedAttributes.add(identifier);
|
98
|
+
logger.debug('Attribute marked as processed', {
|
99
|
+
collectionId,
|
100
|
+
attributeKey,
|
101
|
+
identifier,
|
102
|
+
totalProcessedAttributes: processedAttributes.size,
|
103
|
+
operation: 'markAttributeProcessed'
|
104
|
+
});
|
105
|
+
};
|
106
|
+
/**
|
107
|
+
* Process only specific attributes in the queue, not entire collections
|
108
|
+
* This prevents triggering full collection re-processing cycles
|
109
|
+
*/
|
11
110
|
export const processQueue = async (db, dbId) => {
|
12
|
-
|
13
|
-
|
14
|
-
|
111
|
+
const startTime = Date.now();
|
112
|
+
if (queuedOperations.length === 0) {
|
113
|
+
MessageFormatter.info("No queued operations to process");
|
114
|
+
logger.info('Queue processing skipped - no operations', {
|
115
|
+
dbId,
|
116
|
+
operation: 'processQueue'
|
117
|
+
});
|
118
|
+
return;
|
119
|
+
}
|
120
|
+
MessageFormatter.section(`Starting surgical queue processing of ${queuedOperations.length} operations for ${dbId}`);
|
121
|
+
logger.info('Starting queue processing', {
|
122
|
+
dbId,
|
123
|
+
queueSize: queuedOperations.length,
|
124
|
+
operations: queuedOperations.map(op => ({
|
125
|
+
type: op.type,
|
126
|
+
attributeKey: op.attribute?.key,
|
127
|
+
collectionId: op.collectionId,
|
128
|
+
dependencies: op.dependencies
|
129
|
+
})),
|
130
|
+
operation: 'processQueue'
|
131
|
+
});
|
15
132
|
let progress = true;
|
16
|
-
|
133
|
+
let attempts = 0;
|
134
|
+
const maxAttempts = 3; // Prevent infinite loops
|
135
|
+
while (progress && attempts < maxAttempts) {
|
17
136
|
progress = false;
|
18
|
-
|
19
|
-
|
137
|
+
attempts++;
|
138
|
+
MessageFormatter.info(`Queue processing attempt ${attempts}/${maxAttempts}`);
|
139
|
+
logger.info('Queue processing attempt started', {
|
140
|
+
attempt: attempts,
|
141
|
+
maxAttempts,
|
142
|
+
remainingOperations: queuedOperations.length,
|
143
|
+
dbId,
|
144
|
+
operation: 'processQueue'
|
145
|
+
});
|
146
|
+
for (let i = queuedOperations.length - 1; i >= 0; i--) {
|
20
147
|
const operation = queuedOperations[i];
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
148
|
+
if (!operation.attribute || !operation.collectionId) {
|
149
|
+
MessageFormatter.warning("Invalid operation, removing from queue");
|
150
|
+
queuedOperations.splice(i, 1);
|
151
|
+
continue;
|
152
|
+
}
|
153
|
+
const attributeKey = operation.attribute.key;
|
154
|
+
const collectionId = operation.collectionId;
|
155
|
+
// Skip if this specific attribute was already processed
|
156
|
+
if (isAttributeProcessed(collectionId, attributeKey)) {
|
157
|
+
MessageFormatter.debug(`Attribute '${attributeKey}' already processed, removing from queue`);
|
158
|
+
logger.debug('Removing already processed attribute from queue', {
|
159
|
+
attributeKey,
|
160
|
+
collectionId,
|
161
|
+
queueIndex: i,
|
162
|
+
operation: 'processQueue'
|
163
|
+
});
|
164
|
+
queuedOperations.splice(i, 1);
|
165
|
+
continue;
|
166
|
+
}
|
167
|
+
let targetCollection;
|
168
|
+
// Resolve the target collection (where the attribute will be created)
|
169
|
+
try {
|
170
|
+
targetCollection = await tryAwaitWithRetry(async () => {
|
171
|
+
if ('getMetadata' in db && typeof db.getMetadata === 'function') {
|
172
|
+
// DatabaseAdapter
|
173
|
+
return (await db.getTable({ databaseId: dbId, tableId: collectionId })).data;
|
29
174
|
}
|
30
|
-
|
31
|
-
|
175
|
+
else {
|
176
|
+
// Legacy Databases
|
177
|
+
return await db.getCollection(dbId, collectionId);
|
32
178
|
}
|
33
|
-
}
|
34
|
-
|
35
|
-
|
36
|
-
|
179
|
+
});
|
180
|
+
}
|
181
|
+
catch (e) {
|
182
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
183
|
+
MessageFormatter.error(`Target collection ${collectionId} not found, removing from queue`);
|
184
|
+
logger.error('Target collection not found during queue processing', {
|
185
|
+
collectionId,
|
186
|
+
attributeKey,
|
187
|
+
error: errorMessage,
|
188
|
+
operation: 'processQueue'
|
189
|
+
});
|
190
|
+
queuedOperations.splice(i, 1);
|
191
|
+
continue;
|
192
|
+
}
|
193
|
+
// For relationship attributes, ensure the related collection exists
|
194
|
+
let canProcess = true;
|
195
|
+
if (operation.attribute.type === "relationship") {
|
196
|
+
const relatedCollection = operation.attribute.relatedCollection;
|
197
|
+
if (relatedCollection) {
|
198
|
+
// Try to resolve related collection by ID first, then by name
|
199
|
+
let relatedFound = false;
|
37
200
|
try {
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
201
|
+
await tryAwaitWithRetry(async () => {
|
202
|
+
if ('getMetadata' in db && typeof db.getMetadata === 'function') {
|
203
|
+
// DatabaseAdapter
|
204
|
+
return (await db.getTable({ databaseId: dbId, tableId: relatedCollection })).data;
|
205
|
+
}
|
206
|
+
else {
|
207
|
+
// Legacy Databases
|
208
|
+
return await db.getCollection(dbId, relatedCollection);
|
209
|
+
}
|
210
|
+
});
|
211
|
+
relatedFound = true;
|
212
|
+
nameToIdMapping.set(relatedCollection, relatedCollection); // Cache the ID mapping
|
43
213
|
}
|
44
214
|
catch (_) {
|
45
|
-
//
|
215
|
+
// Try by name lookup
|
216
|
+
const cachedId = nameToIdMapping.get(relatedCollection);
|
217
|
+
if (cachedId) {
|
218
|
+
try {
|
219
|
+
await tryAwaitWithRetry(async () => {
|
220
|
+
if ('getMetadata' in db && typeof db.getMetadata === 'function') {
|
221
|
+
// DatabaseAdapter
|
222
|
+
return (await db.getTable({ databaseId: dbId, tableId: cachedId })).data;
|
223
|
+
}
|
224
|
+
else {
|
225
|
+
// Legacy Databases
|
226
|
+
return await db.getCollection(dbId, cachedId);
|
227
|
+
}
|
228
|
+
});
|
229
|
+
relatedFound = true;
|
230
|
+
}
|
231
|
+
catch (_) {
|
232
|
+
nameToIdMapping.delete(relatedCollection); // Remove stale cache
|
233
|
+
}
|
234
|
+
}
|
235
|
+
if (!relatedFound) {
|
236
|
+
// Final attempt: search by name
|
237
|
+
try {
|
238
|
+
const collections = 'getMetadata' in db && typeof db.getMetadata === 'function'
|
239
|
+
? await db.listTables({ databaseId: dbId, queries: [Query.equal("name", relatedCollection)] })
|
240
|
+
: await db.listCollections(dbId, [Query.equal("name", relatedCollection)]);
|
241
|
+
if (collections.total && collections.total > 0) {
|
242
|
+
const firstCollection = 'getMetadata' in db && typeof db.getMetadata === 'function'
|
243
|
+
? collections.tables?.[0]
|
244
|
+
: collections.collections?.[0];
|
245
|
+
nameToIdMapping.set(relatedCollection, firstCollection.$id);
|
246
|
+
relatedFound = true;
|
247
|
+
}
|
248
|
+
}
|
249
|
+
catch (_) {
|
250
|
+
// Related collection truly doesn't exist yet
|
251
|
+
}
|
252
|
+
}
|
46
253
|
}
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
}
|
51
|
-
// Handle dependencies if collection still not found
|
52
|
-
if (!collectionFound) {
|
53
|
-
for (const dep of operation.dependencies || []) {
|
54
|
-
collectionFound = await fetchAndCacheCollectionByName(db, dbId, dep);
|
55
|
-
if (collectionFound)
|
56
|
-
break; // Break early if collection is found
|
254
|
+
if (!relatedFound) {
|
255
|
+
MessageFormatter.warning(`Related collection '${relatedCollection}' not ready for attribute '${attributeKey}', keeping in queue`);
|
256
|
+
canProcess = false;
|
57
257
|
}
|
58
258
|
}
|
59
259
|
}
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
try {
|
64
|
-
collectionFound = await tryAwaitWithRetry(async () => await db.getCollection(dbId, operation.collectionId));
|
65
|
-
}
|
66
|
-
catch (e) {
|
67
|
-
console.log(`\tCollection not found by ID: ${operation.collectionId}`);
|
68
|
-
}
|
69
|
-
}
|
70
|
-
// Process the operation if the collection is found
|
71
|
-
if (collectionFound && operation.attribute) {
|
72
|
-
console.log(chalk.cyan(`\t📋 Queue processing relationship attribute: ${operation.attribute.key} for collection: ${collectionFound.name}`));
|
73
|
-
const success = await createOrUpdateAttributeWithStatusCheck(db, dbId, collectionFound, operation.attribute);
|
260
|
+
if (canProcess && targetCollection) {
|
261
|
+
MessageFormatter.progress(`Processing queued ${operation.attribute.type} attribute: '${attributeKey}' for collection: '${targetCollection.name}'`);
|
262
|
+
const success = await createOrUpdateAttributeWithStatusCheck(db, dbId, targetCollection, operation.attribute);
|
74
263
|
if (success) {
|
75
|
-
|
264
|
+
MessageFormatter.success(`Successfully processed queued attribute: '${attributeKey}'`);
|
265
|
+
logger.info('Queued attribute processed successfully', {
|
266
|
+
attributeKey,
|
267
|
+
collectionId,
|
268
|
+
targetCollectionName: targetCollection.name,
|
269
|
+
operation: 'processQueue'
|
270
|
+
});
|
271
|
+
markAttributeProcessed(collectionId, attributeKey);
|
76
272
|
queuedOperations.splice(i, 1);
|
77
|
-
i--; // Adjust index since we're modifying the array
|
78
273
|
progress = true;
|
79
274
|
}
|
80
275
|
else {
|
81
|
-
|
276
|
+
MessageFormatter.error(`Failed to process queued attribute: '${attributeKey}', removing from queue`);
|
277
|
+
logger.error('Failed to process queued attribute', {
|
278
|
+
attributeKey,
|
279
|
+
collectionId,
|
280
|
+
targetCollectionName: targetCollection.name,
|
281
|
+
operation: 'processQueue'
|
282
|
+
});
|
82
283
|
queuedOperations.splice(i, 1);
|
83
|
-
i--; // Adjust index since we're modifying the array
|
84
284
|
}
|
85
285
|
}
|
86
|
-
else {
|
87
|
-
console.log(chalk.yellow(`\t⚠️ Collection not found for queued operation, removing from queue: ${operation.attribute?.key || 'unknown'}`));
|
88
|
-
queuedOperations.splice(i, 1);
|
89
|
-
i--; // Adjust index since we're modifying the array
|
90
|
-
}
|
91
286
|
}
|
92
|
-
|
287
|
+
if (queuedOperations.length === 0) {
|
288
|
+
break;
|
289
|
+
}
|
290
|
+
MessageFormatter.info(`Remaining operations after attempt ${attempts}: ${queuedOperations.length}`);
|
93
291
|
}
|
94
292
|
if (queuedOperations.length > 0) {
|
95
|
-
|
96
|
-
|
293
|
+
MessageFormatter.warning(`${queuedOperations.length} operations remain unresolved after ${maxAttempts} attempts:`);
|
294
|
+
queuedOperations.forEach((op, index) => {
|
295
|
+
MessageFormatter.warning(` ${index + 1}. ${op.attribute?.type} attribute '${op.attribute?.key}' for collection ${op.collectionId}`);
|
296
|
+
});
|
297
|
+
MessageFormatter.warning("These may have unmet dependencies or require manual intervention");
|
298
|
+
}
|
299
|
+
else {
|
300
|
+
MessageFormatter.success("All queued operations processed successfully");
|
97
301
|
}
|
98
|
-
|
99
|
-
console.log(`Queue processing complete for ${dbId}`);
|
100
|
-
console.log("---------------------------------");
|
302
|
+
MessageFormatter.section(`Surgical queue processing complete for ${dbId}`);
|
101
303
|
};
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import type { DatabaseAdapter } from "../adapters/DatabaseAdapter.js";
|
2
|
+
import { type OperationRecord } from "./operationsTableSchema.js";
|
3
|
+
/**
|
4
|
+
* Creates the operations tracking table in the specified database
|
5
|
+
* Table is created with underscore prefix to indicate it's a system table
|
6
|
+
*/
|
7
|
+
export declare function createOperationsTable(db: DatabaseAdapter, databaseId: string): Promise<void>;
|
8
|
+
/**
|
9
|
+
* Finds an existing operation or creates a new one
|
10
|
+
* Useful for resuming interrupted operations
|
11
|
+
*/
|
12
|
+
export declare function findOrCreateOperation(db: DatabaseAdapter, databaseId: string, operationType: string, params?: Partial<OperationRecord>): Promise<OperationRecord>;
|
13
|
+
/**
|
14
|
+
* Updates an existing operation record
|
15
|
+
*/
|
16
|
+
export declare function updateOperation(db: DatabaseAdapter, databaseId: string, operationId: string, updates: Partial<OperationRecord>): Promise<OperationRecord>;
|
17
|
+
/**
|
18
|
+
* Gets a single operation by ID
|
19
|
+
*/
|
20
|
+
export declare function getOperation(db: DatabaseAdapter, databaseId: string, operationId: string): Promise<OperationRecord | null>;
|
21
|
+
/**
|
22
|
+
* Cleans up old completed operations
|
23
|
+
* @param olderThan - Optional date, defaults to operations older than 7 days
|
24
|
+
* @returns Number of operations deleted
|
25
|
+
*/
|
26
|
+
export declare function cleanupOperations(db: DatabaseAdapter, databaseId: string, olderThan?: Date): Promise<number>;
|