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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { AppwriteFunctionSchema, type AppwriteFunction } from 'appwrite-utils';
|
|
6
|
+
import { shouldIgnoreDirectory } from '../utils/directoryUtils.js';
|
|
7
|
+
import { MessageFormatter } from '../shared/messageFormatter.js';
|
|
8
|
+
|
|
9
|
+
function findGitRoot(startDir: string): string {
|
|
10
|
+
let dir = path.resolve(startDir);
|
|
11
|
+
while (dir !== path.parse(dir).root) {
|
|
12
|
+
if (fs.existsSync(path.join(dir, '.git'))) return dir;
|
|
13
|
+
const parent = path.dirname(dir);
|
|
14
|
+
if (parent === dir) break;
|
|
15
|
+
dir = parent;
|
|
16
|
+
}
|
|
17
|
+
return path.resolve(startDir);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function expandTilde(p: string): string {
|
|
21
|
+
if (!p) return p;
|
|
22
|
+
if (p === '~' || p.startsWith('~/')) return p.replace(/^~(?=$|\/|\\)/, homedir());
|
|
23
|
+
return p;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function discoverFnConfigs(startDir: string): AppwriteFunction[] {
|
|
27
|
+
const root = findGitRoot(startDir);
|
|
28
|
+
const results: AppwriteFunction[] = [];
|
|
29
|
+
|
|
30
|
+
const visit = (dir: string, depth = 0) => {
|
|
31
|
+
if (depth > 5) return; // cap depth
|
|
32
|
+
const base = path.basename(dir);
|
|
33
|
+
if (shouldIgnoreDirectory(base)) return;
|
|
34
|
+
let entries: fs.Dirent[] = [];
|
|
35
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
36
|
+
|
|
37
|
+
// Check for .fnconfig.yaml / .fnconfig.yml
|
|
38
|
+
for (const fname of ['.fnconfig.yaml', '.fnconfig.yml']) {
|
|
39
|
+
const cfgPath = path.join(dir, fname);
|
|
40
|
+
if (fs.existsSync(cfgPath)) {
|
|
41
|
+
try {
|
|
42
|
+
const raw = fs.readFileSync(cfgPath, 'utf8');
|
|
43
|
+
const data = yaml.load(raw) as any;
|
|
44
|
+
const parsed = AppwriteFunctionSchema.parse({
|
|
45
|
+
$id: data.id || data.$id,
|
|
46
|
+
name: data.name,
|
|
47
|
+
runtime: data.runtime,
|
|
48
|
+
execute: data.execute || [],
|
|
49
|
+
events: data.events || [],
|
|
50
|
+
schedule: data.schedule,
|
|
51
|
+
timeout: data.timeout,
|
|
52
|
+
enabled: data.enabled,
|
|
53
|
+
logging: data.logging,
|
|
54
|
+
entrypoint: data.entrypoint,
|
|
55
|
+
commands: data.commands,
|
|
56
|
+
scopes: data.scopes,
|
|
57
|
+
installationId: data.installationId,
|
|
58
|
+
providerRepositoryId: data.providerRepositoryId,
|
|
59
|
+
providerBranch: data.providerBranch,
|
|
60
|
+
providerSilentMode: data.providerSilentMode,
|
|
61
|
+
providerRootDirectory: data.providerRootDirectory,
|
|
62
|
+
templateRepository: data.templateRepository,
|
|
63
|
+
templateOwner: data.templateOwner,
|
|
64
|
+
templateRootDirectory: data.templateRootDirectory,
|
|
65
|
+
templateVersion: data.templateVersion,
|
|
66
|
+
specification: data.specification,
|
|
67
|
+
dirPath: data.dirPath,
|
|
68
|
+
predeployCommands: data.predeployCommands,
|
|
69
|
+
deployDir: data.deployDir,
|
|
70
|
+
ignore: data.ignore,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Resolve dirPath relative to the config file directory
|
|
74
|
+
let dirPath = parsed.dirPath || '.';
|
|
75
|
+
dirPath = expandTilde(dirPath);
|
|
76
|
+
if (!path.isAbsolute(dirPath)) dirPath = path.resolve(path.dirname(cfgPath), dirPath);
|
|
77
|
+
const merged: AppwriteFunction = { ...parsed, dirPath };
|
|
78
|
+
results.push(merged);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
MessageFormatter.warning(`Failed to parse ${cfgPath}: ${e instanceof Error ? e.message : String(e)}`, { prefix: 'Functions' });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
if (entry.isDirectory()) visit(path.join(dir, entry.name), depth + 1);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
visit(root, 0);
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function mergeDiscoveredFunctions(
|
|
95
|
+
central: AppwriteFunction[] = [],
|
|
96
|
+
discovered: AppwriteFunction[] = []
|
|
97
|
+
): AppwriteFunction[] {
|
|
98
|
+
const map = new Map<string, AppwriteFunction>();
|
|
99
|
+
for (const f of central) if (f?.$id) map.set(f.$id, f);
|
|
100
|
+
for (const f of discovered) if (f?.$id) map.set(f.$id, f); // discovered overrides
|
|
101
|
+
return Array.from(map.values());
|
|
102
|
+
}
|
|
103
|
+
|
package/src/functions/methods.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import chalk from "chalk";
|
|
18
18
|
import { extract as extractTar } from "tar";
|
|
19
19
|
import { MessageFormatter } from "../shared/messageFormatter.js";
|
|
20
|
+
import { expandTildePath, normalizeFunctionName } from "./pathResolution.js";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Validates and filters events array for Appwrite functions
|
|
@@ -72,7 +73,7 @@ export const downloadLatestFunctionDeployment = async (
|
|
|
72
73
|
// Create function directory using provided basePath
|
|
73
74
|
const functionDir = join(
|
|
74
75
|
basePath,
|
|
75
|
-
functionInfo.name
|
|
76
|
+
normalizeFunctionName(functionInfo.name)
|
|
76
77
|
);
|
|
77
78
|
await fs.promises.mkdir(functionDir, { recursive: true });
|
|
78
79
|
|
|
@@ -219,7 +220,8 @@ export const createFunctionTemplate = async (
|
|
|
219
220
|
functionName: string,
|
|
220
221
|
basePath: string = "./functions"
|
|
221
222
|
) => {
|
|
222
|
-
const
|
|
223
|
+
const expandedBasePath = expandTildePath(basePath);
|
|
224
|
+
const functionPath = join(expandedBasePath, functionName);
|
|
223
225
|
const currentFileUrl = import.meta.url;
|
|
224
226
|
const currentDir = dirname(fileURLToPath(currentFileUrl));
|
|
225
227
|
const templatesPath = join(currentDir, "templates", templateType);
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { existsSync, statSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, resolve, isAbsolute } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { MessageFormatter } from '../shared/messageFormatter.js';
|
|
5
|
+
import { logger } from '../shared/logging.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Expands tilde (~) in paths to the user's home directory
|
|
9
|
+
* @param pathStr - Path string that may contain ~
|
|
10
|
+
* @returns Expanded path with home directory
|
|
11
|
+
*/
|
|
12
|
+
export function expandTildePath(pathStr: string): string {
|
|
13
|
+
if (!pathStr) return pathStr;
|
|
14
|
+
|
|
15
|
+
if (pathStr.startsWith('~/') || pathStr === '~') {
|
|
16
|
+
const expandedPath = pathStr.replace(/^~(?=$|\/|\\)/, homedir());
|
|
17
|
+
logger.debug('Expanded tilde path', { original: pathStr, expanded: expandedPath });
|
|
18
|
+
return expandedPath;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return pathStr;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes function name to standard format (lowercase, dashes instead of spaces)
|
|
26
|
+
* @param name - Function name to normalize
|
|
27
|
+
* @returns Normalized function name
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeFunctionName(name: string): string {
|
|
30
|
+
if (!name) return name;
|
|
31
|
+
|
|
32
|
+
const normalized = name.toLowerCase().replace(/\s+/g, '-');
|
|
33
|
+
|
|
34
|
+
if (normalized !== name) {
|
|
35
|
+
logger.debug('Normalized function name', { original: name, normalized });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return normalized;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validates that a directory exists and contains function markers
|
|
43
|
+
* @param dirPath - Directory path to validate
|
|
44
|
+
* @returns True if directory is a valid function directory
|
|
45
|
+
*/
|
|
46
|
+
export function validateFunctionDirectory(dirPath: string): boolean {
|
|
47
|
+
try {
|
|
48
|
+
// Check if directory exists
|
|
49
|
+
if (!existsSync(dirPath)) {
|
|
50
|
+
logger.debug('Directory does not exist', { dirPath });
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check if it's actually a directory
|
|
55
|
+
const stats = statSync(dirPath);
|
|
56
|
+
if (!stats.isDirectory()) {
|
|
57
|
+
logger.debug('Path is not a directory', { dirPath });
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check for function markers
|
|
62
|
+
const contents = readdirSync(dirPath);
|
|
63
|
+
const hasPackageJson = contents.includes('package.json');
|
|
64
|
+
const hasPyprojectToml = contents.includes('pyproject.toml');
|
|
65
|
+
const hasSrcDir = contents.includes('src');
|
|
66
|
+
|
|
67
|
+
const isValid = hasPackageJson || hasPyprojectToml || hasSrcDir;
|
|
68
|
+
|
|
69
|
+
logger.debug('Function directory validation', {
|
|
70
|
+
dirPath,
|
|
71
|
+
isValid,
|
|
72
|
+
markers: {
|
|
73
|
+
hasPackageJson,
|
|
74
|
+
hasPyprojectToml,
|
|
75
|
+
hasSrcDir
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return isValid;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.debug('Error validating function directory', {
|
|
82
|
+
dirPath,
|
|
83
|
+
error: error instanceof Error ? error.message : String(error)
|
|
84
|
+
});
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Helper function to search for function in standard locations
|
|
91
|
+
* @param configDirPath - Directory where config file is located
|
|
92
|
+
* @param normalizedName - Normalized function name
|
|
93
|
+
* @returns First valid function directory path or undefined
|
|
94
|
+
*/
|
|
95
|
+
export function findFunctionInStandardLocations(
|
|
96
|
+
configDirPath: string,
|
|
97
|
+
normalizedName: string
|
|
98
|
+
): string | undefined {
|
|
99
|
+
const searchPaths = [
|
|
100
|
+
// Same directory as config
|
|
101
|
+
join(configDirPath, 'functions', normalizedName),
|
|
102
|
+
// Parent directory of config
|
|
103
|
+
join(configDirPath, '..', 'functions', normalizedName),
|
|
104
|
+
// Current working directory
|
|
105
|
+
join(process.cwd(), 'functions', normalizedName),
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
logger.debug('Searching for function in standard locations', {
|
|
109
|
+
normalizedName,
|
|
110
|
+
configDirPath,
|
|
111
|
+
searchPaths
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
for (const searchPath of searchPaths) {
|
|
115
|
+
const resolvedPath = resolve(searchPath);
|
|
116
|
+
logger.debug('Checking search path', { searchPath, resolvedPath });
|
|
117
|
+
|
|
118
|
+
if (validateFunctionDirectory(resolvedPath)) {
|
|
119
|
+
logger.debug('Found function in standard location', { resolvedPath });
|
|
120
|
+
return resolvedPath;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
logger.debug('Function not found in any standard location', { normalizedName });
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolves the absolute path to a function directory
|
|
130
|
+
* Handles multiple resolution strategies with proper priority
|
|
131
|
+
*
|
|
132
|
+
* @param functionName - Name of the function
|
|
133
|
+
* @param configDirPath - Directory where config file is located
|
|
134
|
+
* @param dirPath - Optional explicit dirPath from config
|
|
135
|
+
* @param explicitPath - Optional path passed as parameter (highest priority)
|
|
136
|
+
* @returns Absolute path to the function directory
|
|
137
|
+
* @throws Error if function directory cannot be found or is invalid
|
|
138
|
+
*/
|
|
139
|
+
export function resolveFunctionDirectory(
|
|
140
|
+
functionName: string,
|
|
141
|
+
configDirPath: string,
|
|
142
|
+
dirPath?: string,
|
|
143
|
+
explicitPath?: string
|
|
144
|
+
): string {
|
|
145
|
+
logger.debug('Resolving function directory', {
|
|
146
|
+
functionName,
|
|
147
|
+
configDirPath,
|
|
148
|
+
dirPath,
|
|
149
|
+
explicitPath
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const normalizedName = normalizeFunctionName(functionName);
|
|
153
|
+
|
|
154
|
+
// Priority 1: Explicit path parameter (highest priority)
|
|
155
|
+
if (explicitPath) {
|
|
156
|
+
logger.debug('Using explicit path parameter');
|
|
157
|
+
const expandedPath = expandTildePath(explicitPath);
|
|
158
|
+
const resolvedPath = isAbsolute(expandedPath)
|
|
159
|
+
? expandedPath
|
|
160
|
+
: resolve(process.cwd(), expandedPath);
|
|
161
|
+
|
|
162
|
+
if (!validateFunctionDirectory(resolvedPath)) {
|
|
163
|
+
const errorMsg = `Explicit path is not a valid function directory: ${resolvedPath}`;
|
|
164
|
+
logger.error(errorMsg);
|
|
165
|
+
MessageFormatter.error('Invalid function directory', errorMsg, { prefix: 'Path Resolution' });
|
|
166
|
+
throw new Error(errorMsg);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
logger.debug('Resolved using explicit path', { resolvedPath });
|
|
170
|
+
MessageFormatter.debug(`Resolved function directory using explicit path: ${resolvedPath}`, undefined, { prefix: 'Path Resolution' });
|
|
171
|
+
return resolvedPath;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Priority 2: dirPath from config (relative to config location)
|
|
175
|
+
if (dirPath) {
|
|
176
|
+
logger.debug('Using dirPath from config');
|
|
177
|
+
const expandedPath = expandTildePath(dirPath);
|
|
178
|
+
const resolvedPath = isAbsolute(expandedPath)
|
|
179
|
+
? expandedPath
|
|
180
|
+
: resolve(configDirPath, expandedPath);
|
|
181
|
+
|
|
182
|
+
if (!validateFunctionDirectory(resolvedPath)) {
|
|
183
|
+
const errorMsg = `Config dirPath is not a valid function directory: ${resolvedPath}`;
|
|
184
|
+
logger.error(errorMsg);
|
|
185
|
+
MessageFormatter.error('Invalid function directory', errorMsg, { prefix: 'Path Resolution' });
|
|
186
|
+
throw new Error(errorMsg);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
logger.debug('Resolved using config dirPath', { resolvedPath });
|
|
190
|
+
MessageFormatter.debug(`Resolved function directory using config dirPath: ${resolvedPath}`, undefined, { prefix: 'Path Resolution' });
|
|
191
|
+
return resolvedPath;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Priority 3: Search standard locations
|
|
195
|
+
logger.debug('Searching standard locations for function');
|
|
196
|
+
const foundPath = findFunctionInStandardLocations(configDirPath, normalizedName);
|
|
197
|
+
|
|
198
|
+
if (foundPath) {
|
|
199
|
+
logger.debug('Resolved using standard location search', { foundPath });
|
|
200
|
+
MessageFormatter.debug(`Found function directory in standard location: ${foundPath}`, undefined, { prefix: 'Path Resolution' });
|
|
201
|
+
return foundPath;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Priority 4: Not found - throw error
|
|
205
|
+
const searchedLocations = [
|
|
206
|
+
join(configDirPath, 'functions', normalizedName),
|
|
207
|
+
join(configDirPath, '..', 'functions', normalizedName),
|
|
208
|
+
join(process.cwd(), 'functions', normalizedName),
|
|
209
|
+
];
|
|
210
|
+
|
|
211
|
+
const errorMsg = `Function directory not found for '${functionName}' (normalized: '${normalizedName}'). ` +
|
|
212
|
+
`Searched locations:\n${searchedLocations.map(p => ` - ${p}`).join('\n')}`;
|
|
213
|
+
|
|
214
|
+
logger.error('Function directory not found', {
|
|
215
|
+
functionName,
|
|
216
|
+
normalizedName,
|
|
217
|
+
searchedLocations
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
MessageFormatter.error(
|
|
221
|
+
'Function directory not found',
|
|
222
|
+
errorMsg,
|
|
223
|
+
{ prefix: 'Path Resolution' }
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
throw new Error(errorMsg);
|
|
227
|
+
}
|
package/src/interactiveCLI.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Compression,
|
|
11
11
|
Query,
|
|
12
12
|
Functions,
|
|
13
|
+
DatabaseType,
|
|
13
14
|
} from "node-appwrite";
|
|
14
15
|
import {
|
|
15
16
|
PermissionToAppwritePermission,
|
|
@@ -232,10 +233,10 @@ export class InteractiveCLI {
|
|
|
232
233
|
const configDatabases = this.getLocalDatabases();
|
|
233
234
|
const allDatabases = [...databases, ...configDatabases]
|
|
234
235
|
.reduce((acc, db) => {
|
|
235
|
-
// Local config takes precedence - if a database with same name exists, use local version
|
|
236
|
-
const existingIndex = acc.findIndex((d) => d.name === db.name);
|
|
236
|
+
// Local config takes precedence - if a database with same name or ID exists, use local version
|
|
237
|
+
const existingIndex = acc.findIndex((d) => d.name === db.name || d.$id === db.$id);
|
|
237
238
|
if (existingIndex >= 0) {
|
|
238
|
-
if (configDatabases.some((cdb) => cdb.name === db.name)) {
|
|
239
|
+
if (configDatabases.some((cdb) => cdb.name === db.name || cdb.$id === db.$id)) {
|
|
239
240
|
acc[existingIndex] = db; // Replace with local version
|
|
240
241
|
}
|
|
241
242
|
} else {
|
|
@@ -246,10 +247,10 @@ export class InteractiveCLI {
|
|
|
246
247
|
|
|
247
248
|
const hasLocalAndRemote =
|
|
248
249
|
allDatabases.some((db) =>
|
|
249
|
-
configDatabases.some((c) => c.name === db.name)
|
|
250
|
+
configDatabases.some((c) => c.name === db.name || c.$id === db.$id)
|
|
250
251
|
) &&
|
|
251
252
|
allDatabases.some(
|
|
252
|
-
(db) => !configDatabases.some((c) => c.name === db.name)
|
|
253
|
+
(db) => !configDatabases.some((c) => c.name === db.name || c.$id === db.$id)
|
|
253
254
|
);
|
|
254
255
|
|
|
255
256
|
const choices = allDatabases
|
|
@@ -258,7 +259,7 @@ export class InteractiveCLI {
|
|
|
258
259
|
name:
|
|
259
260
|
db.name +
|
|
260
261
|
(hasLocalAndRemote
|
|
261
|
-
? configDatabases.some((c) => c.name === db.name)
|
|
262
|
+
? configDatabases.some((c) => c.name === db.name || c.$id === db.$id)
|
|
262
263
|
? " (Local)"
|
|
263
264
|
: " (Remote)"
|
|
264
265
|
: ""),
|
|
@@ -311,7 +312,7 @@ export class InteractiveCLI {
|
|
|
311
312
|
let allCollections = preferLocal
|
|
312
313
|
? remoteCollections.reduce(
|
|
313
314
|
(acc, remoteCollection) => {
|
|
314
|
-
if (!acc.some((c) => c.name === remoteCollection.name)) {
|
|
315
|
+
if (!acc.some((c) => c.name === remoteCollection.name || c.$id === remoteCollection.$id)) {
|
|
315
316
|
acc.push(remoteCollection);
|
|
316
317
|
}
|
|
317
318
|
return acc;
|
|
@@ -321,7 +322,7 @@ export class InteractiveCLI {
|
|
|
321
322
|
: [
|
|
322
323
|
...remoteCollections,
|
|
323
324
|
...configCollections.filter(
|
|
324
|
-
(c) => !remoteCollections.some((rc) => rc.name === c.name)
|
|
325
|
+
(c) => !remoteCollections.some((rc) => rc.name === c.name || rc.$id === c.$id)
|
|
325
326
|
),
|
|
326
327
|
];
|
|
327
328
|
|
|
@@ -329,7 +330,7 @@ export class InteractiveCLI {
|
|
|
329
330
|
// Show collections that EITHER exist in the remote database OR have matching local databaseId metadata
|
|
330
331
|
allCollections = allCollections.filter((c: any) => {
|
|
331
332
|
// Include if it exists remotely in this database
|
|
332
|
-
const existsInRemoteDb = remoteCollections.some((rc) => rc.name === c.name);
|
|
333
|
+
const existsInRemoteDb = remoteCollections.some((rc) => rc.name === c.name || rc.$id === c.$id);
|
|
333
334
|
|
|
334
335
|
// Include if local metadata claims it belongs to this database
|
|
335
336
|
const hasMatchingLocalMetadata = c.databaseId === database.$id;
|
|
@@ -345,10 +346,10 @@ export class InteractiveCLI {
|
|
|
345
346
|
|
|
346
347
|
const hasLocalAndRemote =
|
|
347
348
|
allCollections.some((coll) =>
|
|
348
|
-
configCollections.some((c) => c.name === coll.name)
|
|
349
|
+
configCollections.some((c) => c.name === coll.name || c.$id === coll.$id)
|
|
349
350
|
) &&
|
|
350
351
|
allCollections.some(
|
|
351
|
-
(coll) => !configCollections.some((c) => c.name === coll.name)
|
|
352
|
+
(coll) => !configCollections.some((c) => c.name === coll.name || c.$id === coll.$id)
|
|
352
353
|
);
|
|
353
354
|
|
|
354
355
|
// Enhanced choice display with type indicators
|
|
@@ -365,7 +366,7 @@ export class InteractiveCLI {
|
|
|
365
366
|
return a.name.localeCompare(b.name);
|
|
366
367
|
})
|
|
367
368
|
.map((collection) => {
|
|
368
|
-
const localCollection = configCollections.find((c) => c.name === collection.name);
|
|
369
|
+
const localCollection = configCollections.find((c) => c.name === collection.name || c.$id === collection.$id);
|
|
369
370
|
const isLocal = !!localCollection;
|
|
370
371
|
const isTable = localCollection?._isFromTablesDir || (collection as any)._isFromTablesDir || false;
|
|
371
372
|
const sourceFolder = localCollection?._sourceFolder || (collection as any)._sourceFolder || 'collections';
|
|
@@ -447,12 +448,15 @@ export class InteractiveCLI {
|
|
|
447
448
|
MessageFormatter.info(`📊 ${tablesCount} tables available from tables/ folder`, { prefix: "Collections" });
|
|
448
449
|
}
|
|
449
450
|
|
|
450
|
-
//
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
451
|
+
// Show current database context clearly before view mode selection
|
|
452
|
+
MessageFormatter.info(`DB: ${database.name}`, { prefix: "Collections" });
|
|
453
|
+
|
|
454
|
+
// Ask user if they want to filter by database or show all
|
|
455
|
+
const { filterChoice } = await inquirer.prompt([
|
|
456
|
+
{
|
|
457
|
+
type: "list",
|
|
458
|
+
name: "filterChoice",
|
|
459
|
+
message: chalk.blue("How would you like to view collections/tables?"),
|
|
456
460
|
choices: [
|
|
457
461
|
{
|
|
458
462
|
name: `Show all available collections/tables (${totalCount} total) - You can push any collection to any database`,
|
|
@@ -629,7 +633,7 @@ export class InteractiveCLI {
|
|
|
629
633
|
const allFunctions = [
|
|
630
634
|
...localFunctions,
|
|
631
635
|
...remoteFunctions.functions.filter(
|
|
632
|
-
(rf: any) => !localFunctions.some((lf) => lf.name === rf.name)
|
|
636
|
+
(rf: any) => !localFunctions.some((lf) => lf.name === rf.name || lf.$id === rf.$id)
|
|
633
637
|
),
|
|
634
638
|
];
|
|
635
639
|
|
|
@@ -640,7 +644,7 @@ export class InteractiveCLI {
|
|
|
640
644
|
message,
|
|
641
645
|
choices: allFunctions.map((f) => ({
|
|
642
646
|
name: `${f.name} (${f.$id})${
|
|
643
|
-
localFunctions.some((lf) => lf.name === f.name)
|
|
647
|
+
localFunctions.some((lf) => lf.name === f.name || lf.$id === f.$id)
|
|
644
648
|
? " (Local)"
|
|
645
649
|
: " (Remote)"
|
|
646
650
|
}`,
|
|
@@ -980,6 +984,7 @@ export class InteractiveCLI {
|
|
|
980
984
|
$updatedAt: DateTime.now().toISO(),
|
|
981
985
|
name: db.name,
|
|
982
986
|
enabled: true,
|
|
987
|
+
type: "tablesdb" as DatabaseType,
|
|
983
988
|
}));
|
|
984
989
|
}
|
|
985
990
|
|