do-functions-cli 1.0.0

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.
Files changed (45) hide show
  1. package/README.md +70 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +32 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/create.d.ts +4 -0
  7. package/dist/commands/create.d.ts.map +1 -0
  8. package/dist/commands/create.js +130 -0
  9. package/dist/commands/create.js.map +1 -0
  10. package/dist/constants.d.ts +11 -0
  11. package/dist/constants.d.ts.map +1 -0
  12. package/dist/constants.js +11 -0
  13. package/dist/constants.js.map +1 -0
  14. package/dist/schemas/doFunction.d.ts +23 -0
  15. package/dist/schemas/doFunction.d.ts.map +1 -0
  16. package/dist/schemas/doFunction.js +19 -0
  17. package/dist/schemas/doFunction.js.map +1 -0
  18. package/dist/schemas/doPackage.d.ts +25 -0
  19. package/dist/schemas/doPackage.d.ts.map +1 -0
  20. package/dist/schemas/doPackage.js +14 -0
  21. package/dist/schemas/doPackage.js.map +1 -0
  22. package/dist/schemas/doProjectYml.d.ts +26 -0
  23. package/dist/schemas/doProjectYml.d.ts.map +1 -0
  24. package/dist/schemas/doProjectYml.js +12 -0
  25. package/dist/schemas/doProjectYml.js.map +1 -0
  26. package/dist/utils/scaffold.d.ts +24 -0
  27. package/dist/utils/scaffold.d.ts.map +1 -0
  28. package/dist/utils/scaffold.js +56 -0
  29. package/dist/utils/scaffold.js.map +1 -0
  30. package/dist/utils/util.d.ts +24 -0
  31. package/dist/utils/util.d.ts.map +1 -0
  32. package/dist/utils/util.js +56 -0
  33. package/dist/utils/util.js.map +1 -0
  34. package/dist/utils/validators.d.ts +8 -0
  35. package/dist/utils/validators.d.ts.map +1 -0
  36. package/dist/utils/validators.js +14 -0
  37. package/dist/utils/validators.js.map +1 -0
  38. package/package.json +49 -0
  39. package/templates/functions/javascript/.include +1 -0
  40. package/templates/functions/javascript/index.js +3 -0
  41. package/templates/functions/javascript/package.json +10 -0
  42. package/templates/functions/typescript/.include +1 -0
  43. package/templates/functions/typescript/index.ts +3 -0
  44. package/templates/functions/typescript/package.json +12 -0
  45. package/templates/functions/typescript/tsconfig.json +43 -0
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # do-functions-cli
2
+
3
+ ![npm version](https://img.shields.io/npm/v/do-functions-cli.svg)
4
+ ![license](https://img.shields.io/badge/license-MIT-blue.svg)
5
+
6
+ A CLI tool to streamline working with DigitalOcean Serverless Functions.
7
+
8
+ ## Features
9
+
10
+ - Create new function packages via an interactive `create` command
11
+ - Support for multiple languages (JavaScript, TypeScript, more to come)
12
+ - Auto-install dependencies in the generated package (optional)
13
+ - Automatically update the root `project.yml` with the new package/function (optional)
14
+
15
+ ## Installation
16
+
17
+ Global:
18
+
19
+ ```bash
20
+ npm i -g do-functions-cli
21
+ ```
22
+
23
+ Local:
24
+
25
+ ```bash
26
+ npm i -D do-functions-cli
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Run the CLI and follow the prompts:
32
+
33
+ ```bash
34
+ # Show help
35
+ npx do-functions-cli --help
36
+
37
+ # Create a new function
38
+ npx do-functions-cli create
39
+ ```
40
+
41
+ ## Roadmap / Ideas
42
+
43
+ - Non-interactive flags for commands (see below)
44
+ - Additional commands: `validate`, `deploy`, etc.
45
+
46
+ ## Planned CLI Flags
47
+
48
+ - `--packages-dir <path>`: Set root packages directory (defaults to `./packages`).
49
+ - `--function <package/name>`: Provide function identifier directly.
50
+ - `--<language>`: Choose template language (e.g., `--javascript`, `--typescript`).
51
+ - `--install` / `--no-install`: Control dependency installation step.
52
+ - `--add-project` / `--no-add-project`: Control `project.yml` auto-update.
53
+ - `--dry-run`: Show intended actions without any file changes.
54
+ - `--verbose`: Print detailed logs for troubleshooting.
55
+
56
+ ## Troubleshooting
57
+
58
+ - If `project.yml` parsing fails, ensure the file is valid YAML and matches the expected schema:
59
+
60
+ ```yaml
61
+ packages:
62
+ - name: Package Name
63
+ functions:
64
+ - name: sample/func1
65
+ runtime: nodejs:18
66
+ web: true
67
+ ...
68
+ ```
69
+
70
+ - Ensure Node.js 18+ for best compatibility with templates and runtime.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import createCommand from './commands/create.js';
4
+ import fs from 'fs-extra/esm';
5
+ const packageJson = await fs.readJSON(new URL('../package.json', import.meta.url));
6
+ const cliVersion = packageJson.version;
7
+ // Initialize the main CLI program
8
+ const program = new Command();
9
+ program
10
+ .name('do-functions-cli')
11
+ .description('CLI tool for managing DigitalOcean serverless functions')
12
+ .version(cliVersion);
13
+ // Register the 'create' command for creating new serverless functions
14
+ program.addCommand(createCommand);
15
+ // Parse command-line arguments and execute
16
+ program.parse(process.argv);
17
+ /**
18
+ * Handle uncaught exceptions globally.
19
+ *
20
+ * ExitPromptError is thrown when a user cancels an interactive prompt,
21
+ * so we handle it gracefully. Other errors are re-thrown for proper error reporting.
22
+ */
23
+ process.on('uncaughtException', (error) => {
24
+ if (error instanceof Error && error.name === 'ExitPromptError') {
25
+ console.log('👋 until next time!');
26
+ }
27
+ else {
28
+ // Rethrow unknown errors for proper error handling and exit
29
+ throw error;
30
+ }
31
+ });
32
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,aAAa,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,MAAM,cAAc,CAAC;AAE9B,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACnF,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC;AAEvC,kCAAkC;AAClC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,kBAAkB,CAAC;KACxB,WAAW,CAAC,yDAAyD,CAAC;KACtE,OAAO,CAAC,UAAU,CAAC,CAAC;AAEvB,sEAAsE;AACtE,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAElC,2CAA2C;AAC3C,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5B;;;;;GAKG;AACH,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE;IACxC,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACrC,CAAC;SAAM,CAAC;QACN,4DAA4D;QAC5D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ declare const createCommand: Command;
3
+ export default createCommand;
4
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,QAAA,MAAM,aAAa,SAAwB,CAAC;AAmH5C,eAAe,aAAa,CAAC"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Command module for creating new DigitalOcean serverless functions.
3
+ *
4
+ * This module provides an interactive CLI command that guides users through:
5
+ * 1. Selecting a packages directory
6
+ * 2. Creating a new function with package/name structure
7
+ * 3. Choosing between different language templates
8
+ * 4. Installing dependencies (optional)
9
+ * 5. Adding the function to project.yml configuration (optional)
10
+ */
11
+ import { confirm, input, select } from '@inquirer/prompts';
12
+ import { Command } from 'commander';
13
+ import fs from 'fs-extra';
14
+ import * as child_process from 'node:child_process';
15
+ import * as path from 'node:path';
16
+ import * as util from 'node:util';
17
+ import ora from 'ora';
18
+ import { scaffoldFunction, updateProjectConfig } from '../utils/util.js';
19
+ import { validateFunctionName } from '../utils/validators.js';
20
+ // Promisify exec to use async/await syntax
21
+ const exec = util.promisify(child_process.exec);
22
+ const createCommand = new Command('create');
23
+ createCommand.description('create a new serverless function');
24
+ createCommand.option('-y, --yes', 'skip all optional prompts and use defaults');
25
+ createCommand.action(async (options) => {
26
+ const userPackagesDir = await input({
27
+ message: 'Enter the root function packages directory:',
28
+ default: './packages',
29
+ });
30
+ const userFuncPath = await input({
31
+ message: 'Enter the package/function name to create:',
32
+ default: 'sample/hello',
33
+ validate: validateFunctionName,
34
+ });
35
+ const [pkgName, funcName] = userFuncPath.split('/');
36
+ const resolvedFuncDir = path.resolve(userPackagesDir, userFuncPath);
37
+ if (await fs.exists(resolvedFuncDir)) {
38
+ const overwrite = await confirm({
39
+ message: `The directory "${resolvedFuncDir}" already exists. Do you want to overwrite it?`,
40
+ default: false,
41
+ });
42
+ if (!overwrite) {
43
+ ora('Operation cancelled. No changes were made.').fail();
44
+ return;
45
+ }
46
+ }
47
+ const funcLanguage = (await select({
48
+ message: 'Choose a language for the function:',
49
+ choices: [
50
+ { name: 'JavaScript', value: 'javascript' },
51
+ { name: 'TypeScript', value: 'typescript' },
52
+ ],
53
+ default: 'javascript',
54
+ }));
55
+ // Initialize a spinner to show progress to the user
56
+ const spinner = ora(`Creating function '${userFuncPath}'...`).start();
57
+ try {
58
+ await scaffoldFunction({
59
+ targetDir: resolvedFuncDir,
60
+ funcPath: userFuncPath,
61
+ funcLanguage,
62
+ });
63
+ spinner.succeed(`Function "${userFuncPath}" successfully created at "${resolvedFuncDir}"`);
64
+ }
65
+ catch (error) {
66
+ spinner.fail('An error occurred while creating the function');
67
+ throw error;
68
+ }
69
+ // Offer to install dependencies in the new function directory
70
+ const installDeps = options.yes ||
71
+ (await confirm({
72
+ message: 'Do you want to install dependencies now?',
73
+ default: true,
74
+ }));
75
+ if (installDeps) {
76
+ spinner.start('Installing dependencies...');
77
+ try {
78
+ // Run npm install in the function directory
79
+ await exec('npm install', { cwd: resolvedFuncDir });
80
+ spinner.succeed('Dependencies installed successfully');
81
+ }
82
+ catch (error) {
83
+ spinner.fail('An error occurred while installing dependencies');
84
+ throw error;
85
+ }
86
+ }
87
+ // Offer to add the new function to the project configuration file
88
+ const addToProject = options.yes ||
89
+ (await confirm({
90
+ message: 'Do you want to add this function automatically to the project.yml config?',
91
+ default: true,
92
+ }));
93
+ if (addToProject) {
94
+ const projectYmlPath = path.resolve('project.yml');
95
+ const createIfMissing = !(await fs.exists(projectYmlPath))
96
+ ? options.yes ||
97
+ (await confirm({
98
+ message: 'A project.yml file was not found. Would you like to create one?',
99
+ default: true,
100
+ }))
101
+ : true;
102
+ if (!createIfMissing) {
103
+ spinner.info('Skipping creating the project.yml file...');
104
+ }
105
+ else {
106
+ spinner.start('Updating project.yml configuration...');
107
+ const result = await updateProjectConfig(projectYmlPath, pkgName, funcName);
108
+ const resultStatusMessageMap = {
109
+ 'created-new-config': `Created project.yml with package "${pkgName}" and function "${userFuncPath}"`,
110
+ 'added-package': `Added new package "${pkgName}" with function "${userFuncPath}" to project.yml`,
111
+ 'added-function': `Added function "${userFuncPath}" to existing package "${pkgName}" in project.yml`,
112
+ 'function-exists': `Function "${userFuncPath}" already exists in package "${pkgName}". Skipping update.`,
113
+ error: 'Failed to update project.yml configuration',
114
+ };
115
+ const resultStatusMessage = resultStatusMessageMap[result.status];
116
+ if (result.status === 'error') {
117
+ spinner.fail(resultStatusMessage);
118
+ }
119
+ else if (result.status === 'function-exists') {
120
+ spinner.info(resultStatusMessage);
121
+ }
122
+ else {
123
+ spinner.succeed(resultStatusMessage);
124
+ }
125
+ }
126
+ }
127
+ });
128
+ // Export the create command for registration in the CLI
129
+ export default createCommand;
130
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Default runtime environment for DigitalOcean Functions.
3
+ */
4
+ export declare const DEFAULT_RUNTIME = "nodejs:18";
5
+ /**
6
+ * Regex pattern to validate function names.
7
+ * Function names must be in the format: package/name (e.g., 'myapp/hello')
8
+ * Both the package and function name must contain only lowercase letters.
9
+ */
10
+ export declare const FUNCTION_NAME_REGEX: RegExp;
11
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAqB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Default runtime environment for DigitalOcean Functions.
3
+ */
4
+ export const DEFAULT_RUNTIME = 'nodejs:18';
5
+ /**
6
+ * Regex pattern to validate function names.
7
+ * Function names must be in the format: package/name (e.g., 'myapp/hello')
8
+ * Both the package and function name must contain only lowercase letters.
9
+ */
10
+ export const FUNCTION_NAME_REGEX = /^[a-z]+\/[a-z]+$/;
11
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Zod schema for validating a DigitalOcean function entry in project.yml
4
+ *
5
+ * Properties:
6
+ * - name: The function name (required)
7
+ * - runtime: The Node.js runtime version (defaults to DEFAULT_RUNTIME)
8
+ * - web: Whether the function is web-accessible (defaults to true)
9
+ */
10
+ export declare const DOFunctionSchema: z.ZodObject<{
11
+ name: z.ZodString;
12
+ runtime: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
13
+ "nodejs:18": "nodejs:18";
14
+ "nodejs:14": "nodejs:14";
15
+ "nodejs:default": "nodejs:default";
16
+ }>>>;
17
+ web: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
18
+ }, z.core.$strip>;
19
+ /**
20
+ * TypeScript type derived from the DOFunctionSchema
21
+ */
22
+ export type DOFunction = z.infer<typeof DOFunctionSchema>;
23
+ //# sourceMappingURL=doFunction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doFunction.d.ts","sourceRoot":"","sources":["../../src/schemas/doFunction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAGzB;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;iBAO3B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import * as z from 'zod';
2
+ import { DEFAULT_RUNTIME } from '../constants.js';
3
+ /**
4
+ * Zod schema for validating a DigitalOcean function entry in project.yml
5
+ *
6
+ * Properties:
7
+ * - name: The function name (required)
8
+ * - runtime: The Node.js runtime version (defaults to DEFAULT_RUNTIME)
9
+ * - web: Whether the function is web-accessible (defaults to true)
10
+ */
11
+ export const DOFunctionSchema = z.object({
12
+ name: z.string(),
13
+ runtime: z
14
+ .enum(['nodejs:14', 'nodejs:18', 'nodejs:default'])
15
+ .optional()
16
+ .default(DEFAULT_RUNTIME),
17
+ web: z.boolean().optional().default(true),
18
+ });
19
+ //# sourceMappingURL=doFunction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doFunction.js","sourceRoot":"","sources":["../../src/schemas/doFunction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,CAAC;SACP,IAAI,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,gBAAgB,CAAC,CAAC;SAClD,QAAQ,EAAE;SACV,OAAO,CAAC,eAA8B,CAAC;IAC1C,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CAC1C,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Zod schema for validating a DigitalOcean function package entry in project.yml
4
+ *
5
+ * Properties:
6
+ * - name: The package name (required, used for namespacing functions)
7
+ * - functions: Array of functions belonging to this package (defaults to empty array)
8
+ */
9
+ export declare const DOPackageSchema: z.ZodObject<{
10
+ name: z.ZodString;
11
+ functions: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
12
+ name: z.ZodString;
13
+ runtime: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
14
+ "nodejs:18": "nodejs:18";
15
+ "nodejs:14": "nodejs:14";
16
+ "nodejs:default": "nodejs:default";
17
+ }>>>;
18
+ web: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
19
+ }, z.core.$strip>>>>;
20
+ }, z.core.$strip>;
21
+ /**
22
+ * TypeScript type derived from the DOPackageSchema
23
+ */
24
+ export type DOPackage = z.infer<typeof DOPackageSchema>;
25
+ //# sourceMappingURL=doPackage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doPackage.d.ts","sourceRoot":"","sources":["../../src/schemas/doPackage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAGzB;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;iBAG1B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import * as z from 'zod';
2
+ import { DOFunctionSchema } from './doFunction.js';
3
+ /**
4
+ * Zod schema for validating a DigitalOcean function package entry in project.yml
5
+ *
6
+ * Properties:
7
+ * - name: The package name (required, used for namespacing functions)
8
+ * - functions: Array of functions belonging to this package (defaults to empty array)
9
+ */
10
+ export const DOPackageSchema = z.object({
11
+ name: z.string(),
12
+ functions: z.array(DOFunctionSchema).optional().default([]),
13
+ });
14
+ //# sourceMappingURL=doPackage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doPackage.js","sourceRoot":"","sources":["../../src/schemas/doPackage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC5D,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Zod schema for validating a project.yml configuration structure
4
+ *
5
+ * Properties:
6
+ * - packages: Array of packages that make up the project (defaults to empty array)
7
+ */
8
+ export declare const DOProjectYmlSchema: z.ZodObject<{
9
+ packages: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
10
+ name: z.ZodString;
11
+ functions: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
12
+ name: z.ZodString;
13
+ runtime: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
14
+ "nodejs:18": "nodejs:18";
15
+ "nodejs:14": "nodejs:14";
16
+ "nodejs:default": "nodejs:default";
17
+ }>>>;
18
+ web: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
19
+ }, z.core.$strip>>>>;
20
+ }, z.core.$strip>>>>;
21
+ }, z.core.$strip>;
22
+ /**
23
+ * TypeScript type derived from the DOProjectYmlSchema
24
+ */
25
+ export type DOProjectYml = z.infer<typeof DOProjectYmlSchema>;
26
+ //# sourceMappingURL=doProjectYml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doProjectYml.d.ts","sourceRoot":"","sources":["../../src/schemas/doProjectYml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAGzB;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;iBAE7B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import * as z from 'zod';
2
+ import { DOPackageSchema } from './doPackage.js';
3
+ /**
4
+ * Zod schema for validating a project.yml configuration structure
5
+ *
6
+ * Properties:
7
+ * - packages: Array of packages that make up the project (defaults to empty array)
8
+ */
9
+ export const DOProjectYmlSchema = z.object({
10
+ packages: z.array(DOPackageSchema).optional().default([]),
11
+ });
12
+ //# sourceMappingURL=doProjectYml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doProjectYml.js","sourceRoot":"","sources":["../../src/schemas/doProjectYml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC1D,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ export type LanguageChoice = 'typescript' | 'javascript';
2
+ /**
3
+ * Get the template directory for a function depending on language choice.
4
+ */
5
+ export declare function getTemplateDir(funcLanguage: LanguageChoice): string;
6
+ export type ScaffoldOptions = {
7
+ targetDir: string;
8
+ funcPath: string;
9
+ funcLanguage: LanguageChoice;
10
+ };
11
+ /**
12
+ * Scaffolds a function directory by emptying target and copying template, then updating package.json name.
13
+ */
14
+ export declare function scaffoldFunction({ targetDir, funcPath, funcLanguage, }: ScaffoldOptions): Promise<void>;
15
+ export type UpdateStatus = 'created-new-config' | 'added-package' | 'added-function' | 'function-exists' | 'error';
16
+ export type UpdateResult = {
17
+ status: UpdateStatus;
18
+ };
19
+ /**
20
+ * Update or create the project.yml configuration with the given package and function.
21
+ * Uses early returns for clarity. Returns a status describing the performed action.
22
+ */
23
+ export declare function updateProjectConfig(projectYmlPath: string, pkgName: string, funcName: string): Promise<UpdateResult>;
24
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/utils/scaffold.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,YAAY,CAAC;AAEzD;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,cAAc,GAAG,MAAM,CAOnE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,cAAc,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,YAAY,GACb,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjC;AAED,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,eAAe,GACf,gBAAgB,GAChB,iBAAiB,GACjB,OAAO,CAAC;AAEZ,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CA+BvB"}
@@ -0,0 +1,56 @@
1
+ import fs from 'fs-extra';
2
+ import * as path from 'node:path';
3
+ import { DEFAULT_RUNTIME } from '../constants.js';
4
+ import { DOProjectYmlSchema } from '../schemas/doProjectYml.js';
5
+ import YAML from 'yaml';
6
+ import { fileURLToPath } from 'node:url';
7
+ /**
8
+ * Get the template directory for a function depending on language choice.
9
+ */
10
+ export function getTemplateDir(funcLanguage) {
11
+ return path.join(fileURLToPath(new URL('../../', import.meta.url)), 'templates', 'functions', funcLanguage);
12
+ }
13
+ /**
14
+ * Scaffolds a function directory by emptying target and copying template, then updating package.json name.
15
+ */
16
+ export async function scaffoldFunction({ targetDir, funcPath, funcLanguage, }) {
17
+ await fs.emptyDir(targetDir);
18
+ const templateDir = getTemplateDir(funcLanguage);
19
+ await fs.copy(templateDir, targetDir);
20
+ const packageJsonPath = path.join(targetDir, 'package.json');
21
+ const packageJson = await fs.readJson(packageJsonPath);
22
+ await fs.writeJson(packageJsonPath, { name: `@${funcPath}`, ...packageJson }, { spaces: 2 });
23
+ }
24
+ /**
25
+ * Update or create the project.yml configuration with the given package and function.
26
+ * Uses early returns for clarity. Returns a status describing the performed action.
27
+ */
28
+ export async function updateProjectConfig(projectYmlPath, pkgName, funcName) {
29
+ try {
30
+ const functionObj = { name: funcName, runtime: DEFAULT_RUNTIME, web: true };
31
+ const exists = await fs.exists(projectYmlPath);
32
+ if (!exists) {
33
+ const initial = { packages: [{ name: pkgName, functions: [functionObj] }] };
34
+ await fs.writeFile(projectYmlPath, YAML.stringify(initial));
35
+ return { status: 'created-new-config' };
36
+ }
37
+ const parsed = DOProjectYmlSchema.parse(YAML.parse(await fs.readFile(projectYmlPath, 'utf-8')));
38
+ const pkg = parsed.packages.find((p) => p.name === pkgName);
39
+ if (!pkg) {
40
+ parsed.packages.push({ name: pkgName, functions: [functionObj] });
41
+ await fs.writeFile(projectYmlPath, YAML.stringify(parsed));
42
+ return { status: 'added-package' };
43
+ }
44
+ if (pkg.functions.find((f) => f.name === funcName)) {
45
+ return { status: 'function-exists' }; // No write needed
46
+ }
47
+ pkg.functions.push(functionObj);
48
+ await fs.writeFile(projectYmlPath, YAML.stringify(parsed));
49
+ return { status: 'added-function' };
50
+ }
51
+ catch (e) {
52
+ console.error(e);
53
+ return { status: 'error' };
54
+ }
55
+ }
56
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/utils/scaffold.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export type LanguageChoice = 'typescript' | 'javascript';
2
+ /**
3
+ * Get the template directory for a function depending on language choice.
4
+ */
5
+ export declare function getTemplateDir(funcLanguage: LanguageChoice): string;
6
+ export type ScaffoldOptions = {
7
+ targetDir: string;
8
+ funcPath: string;
9
+ funcLanguage: LanguageChoice;
10
+ };
11
+ /**
12
+ * Scaffolds a function directory by emptying target and copying template, then updating package.json name.
13
+ */
14
+ export declare function scaffoldFunction({ targetDir, funcPath, funcLanguage, }: ScaffoldOptions): Promise<void>;
15
+ export type UpdateStatus = 'created-new-config' | 'added-package' | 'added-function' | 'function-exists' | 'error';
16
+ export type UpdateResult = {
17
+ status: UpdateStatus;
18
+ };
19
+ /**
20
+ * Update or create the project.yml configuration with the given package and function.
21
+ * Uses early returns for clarity. Returns a status describing the performed action.
22
+ */
23
+ export declare function updateProjectConfig(projectYmlPath: string, pkgName: string, funcName: string): Promise<UpdateResult>;
24
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/utils/util.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,YAAY,CAAC;AAEzD;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,cAAc,GAAG,MAAM,CAOnE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,cAAc,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,YAAY,GACb,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjC;AAED,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,eAAe,GACf,gBAAgB,GAChB,iBAAiB,GACjB,OAAO,CAAC;AAEZ,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CA+BvB"}
@@ -0,0 +1,56 @@
1
+ import fs from 'fs-extra';
2
+ import * as path from 'node:path';
3
+ import { DEFAULT_RUNTIME } from '../constants.js';
4
+ import { DOProjectYmlSchema } from '../schemas/doProjectYml.js';
5
+ import YAML from 'yaml';
6
+ import { fileURLToPath } from 'node:url';
7
+ /**
8
+ * Get the template directory for a function depending on language choice.
9
+ */
10
+ export function getTemplateDir(funcLanguage) {
11
+ return path.join(fileURLToPath(new URL('../../', import.meta.url)), 'templates', 'functions', funcLanguage);
12
+ }
13
+ /**
14
+ * Scaffolds a function directory by emptying target and copying template, then updating package.json name.
15
+ */
16
+ export async function scaffoldFunction({ targetDir, funcPath, funcLanguage, }) {
17
+ await fs.emptyDir(targetDir);
18
+ const templateDir = getTemplateDir(funcLanguage);
19
+ await fs.copy(templateDir, targetDir);
20
+ const packageJsonPath = path.join(targetDir, 'package.json');
21
+ const packageJson = await fs.readJson(packageJsonPath);
22
+ await fs.writeJson(packageJsonPath, { name: `@${funcPath}`, ...packageJson }, { spaces: 2 });
23
+ }
24
+ /**
25
+ * Update or create the project.yml configuration with the given package and function.
26
+ * Uses early returns for clarity. Returns a status describing the performed action.
27
+ */
28
+ export async function updateProjectConfig(projectYmlPath, pkgName, funcName) {
29
+ try {
30
+ const functionObj = { name: funcName, runtime: DEFAULT_RUNTIME, web: true };
31
+ const exists = await fs.exists(projectYmlPath);
32
+ if (!exists) {
33
+ const initial = { packages: [{ name: pkgName, functions: [functionObj] }] };
34
+ await fs.writeFile(projectYmlPath, YAML.stringify(initial));
35
+ return { status: 'created-new-config' };
36
+ }
37
+ const parsed = DOProjectYmlSchema.parse(YAML.parse(await fs.readFile(projectYmlPath, 'utf-8')));
38
+ const pkg = parsed.packages.find((p) => p.name === pkgName);
39
+ if (!pkg) {
40
+ parsed.packages.push({ name: pkgName, functions: [functionObj] });
41
+ await fs.writeFile(projectYmlPath, YAML.stringify(parsed));
42
+ return { status: 'added-package' };
43
+ }
44
+ if (pkg.functions.find((f) => f.name === funcName)) {
45
+ return { status: 'function-exists' }; // No write needed
46
+ }
47
+ pkg.functions.push(functionObj);
48
+ await fs.writeFile(projectYmlPath, YAML.stringify(parsed));
49
+ return { status: 'added-function' };
50
+ }
51
+ catch (e) {
52
+ console.error(e);
53
+ return { status: 'error' };
54
+ }
55
+ }
56
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/utils/util.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Validates that a function name matches the required format (package/name).
3
+ *
4
+ * @param value - The function name input to validate
5
+ * @returns true if valid, or an error message string if invalid
6
+ */
7
+ export declare function validateFunctionName(value: string): true | "Function name must be in the format \"package/name\" with lowercase letters only";
8
+ //# sourceMappingURL=validators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/utils/validators.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,6FAMjD"}
@@ -0,0 +1,14 @@
1
+ import { FUNCTION_NAME_REGEX } from '../constants.js';
2
+ /**
3
+ * Validates that a function name matches the required format (package/name).
4
+ *
5
+ * @param value - The function name input to validate
6
+ * @returns true if valid, or an error message string if invalid
7
+ */
8
+ export function validateFunctionName(value) {
9
+ if (!FUNCTION_NAME_REGEX.test(value)) {
10
+ return 'Function name must be in the format "package/name" with lowercase letters only';
11
+ }
12
+ return true;
13
+ }
14
+ //# sourceMappingURL=validators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/utils/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO,gFAAgF,CAAC;IAC1F,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "do-functions-cli",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/grantchatterton/do-functions-cli.git"
8
+ },
9
+ "author": "Grant Chatterton",
10
+ "license": "MIT",
11
+ "bin": {
12
+ "do-functions-cli": "dist/cli.js"
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "format": "prettier --write .",
17
+ "format:check": "prettier --check .",
18
+ "prepublishOnly": "npm run build",
19
+ "watch": "tsc -w"
20
+ },
21
+ "dependencies": {
22
+ "@inquirer/prompts": "^8.0.1",
23
+ "commander": "^14.0.2",
24
+ "fs-extra": "^11.3.2",
25
+ "ora": "^9.0.0",
26
+ "yaml": "^2.8.1",
27
+ "zod": "^4.1.13"
28
+ },
29
+ "devDependencies": {
30
+ "@types/fs-extra": "^11.0.4",
31
+ "@types/node": "^24.10.1",
32
+ "prettier": "^3.7.2",
33
+ "typescript": "^5.9.3"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "templates"
38
+ ],
39
+ "keywords": [
40
+ "functions",
41
+ "utility",
42
+ "typescript",
43
+ "cli",
44
+ "npm",
45
+ "do",
46
+ "digitalocean",
47
+ "serverless"
48
+ ]
49
+ }
@@ -0,0 +1 @@
1
+ dist/bundle.js
@@ -0,0 +1,3 @@
1
+ export function main() {
2
+ return { body: 'Hello World!' };
3
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "scripts": {
5
+ "build": "esbuild ./index.js --bundle --platform=node --target=node18 --format=cjs --outfile=./dist/bundle.js --minify"
6
+ },
7
+ "devDependencies": {
8
+ "esbuild": "^0.27.0"
9
+ }
10
+ }
@@ -0,0 +1 @@
1
+ dist/bundle.js
@@ -0,0 +1,3 @@
1
+ export function main() {
2
+ return { body: 'Hello, World!' };
3
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "scripts": {
5
+ "build": "tsc --noEmit && esbuild ./index.ts --bundle --platform=node --target=node18 --format=cjs --outfile=./dist/bundle.js --minify"
6
+ },
7
+ "devDependencies": {
8
+ "@types/node": "^24.10.1",
9
+ "esbuild": "^0.27.0",
10
+ "typescript": "^5.9.3"
11
+ }
12
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ // Visit https://aka.ms/tsconfig to read more about this file
3
+ "compilerOptions": {
4
+ // File Layout
5
+ // "rootDir": "./src",
6
+ "outDir": "./dist",
7
+
8
+ // Environment Settings
9
+ // See also https://aka.ms/tsconfig/module
10
+ "module": "es2022",
11
+ "target": "es2022",
12
+ // For nodejs:
13
+ "lib": ["ES2022"],
14
+ "types": ["node"],
15
+ // and npm install -D @types/node
16
+
17
+ // Other Outputs
18
+ "sourceMap": true,
19
+ "declaration": true,
20
+ "declarationMap": true,
21
+
22
+ // Stricter Typechecking Options
23
+ "noUncheckedIndexedAccess": true,
24
+ "exactOptionalPropertyTypes": true,
25
+
26
+ // Style Options
27
+ // "noImplicitReturns": true,
28
+ // "noImplicitOverride": true,
29
+ // "noUnusedLocals": true,
30
+ // "noUnusedParameters": true,
31
+ // "noFallthroughCasesInSwitch": true,
32
+ // "noPropertyAccessFromIndexSignature": true,
33
+
34
+ // Recommended Options
35
+ "strict": true,
36
+ "jsx": "react-jsx",
37
+ "verbatimModuleSyntax": true,
38
+ "isolatedModules": true,
39
+ "noUncheckedSideEffectImports": true,
40
+ "moduleDetection": "force",
41
+ "skipLibCheck": true
42
+ }
43
+ }