appwrite-utils-cli 1.7.8 ā 1.7.9
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/dist/cli/commands/databaseCommands.js +7 -8
- package/dist/config/services/ConfigLoaderService.d.ts +7 -0
- package/dist/config/services/ConfigLoaderService.js +47 -1
- package/dist/functions/deployments.js +5 -23
- 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/main.js +8 -8
- package/dist/shared/selectionDialogs.d.ts +1 -1
- package/dist/shared/selectionDialogs.js +31 -7
- package/dist/utilsController.d.ts +2 -1
- package/dist/utilsController.js +111 -19
- package/package.json +4 -2
- package/scripts/copy-templates.ts +23 -0
- package/src/cli/commands/databaseCommands.ts +7 -8
- package/src/config/services/ConfigLoaderService.ts +62 -1
- package/src/functions/deployments.ts +10 -35
- package/src/functions/methods.ts +4 -2
- package/src/functions/pathResolution.ts +227 -0
- package/src/main.ts +8 -8
- package/src/shared/selectionDialogs.ts +36 -7
- package/src/utilsController.ts +138 -22
- 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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import { resolve as resolvePath, dirname, isAbsolute } from "node:path";
|
|
3
4
|
import yaml from "js-yaml";
|
|
4
5
|
import { register } from "tsx/esm/api";
|
|
5
6
|
import { pathToFileURL } from "node:url";
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
type CollectionDiscoveryResult,
|
|
20
21
|
type TableDiscoveryResult,
|
|
21
22
|
} from "../../utils/configDiscovery.js";
|
|
23
|
+
import { expandTildePath } from "../../functions/pathResolution.js";
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Options for loading collections or tables
|
|
@@ -51,6 +53,35 @@ export interface CollectionLoadOptions {
|
|
|
51
53
|
* - Validates and normalizes configuration data
|
|
52
54
|
*/
|
|
53
55
|
export class ConfigLoaderService {
|
|
56
|
+
/**
|
|
57
|
+
* Normalizes function dirPath to absolute path
|
|
58
|
+
* @param func Function configuration object
|
|
59
|
+
* @param configDir Directory containing the config file
|
|
60
|
+
* @returns Function with normalized dirPath
|
|
61
|
+
*/
|
|
62
|
+
private normalizeFunctionPath(func: any, configDir: string): any {
|
|
63
|
+
if (!func.dirPath) {
|
|
64
|
+
return func;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Expand tilde first
|
|
68
|
+
const expandedPath = expandTildePath(func.dirPath);
|
|
69
|
+
|
|
70
|
+
// If already absolute, return as-is
|
|
71
|
+
if (isAbsolute(expandedPath)) {
|
|
72
|
+
return {
|
|
73
|
+
...func,
|
|
74
|
+
dirPath: expandedPath
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Resolve relative to config directory
|
|
79
|
+
return {
|
|
80
|
+
...func,
|
|
81
|
+
dirPath: resolvePath(configDir, expandedPath)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
54
85
|
/**
|
|
55
86
|
* Loads configuration from a discovered path, auto-detecting the type
|
|
56
87
|
* @param configPath Path to the configuration file
|
|
@@ -78,6 +109,13 @@ export class ConfigLoaderService {
|
|
|
78
109
|
);
|
|
79
110
|
}
|
|
80
111
|
|
|
112
|
+
const configDir = path.dirname(configPath);
|
|
113
|
+
|
|
114
|
+
// Normalize function paths
|
|
115
|
+
const normalizedFunctions = partialConfig.functions
|
|
116
|
+
? partialConfig.functions.map(func => this.normalizeFunctionPath(func, configDir))
|
|
117
|
+
: [];
|
|
118
|
+
|
|
81
119
|
return {
|
|
82
120
|
appwriteEndpoint: partialConfig.appwriteEndpoint,
|
|
83
121
|
appwriteProject: partialConfig.appwriteProject,
|
|
@@ -105,7 +143,7 @@ export class ConfigLoaderService {
|
|
|
105
143
|
},
|
|
106
144
|
databases: partialConfig.databases || [],
|
|
107
145
|
buckets: partialConfig.buckets || [],
|
|
108
|
-
functions:
|
|
146
|
+
functions: normalizedFunctions,
|
|
109
147
|
collections: partialConfig.collections || [],
|
|
110
148
|
sessionCookie: partialConfig.sessionCookie,
|
|
111
149
|
authMethod: partialConfig.authMethod || "auto",
|
|
@@ -155,6 +193,13 @@ export class ConfigLoaderService {
|
|
|
155
193
|
|
|
156
194
|
// Load collections and tables from their respective directories
|
|
157
195
|
const configDir = path.dirname(yamlPath);
|
|
196
|
+
|
|
197
|
+
// Normalize function paths
|
|
198
|
+
if (config.functions) {
|
|
199
|
+
config.functions = config.functions.map(func =>
|
|
200
|
+
this.normalizeFunctionPath(func, configDir)
|
|
201
|
+
);
|
|
202
|
+
}
|
|
158
203
|
const collectionsDir = path.join(configDir, config.schemaConfig?.collectionsDirectory || "collections");
|
|
159
204
|
const tablesDir = path.join(configDir, config.schemaConfig?.tablesDirectory || "tables");
|
|
160
205
|
|
|
@@ -248,6 +293,14 @@ export class ConfigLoaderService {
|
|
|
248
293
|
throw new Error(`Failed to load TypeScript config from: ${tsPath}`);
|
|
249
294
|
}
|
|
250
295
|
|
|
296
|
+
// Normalize function paths
|
|
297
|
+
const configDir = path.dirname(tsPath);
|
|
298
|
+
if (config.functions) {
|
|
299
|
+
config.functions = config.functions.map(func =>
|
|
300
|
+
this.normalizeFunctionPath(func, configDir)
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
251
304
|
MessageFormatter.success(`Loaded TypeScript config from: ${tsPath}`, {
|
|
252
305
|
prefix: "Config",
|
|
253
306
|
});
|
|
@@ -292,6 +345,14 @@ export class ConfigLoaderService {
|
|
|
292
345
|
appwriteConfig.collections = collections;
|
|
293
346
|
}
|
|
294
347
|
|
|
348
|
+
// Normalize function paths
|
|
349
|
+
const configDir = path.dirname(jsonPath);
|
|
350
|
+
if (appwriteConfig.functions) {
|
|
351
|
+
appwriteConfig.functions = appwriteConfig.functions.map(func =>
|
|
352
|
+
this.normalizeFunctionPath(func, configDir)
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
295
356
|
MessageFormatter.success(`Loaded project config from: ${jsonPath}`, {
|
|
296
357
|
prefix: "Config",
|
|
297
358
|
});
|
|
@@ -16,30 +16,7 @@ import {
|
|
|
16
16
|
} from "./methods.js";
|
|
17
17
|
import ignore from "ignore";
|
|
18
18
|
import { MessageFormatter } from "../shared/messageFormatter.js";
|
|
19
|
-
|
|
20
|
-
const findFunctionDirectory = (
|
|
21
|
-
basePath: string,
|
|
22
|
-
functionName: string
|
|
23
|
-
): string | undefined => {
|
|
24
|
-
const normalizedName = functionName.toLowerCase().replace(/\s+/g, "-");
|
|
25
|
-
const dirs = fs.readdirSync(basePath, { withFileTypes: true });
|
|
26
|
-
|
|
27
|
-
for (const dir of dirs) {
|
|
28
|
-
if (dir.isDirectory()) {
|
|
29
|
-
const fullPath = join(basePath, dir.name);
|
|
30
|
-
if (dir.name.toLowerCase() === normalizedName) {
|
|
31
|
-
return fullPath;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const nestedResult = findFunctionDirectory(fullPath, functionName);
|
|
35
|
-
if (nestedResult) {
|
|
36
|
-
return nestedResult;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return undefined;
|
|
42
|
-
};
|
|
19
|
+
import { resolveFunctionDirectory, validateFunctionDirectory } from './pathResolution.js';
|
|
43
20
|
|
|
44
21
|
export const deployFunction = async (
|
|
45
22
|
client: Client,
|
|
@@ -183,18 +160,16 @@ export const deployLocalFunction = async (
|
|
|
183
160
|
functionExists = false;
|
|
184
161
|
}
|
|
185
162
|
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
functionName.toLowerCase().replace(/\s+/g, "-")
|
|
194
|
-
);
|
|
163
|
+
const configDirPath = process.cwd(); // TODO: This should be passed from caller
|
|
164
|
+
const resolvedPath = resolveFunctionDirectory(
|
|
165
|
+
functionName,
|
|
166
|
+
configDirPath,
|
|
167
|
+
functionConfig.dirPath,
|
|
168
|
+
functionPath
|
|
169
|
+
);
|
|
195
170
|
|
|
196
|
-
if (!
|
|
197
|
-
throw new Error(`Function directory
|
|
171
|
+
if (!validateFunctionDirectory(resolvedPath)) {
|
|
172
|
+
throw new Error(`Function directory is invalid or missing required files: ${resolvedPath}`);
|
|
198
173
|
}
|
|
199
174
|
|
|
200
175
|
if (functionConfig.predeployCommands?.length) {
|
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/main.ts
CHANGED
|
@@ -149,14 +149,14 @@ async function performEnhancedSync(
|
|
|
149
149
|
bucketSelections
|
|
150
150
|
);
|
|
151
151
|
|
|
152
|
-
const confirmed = await SelectionDialogs.confirmSyncSelection(selectionSummary);
|
|
152
|
+
const confirmed = await SelectionDialogs.confirmSyncSelection(selectionSummary, 'pull');
|
|
153
153
|
if (!confirmed) {
|
|
154
|
-
MessageFormatter.info("
|
|
154
|
+
MessageFormatter.info("Pull operation cancelled by user", { prefix: "Sync" });
|
|
155
155
|
return null;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
// Perform sync with existing configuration
|
|
159
|
-
await controller.
|
|
158
|
+
// Perform sync with existing configuration (pull from remote)
|
|
159
|
+
await controller.selectivePull(databaseSelections, bucketSelections);
|
|
160
160
|
return selectionSummary;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
@@ -275,14 +275,14 @@ async function performEnhancedSync(
|
|
|
275
275
|
bucketSelections
|
|
276
276
|
);
|
|
277
277
|
|
|
278
|
-
const confirmed = await SelectionDialogs.confirmSyncSelection(selectionSummary);
|
|
278
|
+
const confirmed = await SelectionDialogs.confirmSyncSelection(selectionSummary, 'pull');
|
|
279
279
|
if (!confirmed) {
|
|
280
|
-
MessageFormatter.info("
|
|
280
|
+
MessageFormatter.info("Pull operation cancelled by user", { prefix: "Sync" });
|
|
281
281
|
return null;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
// Perform the selective sync
|
|
285
|
-
await controller.
|
|
284
|
+
// Perform the selective sync (pull from remote)
|
|
285
|
+
await controller.selectivePull(databaseSelections, bucketSelections);
|
|
286
286
|
|
|
287
287
|
MessageFormatter.success("Enhanced sync completed successfully", { prefix: "Sync" });
|
|
288
288
|
return selectionSummary;
|
|
@@ -520,8 +520,37 @@ export class SelectionDialogs {
|
|
|
520
520
|
/**
|
|
521
521
|
* Shows final confirmation dialog with sync selection summary
|
|
522
522
|
*/
|
|
523
|
-
static async confirmSyncSelection(
|
|
524
|
-
|
|
523
|
+
static async confirmSyncSelection(
|
|
524
|
+
selectionSummary: SyncSelectionSummary,
|
|
525
|
+
operationType: 'push' | 'pull' | 'sync' = 'sync'
|
|
526
|
+
): Promise<boolean> {
|
|
527
|
+
const labels = {
|
|
528
|
+
push: {
|
|
529
|
+
banner: "Push Selection Summary",
|
|
530
|
+
subtitle: "Review selections before pushing to Appwrite",
|
|
531
|
+
confirm: "Proceed with push operation?",
|
|
532
|
+
success: "Push operation confirmed.",
|
|
533
|
+
cancel: "Push operation cancelled."
|
|
534
|
+
},
|
|
535
|
+
pull: {
|
|
536
|
+
banner: "Pull Selection Summary",
|
|
537
|
+
subtitle: "Review selections before pulling from Appwrite",
|
|
538
|
+
confirm: "Proceed with pull operation?",
|
|
539
|
+
success: "Pull operation confirmed.",
|
|
540
|
+
cancel: "Pull operation cancelled."
|
|
541
|
+
},
|
|
542
|
+
sync: {
|
|
543
|
+
banner: "Sync Selection Summary",
|
|
544
|
+
subtitle: "Review your selections before proceeding",
|
|
545
|
+
confirm: "Proceed with sync operation?",
|
|
546
|
+
success: "Sync operation confirmed.",
|
|
547
|
+
cancel: "Sync operation cancelled."
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const label = labels[operationType];
|
|
552
|
+
|
|
553
|
+
MessageFormatter.banner(label.banner, label.subtitle);
|
|
525
554
|
|
|
526
555
|
// Database summary
|
|
527
556
|
console.log(chalk.bold.cyan("\nš Databases:"));
|
|
@@ -563,20 +592,20 @@ export class SelectionDialogs {
|
|
|
563
592
|
const { confirmed } = await inquirer.prompt([{
|
|
564
593
|
type: 'confirm',
|
|
565
594
|
name: 'confirmed',
|
|
566
|
-
message: chalk.green.bold(
|
|
595
|
+
message: chalk.green.bold(label.confirm),
|
|
567
596
|
default: true
|
|
568
597
|
}]);
|
|
569
598
|
|
|
570
599
|
if (confirmed) {
|
|
571
|
-
MessageFormatter.success(
|
|
572
|
-
logger.info(
|
|
600
|
+
MessageFormatter.success(label.success, { skipLogging: true });
|
|
601
|
+
logger.info(`${operationType} selection confirmed`, {
|
|
573
602
|
databases: selectionSummary.totalDatabases,
|
|
574
603
|
tables: selectionSummary.totalTables,
|
|
575
604
|
buckets: selectionSummary.totalBuckets
|
|
576
605
|
});
|
|
577
606
|
} else {
|
|
578
|
-
MessageFormatter.warning(
|
|
579
|
-
logger.info(
|
|
607
|
+
MessageFormatter.warning(label.cancel, { skipLogging: true });
|
|
608
|
+
logger.info(`${operationType} selection cancelled by user`);
|
|
580
609
|
}
|
|
581
610
|
|
|
582
611
|
return confirmed;
|