@yc-tools/functions-yc 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.
@@ -0,0 +1,50 @@
1
+ export interface FunctionEntry {
2
+ name: string;
3
+ zipPath: string;
4
+ entry: string;
5
+ route: string;
6
+ params: string[];
7
+ memory: number;
8
+ timeout: number;
9
+ env: Record<string, string>;
10
+ }
11
+ export interface FunctionsManifest {
12
+ appName: string;
13
+ buildId: string;
14
+ timestamp: string;
15
+ functions: FunctionEntry[];
16
+ }
17
+ export interface BuildOptions {
18
+ projectPath: string;
19
+ outputDir: string;
20
+ handlersDir?: string;
21
+ appName?: string;
22
+ buildId?: string;
23
+ externalPackages?: string[];
24
+ memory?: number;
25
+ timeout?: number;
26
+ verbose?: boolean;
27
+ }
28
+ export declare class Builder {
29
+ build(options: BuildOptions): Promise<FunctionsManifest>;
30
+ }
31
+ /**
32
+ * Convert a file path (relative to handlers/) to an API route.
33
+ *
34
+ * tg/[botId]/index.ts → /tg/{botId}
35
+ * webhook.ts → /webhook
36
+ * index.ts → /
37
+ */
38
+ export declare function filePathToRoute(filePath: string): string;
39
+ /**
40
+ * Extract path parameter names from a route.
41
+ * /tg/{botId} → ['botId']
42
+ */
43
+ export declare function extractRouteParams(route: string): string[];
44
+ /**
45
+ * Convert a route to a valid terraform resource name.
46
+ * /tg/{botId} → tg-botId
47
+ * / → root
48
+ */
49
+ export declare function routeToFunctionName(route: string): string;
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/build/index.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,OAAO;IACZ,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA2G/D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQzD"}
@@ -0,0 +1,176 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import archiver from 'archiver';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import * as esbuild from 'esbuild';
7
+ import { glob } from 'glob';
8
+ export class Builder {
9
+ async build(options) {
10
+ const { projectPath, outputDir, handlersDir = 'handlers', externalPackages = [], memory = 256, timeout = 30, verbose, } = options;
11
+ const spinner = ora();
12
+ const artifactsDir = path.join(outputDir, 'artifacts');
13
+ await fs.ensureDir(artifactsDir);
14
+ const buildId = options.buildId ?? generateBuildId();
15
+ const appName = options.appName ?? path.basename(projectPath);
16
+ // Scan handlers
17
+ const handlersAbsDir = path.resolve(projectPath, handlersDir);
18
+ if (!(await fs.pathExists(handlersAbsDir))) {
19
+ throw new Error(`Handlers directory not found: ${handlersAbsDir}`);
20
+ }
21
+ spinner.start('Scanning handlers...');
22
+ const handlerFiles = await glob('**/*.ts', { cwd: handlersAbsDir, absolute: false });
23
+ if (handlerFiles.length === 0) {
24
+ throw new Error(`No .ts files found in: ${handlersAbsDir}`);
25
+ }
26
+ spinner.succeed(`Found ${handlerFiles.length} handler(s)`);
27
+ // Build each handler
28
+ const functions = [];
29
+ const tempDir = path.join(outputDir, '.tmp-build');
30
+ await fs.ensureDir(tempDir);
31
+ try {
32
+ for (const relFile of handlerFiles.sort()) {
33
+ const route = filePathToRoute(relFile);
34
+ const params = extractRouteParams(route);
35
+ const name = routeToFunctionName(route);
36
+ if (verbose) {
37
+ console.log(chalk.gray(` ${relFile} → ${route}`));
38
+ }
39
+ const entryAbsolute = path.join(handlersAbsDir, relFile);
40
+ const wrapperPath = path.join(tempDir, `${name}-entry.cjs`);
41
+ const distPath = path.join(tempDir, `${name}-bundle.cjs`);
42
+ const zipPath = path.join(artifactsDir, `${name}.zip`);
43
+ await fs.writeFile(wrapperPath, generateWrapper(entryAbsolute));
44
+ spinner.start(`Bundling ${name}...`);
45
+ await esbuild.build({
46
+ entryPoints: [wrapperPath],
47
+ bundle: true,
48
+ platform: 'node',
49
+ target: 'node20',
50
+ format: 'cjs',
51
+ outfile: distPath,
52
+ minify: true,
53
+ treeShaking: true,
54
+ logLevel: 'warning',
55
+ external: externalPackages,
56
+ });
57
+ if (externalPackages.length > 0) {
58
+ await zipBundleWithNodeModules(distPath, projectPath, externalPackages, zipPath);
59
+ }
60
+ else {
61
+ await zipFile(distPath, zipPath, 'index.js');
62
+ }
63
+ functions.push({
64
+ name,
65
+ zipPath: path.relative(outputDir, zipPath),
66
+ entry: 'index.handler',
67
+ route,
68
+ params,
69
+ memory,
70
+ timeout,
71
+ env: { NODE_ENV: 'production' },
72
+ });
73
+ spinner.succeed(`Built ${name} → ${route}`);
74
+ }
75
+ }
76
+ finally {
77
+ await fs.remove(tempDir);
78
+ }
79
+ const manifest = {
80
+ appName,
81
+ buildId,
82
+ timestamp: new Date().toISOString(),
83
+ functions,
84
+ };
85
+ const manifestPath = path.join(outputDir, 'functions.manifest.json');
86
+ await fs.writeJson(manifestPath, manifest, { spaces: 2 });
87
+ if (verbose) {
88
+ console.log(chalk.gray(` Manifest: ${manifestPath}`));
89
+ }
90
+ return manifest;
91
+ }
92
+ }
93
+ /**
94
+ * Convert a file path (relative to handlers/) to an API route.
95
+ *
96
+ * tg/[botId]/index.ts → /tg/{botId}
97
+ * webhook.ts → /webhook
98
+ * index.ts → /
99
+ */
100
+ export function filePathToRoute(filePath) {
101
+ let p = filePath.replace(/\\/g, '/');
102
+ p = p.replace(/\.ts$/, '');
103
+ p = p.replace(/\/index$/, '');
104
+ if (p === '' || p === 'index')
105
+ return '/';
106
+ p = p.replace(/\[([^\]]+)\]/g, '{$1}');
107
+ if (!p.startsWith('/'))
108
+ p = '/' + p;
109
+ return p;
110
+ }
111
+ /**
112
+ * Extract path parameter names from a route.
113
+ * /tg/{botId} → ['botId']
114
+ */
115
+ export function extractRouteParams(route) {
116
+ const params = [];
117
+ const re = /\{([^}]+)\}/g;
118
+ let m;
119
+ while ((m = re.exec(route)) !== null) {
120
+ params.push(m[1]);
121
+ }
122
+ return params;
123
+ }
124
+ /**
125
+ * Convert a route to a valid terraform resource name.
126
+ * /tg/{botId} → tg-botId
127
+ * / → root
128
+ */
129
+ export function routeToFunctionName(route) {
130
+ return (route
131
+ .replace(/^\//, '')
132
+ .replace(/\{([^}]+)\}/g, '$1')
133
+ .replace(/\//g, '-')
134
+ .replace(/[^a-zA-Z0-9-]/g, '') || 'root');
135
+ }
136
+ function generateWrapper(entryAbsolutePath) {
137
+ const escaped = JSON.stringify(entryAbsolutePath);
138
+ return `'use strict';
139
+ const _mod = require(${escaped});
140
+ const _handler = _mod.handler || _mod.default?.handler;
141
+ exports.handler = async (event, context) => {
142
+ event.params = event.pathParameters || {};
143
+ return _handler(event, context);
144
+ };
145
+ `;
146
+ }
147
+ function generateBuildId() {
148
+ return new Date().toISOString().replace(/[^0-9]/g, '').slice(0, 14);
149
+ }
150
+ async function zipFile(sourcePath, destZip, entryName) {
151
+ await new Promise((resolve, reject) => {
152
+ const output = fs.createWriteStream(destZip);
153
+ const archive = archiver('zip', { zlib: { level: 9 } });
154
+ output.on('close', resolve);
155
+ archive.on('error', reject);
156
+ archive.pipe(output);
157
+ archive.file(sourcePath, { name: entryName });
158
+ void archive.finalize();
159
+ });
160
+ }
161
+ async function zipBundleWithNodeModules(bundlePath, projectPath, externals, destZip) {
162
+ await new Promise((resolve, reject) => {
163
+ const output = fs.createWriteStream(destZip);
164
+ const archive = archiver('zip', { zlib: { level: 9 } });
165
+ output.on('close', resolve);
166
+ archive.on('error', reject);
167
+ archive.pipe(output);
168
+ archive.file(bundlePath, { name: 'index.js' });
169
+ for (const pkg of externals) {
170
+ const pkgDir = path.join(projectPath, 'node_modules', pkg);
171
+ archive.directory(pkgDir, `node_modules/${pkg}`);
172
+ }
173
+ void archive.finalize();
174
+ });
175
+ }
176
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/build/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import path from 'path';
5
+ import fs from 'fs-extra';
6
+ import { Builder } from './build/index.js';
7
+ import { Uploader } from './upload/index.js';
8
+ import { cleanupTerraformProject, extractOutputString, prepareTerraformProject, resolveBackendConfig, TerraformRunner, } from './terraform/index.js';
9
+ const program = new Command();
10
+ program
11
+ .name('functions-yc')
12
+ .description('Deploy TypeScript functions to Yandex Cloud')
13
+ .version('1.0.0');
14
+ async function loadConfig(projectPath) {
15
+ const configPath = path.join(projectPath, 'functions-yc.config.json');
16
+ if (await fs.pathExists(configPath)) {
17
+ return (await fs.readJson(configPath));
18
+ }
19
+ return {};
20
+ }
21
+ function e(key) {
22
+ return process.env[key] || undefined;
23
+ }
24
+ function first(...values) {
25
+ return values.find((v) => v !== undefined);
26
+ }
27
+ function collectExternalPackages(cliValues, config) {
28
+ if (cliValues.length > 0)
29
+ return cliValues;
30
+ if (Array.isArray(config.externalPackages))
31
+ return config.externalPackages;
32
+ return [];
33
+ }
34
+ // ── build ─────────────────────────────────────────────────────────────────────
35
+ program
36
+ .command('build')
37
+ .description('Scan handlers/, bundle with esbuild, write functions.manifest.json')
38
+ .option('-p, --project <path>', 'Project root', '.')
39
+ .option('-o, --output <path>', 'Output directory', '.fyc-out')
40
+ .option('--handlers-dir <dir>', 'Handlers directory (relative to project)', '')
41
+ .option('--app-name <name>', 'Application name')
42
+ .option('--external <pkg>', 'Mark package external and copy from node_modules (repeatable)', (v, acc) => { acc.push(v); return acc; }, [])
43
+ .option('--memory <mb>', 'Function memory in MB', '')
44
+ .option('--timeout <s>', 'Function timeout in seconds', '')
45
+ .option('-v, --verbose', 'Verbose output')
46
+ .action(async (opts) => {
47
+ try {
48
+ const projectPath = path.resolve(opts.project);
49
+ const outputDir = path.resolve(opts.output);
50
+ const config = await loadConfig(projectPath);
51
+ const builder = new Builder();
52
+ const manifest = await builder.build({
53
+ projectPath,
54
+ outputDir,
55
+ handlersDir: opts.handlersDir || config.handlersDir || 'handlers',
56
+ appName: opts.appName || config.appName,
57
+ externalPackages: collectExternalPackages(opts.external, config),
58
+ memory: opts.memory ? parseInt(opts.memory, 10) : config.memory,
59
+ timeout: opts.timeout ? parseInt(opts.timeout, 10) : config.timeout,
60
+ verbose: opts.verbose,
61
+ });
62
+ console.log(chalk.green(`\nBuild complete: ${manifest.functions.length} function(s)`));
63
+ for (const fn of manifest.functions) {
64
+ console.log(chalk.gray(` ${fn.name}: ${fn.route}`));
65
+ }
66
+ }
67
+ catch (error) {
68
+ console.error(chalk.red('Build failed:'), error instanceof Error ? error.message : String(error));
69
+ process.exit(1);
70
+ }
71
+ });
72
+ // ── deploy ────────────────────────────────────────────────────────────────────
73
+ program
74
+ .command('deploy')
75
+ .description('Build, upload artifacts, and run terraform apply')
76
+ .option('-p, --project <path>', 'Project root', '.')
77
+ .option('-o, --output <path>', 'Output directory', '.fyc-out')
78
+ .option('--handlers-dir <dir>', 'Handlers directory (relative to project)', '')
79
+ .option('--app-name <name>', 'Application name')
80
+ .option('--external <pkg>', 'Mark package external and copy from node_modules (repeatable)', (v, acc) => { acc.push(v); return acc; }, [])
81
+ .option('--memory <mb>', 'Function memory in MB', '')
82
+ .option('--timeout <s>', 'Function timeout in seconds', '')
83
+ .option('--cloud-id <id>', 'Yandex Cloud ID')
84
+ .option('--folder-id <id>', 'Yandex Cloud Folder ID')
85
+ .option('--iam-token <token>', 'Yandex Cloud IAM token')
86
+ .option('--access-key <key>', 'Object Storage access key for upload')
87
+ .option('--secret-key <key>', 'Object Storage secret key for upload')
88
+ .option('--bucket <name>', 'Deploy bucket name')
89
+ .option('--state-bucket <name>', 'Terraform S3 state bucket')
90
+ .option('--state-key <key>', 'Terraform S3 state key')
91
+ .option('--nodejs-version <ver>', 'Node.js version (nodejs18/nodejs20/nodejs22)', '')
92
+ .option('--domain <name>', 'Custom domain name')
93
+ .option('--env <env>', 'Environment (dev/staging/production)', '')
94
+ .option('--auto-approve', 'Run terraform with -auto-approve')
95
+ .option('-v, --verbose', 'Verbose output')
96
+ .action(async (opts) => {
97
+ const projectPath = path.resolve(opts.project);
98
+ const outputDir = path.resolve(opts.output);
99
+ const config = await loadConfig(projectPath);
100
+ const cloudId = first(opts.cloudId, e('FYC_CLOUD_ID'), config.cloudId);
101
+ const folderId = first(opts.folderId, e('FYC_FOLDER_ID'), config.folderId);
102
+ const iamToken = first(opts.iamToken, e('FYC_IAM_TOKEN'), config.iamToken);
103
+ const accessKey = first(opts.accessKey, e('FYC_STORAGE_ACCESS_KEY'), config.storageAccessKey);
104
+ const secretKey = first(opts.secretKey, e('FYC_STORAGE_SECRET_KEY'), config.storageSecretKey);
105
+ const stateBucket = first(opts.stateBucket, e('FYC_STATE_BUCKET'), config.stateBucket);
106
+ const stateKey = first(opts.stateKey, e('FYC_STATE_KEY'), config.stateKey);
107
+ const appName = first(opts.appName, e('FYC_APP_NAME'), config.appName);
108
+ const nodejsVersion = first(opts.nodejsVersion ? opts.nodejsVersion : undefined, config.nodejsVersion, 'nodejs20');
109
+ const environment = first(opts.env ? opts.env : undefined, config.env, 'production');
110
+ const autoApprove = opts.autoApprove ||
111
+ e('FYC_AUTO_APPROVE') === 'true' ||
112
+ config.autoApprove ||
113
+ false;
114
+ const domainName = first(opts.domain, config.domainName);
115
+ const terraformDir = await prepareTerraformProject();
116
+ try {
117
+ // 1. Build
118
+ const builder = new Builder();
119
+ const manifest = await builder.build({
120
+ projectPath,
121
+ outputDir,
122
+ handlersDir: opts.handlersDir || config.handlersDir || 'handlers',
123
+ appName,
124
+ externalPackages: collectExternalPackages(opts.external, config),
125
+ memory: opts.memory ? parseInt(opts.memory, 10) : config.memory,
126
+ timeout: opts.timeout ? parseInt(opts.timeout, 10) : config.timeout,
127
+ verbose: opts.verbose,
128
+ });
129
+ // 2. Terraform init
130
+ const terraform = new TerraformRunner(terraformDir);
131
+ const backend = resolveBackendConfig({ stateBucket, stateKey }, {
132
+ ...process.env,
133
+ YC_REGION: 'ru-central1',
134
+ YC_ACCESS_KEY: accessKey,
135
+ YC_SECRET_KEY: secretKey,
136
+ });
137
+ await terraform.init(backend || undefined);
138
+ // 3. Upload
139
+ const resolvedAccessKey = accessKey ?? '';
140
+ const resolvedSecretKey = secretKey ?? '';
141
+ if (!resolvedAccessKey || !resolvedSecretKey) {
142
+ throw new Error('Object Storage credentials required for upload. Provide --access-key/--secret-key or FYC_STORAGE_ACCESS_KEY/FYC_STORAGE_SECRET_KEY.');
143
+ }
144
+ // Try to get existing bucket from terraform state, fall back to CLI/config
145
+ let deployBucket = first(opts.bucket, config.deployBucketName);
146
+ if (!deployBucket) {
147
+ try {
148
+ const outputs = await terraform.readOutputs();
149
+ deployBucket = extractOutputString(outputs, 'deploy_bucket');
150
+ }
151
+ catch {
152
+ // no state yet — bucket will be created by terraform
153
+ }
154
+ }
155
+ const uploader = new Uploader();
156
+ await uploader.upload({
157
+ outputDir,
158
+ manifest,
159
+ bucket: deployBucket ?? `${(appName ?? path.basename(projectPath)).toLowerCase()}-${environment}-deploy`,
160
+ accessKey: resolvedAccessKey,
161
+ secretKey: resolvedSecretKey,
162
+ verbose: opts.verbose,
163
+ });
164
+ // 4. Terraform apply
165
+ const tfVarEnv = {
166
+ ...process.env,
167
+ TF_VAR_manifest_path: path.join(outputDir, 'functions.manifest.json'),
168
+ TF_VAR_app_name: appName ?? path.basename(projectPath).toLowerCase().replace(/[^a-z0-9-]/g, '-'),
169
+ TF_VAR_env: environment,
170
+ TF_VAR_nodejs_version: nodejsVersion,
171
+ };
172
+ if (cloudId)
173
+ tfVarEnv['TF_VAR_cloud_id'] = cloudId;
174
+ if (folderId)
175
+ tfVarEnv['TF_VAR_folder_id'] = folderId;
176
+ if (iamToken)
177
+ tfVarEnv['TF_VAR_iam_token'] = iamToken;
178
+ if (resolvedAccessKey)
179
+ tfVarEnv['TF_VAR_storage_access_key'] = resolvedAccessKey;
180
+ if (resolvedSecretKey)
181
+ tfVarEnv['TF_VAR_storage_secret_key'] = resolvedSecretKey;
182
+ if (deployBucket)
183
+ tfVarEnv['TF_VAR_deploy_bucket_name'] = deployBucket;
184
+ if (domainName)
185
+ tfVarEnv['TF_VAR_domain_name'] = domainName;
186
+ await terraform.apply({ autoApprove, env: tfVarEnv });
187
+ // Print outputs
188
+ const outputs = await terraform.readOutputs(tfVarEnv);
189
+ const url = extractOutputString(outputs, 'api_gateway_url');
190
+ if (url) {
191
+ console.log(chalk.green(`\nDeploy complete: ${url}`));
192
+ }
193
+ else {
194
+ console.log(chalk.green('\nDeploy complete'));
195
+ }
196
+ }
197
+ catch (error) {
198
+ console.error(chalk.red('Deploy failed:'), error instanceof Error ? error.message : String(error));
199
+ process.exit(1);
200
+ }
201
+ finally {
202
+ await cleanupTerraformProject(terraformDir);
203
+ }
204
+ });
205
+ program.parse(process.argv);
206
+ if (!process.argv.slice(2).length) {
207
+ program.outputHelp();
208
+ }
209
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ export interface TerraformBackendConfig {
2
+ bucket: string;
3
+ key: string;
4
+ region: string;
5
+ endpoint: string;
6
+ accessKey: string;
7
+ secretKey: string;
8
+ }
9
+ export interface TerraformBackendInput {
10
+ stateBucket?: string;
11
+ stateKey?: string;
12
+ stateRegion?: string;
13
+ stateEndpoint?: string;
14
+ stateAccessKey?: string;
15
+ stateSecretKey?: string;
16
+ }
17
+ export interface TerraformRunOptions {
18
+ captureOutput?: boolean;
19
+ env?: NodeJS.ProcessEnv;
20
+ }
21
+ export interface TerraformApplyOptions {
22
+ targets?: string[];
23
+ replace?: string[];
24
+ autoApprove?: boolean;
25
+ refresh?: boolean;
26
+ env?: NodeJS.ProcessEnv;
27
+ }
28
+ interface TerraformOutputEntry {
29
+ sensitive?: boolean;
30
+ type?: unknown;
31
+ value: unknown;
32
+ }
33
+ export declare function resolveBackendConfig(input: TerraformBackendInput, env?: NodeJS.ProcessEnv): TerraformBackendConfig | null;
34
+ export declare function extractOutputString(outputs: Record<string, TerraformOutputEntry>, key: string): string | undefined;
35
+ export declare function prepareTerraformProject(): Promise<string>;
36
+ export declare function cleanupTerraformProject(terraformDir: string): Promise<void>;
37
+ export declare class TerraformRunner {
38
+ private readonly terraformDir;
39
+ private readonly terraformBin;
40
+ constructor(terraformDir: string, terraformBin?: string);
41
+ init(backend?: TerraformBackendConfig, env?: NodeJS.ProcessEnv): Promise<void>;
42
+ apply(options?: TerraformApplyOptions): Promise<void>;
43
+ readOutputs(env?: NodeJS.ProcessEnv): Promise<Record<string, TerraformOutputEntry>>;
44
+ private run;
45
+ }
46
+ export {};
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/terraform/index.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,UAAU,oBAAoB;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB;AAKD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,qBAAqB,EAC5B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,sBAAsB,GAAG,IAAI,CAsB/B;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAC7C,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAOpB;AAED,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC,CAQ/D;AAED,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjF;AAED,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY;gBADZ,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,MAAoB;IAG/C,IAAI,CAAC,OAAO,CAAC,EAAE,sBAAsB,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9E,KAAK,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBzD,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;YAiB3E,GAAG;CA2ClB"}
@@ -0,0 +1,137 @@
1
+ import fs from 'fs-extra';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import { spawn } from 'child_process';
5
+ import { fileURLToPath } from 'url';
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const TERRAFORM_TEMPLATE_DIR = path.join(__dirname, 'project');
8
+ export function resolveBackendConfig(input, env = process.env) {
9
+ const bucket = input.stateBucket || env['TF_STATE_BUCKET'] || '';
10
+ const key = input.stateKey || env['TF_STATE_KEY'] || '';
11
+ if (!bucket || !key) {
12
+ return null;
13
+ }
14
+ const region = input.stateRegion || env['YC_REGION'] || 'ru-central1';
15
+ const endpoint = input.stateEndpoint || env['TF_STATE_ENDPOINT'] || 'https://storage.yandexcloud.net';
16
+ const accessKey = input.stateAccessKey || env['YC_ACCESS_KEY'] || env['AWS_ACCESS_KEY_ID'] || '';
17
+ const secretKey = input.stateSecretKey || env['YC_SECRET_KEY'] || env['AWS_SECRET_ACCESS_KEY'] || '';
18
+ if (!accessKey || !secretKey) {
19
+ throw new Error('Backend credentials are required: provide YC_ACCESS_KEY/YC_SECRET_KEY (or AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY).');
20
+ }
21
+ return { bucket, key, region, endpoint, accessKey, secretKey };
22
+ }
23
+ export function extractOutputString(outputs, key) {
24
+ const entry = outputs[key];
25
+ if (!entry)
26
+ return undefined;
27
+ if (entry.value === null || entry.value === undefined)
28
+ return undefined;
29
+ const value = String(entry.value).trim();
30
+ if (value === '' || value === 'null')
31
+ return undefined;
32
+ return value;
33
+ }
34
+ export async function prepareTerraformProject() {
35
+ if (!(await fs.pathExists(TERRAFORM_TEMPLATE_DIR))) {
36
+ throw new Error(`Embedded terraform template not found: ${TERRAFORM_TEMPLATE_DIR}`);
37
+ }
38
+ const workingDir = await fs.mkdtemp(path.join(os.tmpdir(), 'functions-yc-terraform-'));
39
+ await fs.copy(TERRAFORM_TEMPLATE_DIR, workingDir);
40
+ return workingDir;
41
+ }
42
+ export async function cleanupTerraformProject(terraformDir) {
43
+ await fs.remove(terraformDir);
44
+ }
45
+ export class TerraformRunner {
46
+ terraformDir;
47
+ terraformBin;
48
+ constructor(terraformDir, terraformBin = 'terraform') {
49
+ this.terraformDir = terraformDir;
50
+ this.terraformBin = terraformBin;
51
+ }
52
+ async init(backend, env) {
53
+ const args = ['init'];
54
+ if (backend) {
55
+ args.push(`-backend-config=bucket=${backend.bucket}`);
56
+ args.push(`-backend-config=key=${backend.key}`);
57
+ args.push(`-backend-config=region=${backend.region}`);
58
+ args.push(`-backend-config=endpoint=${backend.endpoint}`);
59
+ args.push('-backend-config=skip_region_validation=true');
60
+ args.push('-backend-config=skip_credentials_validation=true');
61
+ args.push('-backend-config=skip_metadata_api_check=true');
62
+ args.push('-backend-config=skip_requesting_account_id=true');
63
+ args.push(`-backend-config=access_key=${backend.accessKey}`);
64
+ args.push(`-backend-config=secret_key=${backend.secretKey}`);
65
+ }
66
+ await this.run(args, { env });
67
+ }
68
+ async apply(options = {}) {
69
+ const args = ['apply'];
70
+ if (options.autoApprove) {
71
+ args.push('-auto-approve');
72
+ }
73
+ if (options.refresh === false) {
74
+ args.push('-refresh=false');
75
+ }
76
+ for (const target of options.targets || []) {
77
+ args.push(`-target=${target}`);
78
+ }
79
+ for (const replaceTarget of options.replace || []) {
80
+ args.push(`-replace=${replaceTarget}`);
81
+ }
82
+ await this.run(args, { env: options.env });
83
+ }
84
+ async readOutputs(env) {
85
+ try {
86
+ const { stdout } = await this.run(['output', '-json'], { captureOutput: true, env });
87
+ if (!stdout.trim())
88
+ return {};
89
+ return JSON.parse(stdout);
90
+ }
91
+ catch (error) {
92
+ const message = error instanceof Error ? error.message : String(error);
93
+ if (message.includes('No outputs found') ||
94
+ message.includes('state file either has no outputs defined')) {
95
+ return {};
96
+ }
97
+ throw error;
98
+ }
99
+ }
100
+ async run(args, options = {}) {
101
+ const captureOutput = options.captureOutput ?? false;
102
+ const MAX_CAPTURED_OUTPUT = 256 * 1024;
103
+ return new Promise((resolve, reject) => {
104
+ const child = spawn(this.terraformBin, args, {
105
+ cwd: this.terraformDir,
106
+ env: { ...process.env, ...options.env },
107
+ stdio: 'pipe',
108
+ });
109
+ let stdout = '';
110
+ let stderr = '';
111
+ const appendOutput = (current, chunk) => {
112
+ const next = current + String(chunk);
113
+ return next.length <= MAX_CAPTURED_OUTPUT ? next : next.slice(-MAX_CAPTURED_OUTPUT);
114
+ };
115
+ child.stdout?.on('data', (chunk) => {
116
+ stdout = appendOutput(stdout, chunk);
117
+ if (!captureOutput)
118
+ process.stdout.write(chunk);
119
+ });
120
+ child.stderr?.on('data', (chunk) => {
121
+ stderr = appendOutput(stderr, chunk);
122
+ if (!captureOutput)
123
+ process.stderr.write(chunk);
124
+ });
125
+ child.on('error', (err) => reject(err));
126
+ child.on('close', (code) => {
127
+ if (code === 0) {
128
+ resolve({ stdout, stderr });
129
+ return;
130
+ }
131
+ const command = [this.terraformBin, ...args].join(' ');
132
+ reject(new Error(`Command failed (${code}): ${command}\n${stderr || stdout}`));
133
+ });
134
+ });
135
+ }
136
+ }
137
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/terraform/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ terraform {
2
+ backend "s3" {}
3
+ }
@@ -0,0 +1,183 @@
1
+ locals {
2
+ manifest = jsondecode(file(var.manifest_path))
3
+ app_id = "${var.app_name}-${var.env}"
4
+ functions_map = { for fn in local.manifest.functions : fn.name => fn }
5
+ deploy_bucket = trimspace(var.deploy_bucket_name) != "" ? var.deploy_bucket_name : "${local.app_id}-deploy"
6
+ has_domain = trimspace(var.domain_name) != ""
7
+ }
8
+
9
+ # ── Deploy bucket ─────────────────────────────────────────────────────────────
10
+
11
+ resource "yandex_storage_bucket" "deploy" {
12
+ bucket = local.deploy_bucket
13
+ acl = "private"
14
+
15
+ versioning {
16
+ enabled = false
17
+ }
18
+ }
19
+
20
+ # ── Service account ───────────────────────────────────────────────────────────
21
+
22
+ resource "yandex_iam_service_account" "sa" {
23
+ name = "${local.app_id}-sa"
24
+ description = "Service account for ${var.app_name} functions"
25
+ folder_id = var.folder_id
26
+ }
27
+
28
+ resource "yandex_resourcemanager_folder_iam_member" "invoker" {
29
+ folder_id = var.folder_id
30
+ role = "serverless.functions.invoker"
31
+ member = "serviceAccount:${yandex_iam_service_account.sa.id}"
32
+ }
33
+
34
+ resource "yandex_resourcemanager_folder_iam_member" "storage_viewer" {
35
+ folder_id = var.folder_id
36
+ role = "storage.viewer"
37
+ member = "serviceAccount:${yandex_iam_service_account.sa.id}"
38
+ }
39
+
40
+ # ── Cloud Functions ───────────────────────────────────────────────────────────
41
+
42
+ resource "yandex_function" "fn" {
43
+ for_each = local.functions_map
44
+
45
+ name = "${local.app_id}-${each.key}-fn"
46
+ description = "Function ${each.key} for ${var.app_name}"
47
+ folder_id = var.folder_id
48
+ runtime = var.nodejs_version
49
+ entrypoint = each.value.entry
50
+ memory = try(each.value.memory, var.function_memory)
51
+ execution_timeout = tostring(try(each.value.timeout, var.function_timeout))
52
+
53
+ user_hash = each.value.zipPath
54
+
55
+ package {
56
+ bucket_name = yandex_storage_bucket.deploy.bucket
57
+ object_name = "functions/${each.value.zipPath}"
58
+ }
59
+
60
+ service_account_id = yandex_iam_service_account.sa.id
61
+
62
+ environment = try(each.value.env, { NODE_ENV = "production" })
63
+
64
+ log_options {
65
+ disabled = false
66
+ min_level = "WARN"
67
+ }
68
+
69
+ depends_on = [yandex_storage_bucket.deploy]
70
+ }
71
+
72
+ # ── API Gateway ───────────────────────────────────────────────────────────────
73
+
74
+ locals {
75
+ # Build OpenAPI paths inline using actual function IDs
76
+ openapi_paths = merge([
77
+ for fn in local.manifest.functions : {
78
+ (fn.route) = {
79
+ "x-yc-apigateway-any-method" = {
80
+ operationId = fn.name
81
+ parameters = [
82
+ for p in fn.params : {
83
+ name = p
84
+ in = "path"
85
+ required = true
86
+ schema = { type = "string" }
87
+ }
88
+ ]
89
+ "x-yc-apigateway-integration" = {
90
+ type = "cloud_functions"
91
+ function_id = yandex_function.fn[fn.name].id
92
+ service_account_id = yandex_iam_service_account.sa.id
93
+ payload_format_version = "1.0"
94
+ }
95
+ }
96
+ }
97
+ }
98
+ ]...)
99
+
100
+ openapi_spec = jsonencode({
101
+ openapi = "3.0.0"
102
+ info = { title = var.app_name, version = "1.0.0" }
103
+ paths = local.openapi_paths
104
+ })
105
+ }
106
+
107
+ resource "yandex_api_gateway" "main" {
108
+ name = "${local.app_id}-apigw"
109
+ description = "API Gateway for ${var.app_name}"
110
+ folder_id = var.folder_id
111
+
112
+ spec = local.openapi_spec
113
+
114
+ dynamic "custom_domains" {
115
+ for_each = local.has_domain ? [1] : []
116
+ content {
117
+ fqdn = var.domain_name
118
+ certificate_id = local.effective_cert_id
119
+ }
120
+ }
121
+
122
+ depends_on = [yandex_function.fn]
123
+ }
124
+
125
+ # ── TLS Certificate ───────────────────────────────────────────────────────────
126
+
127
+ resource "yandex_cm_certificate" "main" {
128
+ count = local.has_domain && trimspace(var.certificate_id) == "" ? 1 : 0
129
+ name = "${local.app_id}-cert"
130
+ folder_id = var.folder_id
131
+
132
+ domains = [var.domain_name]
133
+
134
+ managed {
135
+ challenge_type = "DNS_CNAME"
136
+ }
137
+ }
138
+
139
+ locals {
140
+ effective_cert_id = trimspace(var.certificate_id) != "" ? var.certificate_id : (
141
+ length(yandex_cm_certificate.main) > 0 ? yandex_cm_certificate.main[0].id : ""
142
+ )
143
+ }
144
+
145
+ # ── DNS Zone ──────────────────────────────────────────────────────────────────
146
+
147
+ resource "yandex_dns_zone" "main" {
148
+ count = local.has_domain && var.create_dns_zone && trimspace(var.dns_zone_id) == "" ? 1 : 0
149
+ name = "${local.app_id}-zone"
150
+ zone = "${var.domain_name}."
151
+ public = true
152
+ folder_id = var.folder_id
153
+ }
154
+
155
+ locals {
156
+ effective_dns_zone_id = trimspace(var.dns_zone_id) != "" ? var.dns_zone_id : (
157
+ length(yandex_dns_zone.main) > 0 ? yandex_dns_zone.main[0].id : ""
158
+ )
159
+ }
160
+
161
+ resource "yandex_dns_recordset" "apigw_cname" {
162
+ count = local.has_domain && local.effective_dns_zone_id != "" ? 1 : 0
163
+ zone_id = local.effective_dns_zone_id
164
+ name = "${var.domain_name}."
165
+ type = "CNAME"
166
+ ttl = 300
167
+ data = ["${yandex_api_gateway.main.domain}."]
168
+ }
169
+
170
+ resource "yandex_dns_recordset" "cert_validation" {
171
+ count = (
172
+ local.has_domain &&
173
+ local.effective_dns_zone_id != "" &&
174
+ length(yandex_cm_certificate.main) > 0 &&
175
+ length(yandex_cm_certificate.main[0].challenges) > 0
176
+ ) ? 1 : 0
177
+
178
+ zone_id = local.effective_dns_zone_id
179
+ name = yandex_cm_certificate.main[0].challenges[0].dns_name
180
+ type = yandex_cm_certificate.main[0].challenges[0].dns_type
181
+ ttl = 60
182
+ data = [yandex_cm_certificate.main[0].challenges[0].dns_value]
183
+ }
@@ -0,0 +1,29 @@
1
+ output "api_gateway_domain" {
2
+ description = "Default API Gateway domain"
3
+ value = yandex_api_gateway.main.domain
4
+ }
5
+
6
+ output "api_gateway_id" {
7
+ description = "API Gateway ID"
8
+ value = yandex_api_gateway.main.id
9
+ }
10
+
11
+ output "api_gateway_url" {
12
+ description = "API Gateway URL"
13
+ value = "https://${yandex_api_gateway.main.domain}"
14
+ }
15
+
16
+ output "custom_domain" {
17
+ description = "Custom domain (if configured)"
18
+ value = local.has_domain ? var.domain_name : null
19
+ }
20
+
21
+ output "deploy_bucket" {
22
+ description = "Object Storage bucket for function artifacts"
23
+ value = yandex_storage_bucket.deploy.bucket
24
+ }
25
+
26
+ output "service_account_id" {
27
+ description = "Service account ID for functions"
28
+ value = yandex_iam_service_account.sa.id
29
+ }
@@ -0,0 +1,8 @@
1
+ provider "yandex" {
2
+ token = var.iam_token
3
+ cloud_id = var.cloud_id
4
+ folder_id = var.folder_id
5
+ zone = var.zone
6
+ storage_access_key = trimspace(var.storage_access_key) != "" ? var.storage_access_key : null
7
+ storage_secret_key = trimspace(var.storage_secret_key) != "" ? var.storage_secret_key : null
8
+ }
@@ -0,0 +1,120 @@
1
+ variable "cloud_id" {
2
+ description = "Yandex Cloud ID"
3
+ type = string
4
+ }
5
+
6
+ variable "folder_id" {
7
+ description = "Yandex Cloud folder ID"
8
+ type = string
9
+ }
10
+
11
+ variable "iam_token" {
12
+ description = "Yandex Cloud IAM token"
13
+ type = string
14
+ sensitive = true
15
+ }
16
+
17
+ variable "app_name" {
18
+ description = "Application name (lowercase alphanumeric + hyphens, 3-31 chars)"
19
+ type = string
20
+
21
+ validation {
22
+ condition = can(regex("^[a-z0-9][a-z0-9-]{1,29}[a-z0-9]$", var.app_name))
23
+ error_message = "app_name must be 3-31 lowercase alphanumeric characters or hyphens."
24
+ }
25
+ }
26
+
27
+ variable "env" {
28
+ description = "Deployment environment (dev, staging, production)"
29
+ type = string
30
+ default = "production"
31
+
32
+ validation {
33
+ condition = contains(["dev", "staging", "production"], var.env)
34
+ error_message = "env must be one of: dev, staging, production."
35
+ }
36
+ }
37
+
38
+ variable "region" {
39
+ description = "Yandex Cloud region"
40
+ type = string
41
+ default = "ru-central1"
42
+ }
43
+
44
+ variable "zone" {
45
+ description = "Yandex Cloud availability zone"
46
+ type = string
47
+ default = "ru-central1-a"
48
+ }
49
+
50
+ variable "nodejs_version" {
51
+ description = "Node.js runtime version for Cloud Functions"
52
+ type = string
53
+ default = "nodejs20"
54
+
55
+ validation {
56
+ condition = contains(["nodejs18", "nodejs20", "nodejs22"], var.nodejs_version)
57
+ error_message = "nodejs_version must be one of: nodejs18, nodejs20, nodejs22."
58
+ }
59
+ }
60
+
61
+ variable "function_memory" {
62
+ description = "Memory (MB) for Cloud Functions"
63
+ type = number
64
+ default = 256
65
+ }
66
+
67
+ variable "function_timeout" {
68
+ description = "Execution timeout (seconds) for Cloud Functions"
69
+ type = number
70
+ default = 30
71
+ }
72
+
73
+ variable "storage_access_key" {
74
+ description = "Yandex Object Storage access key"
75
+ type = string
76
+ default = ""
77
+ sensitive = true
78
+ }
79
+
80
+ variable "storage_secret_key" {
81
+ description = "Yandex Object Storage secret key"
82
+ type = string
83
+ default = ""
84
+ sensitive = true
85
+ }
86
+
87
+ variable "deploy_bucket_name" {
88
+ description = "Bucket for storing function artifacts (created if empty)"
89
+ type = string
90
+ default = ""
91
+ }
92
+
93
+ variable "manifest_path" {
94
+ description = "Path to the functions.manifest.json file"
95
+ type = string
96
+ }
97
+
98
+ variable "domain_name" {
99
+ description = "Custom domain for the API Gateway"
100
+ type = string
101
+ default = ""
102
+ }
103
+
104
+ variable "dns_zone_id" {
105
+ description = "Existing DNS zone ID (created if empty and domain_name is set)"
106
+ type = string
107
+ default = ""
108
+ }
109
+
110
+ variable "certificate_id" {
111
+ description = "Existing TLS certificate ID (created if empty and domain_name is set)"
112
+ type = string
113
+ default = ""
114
+ }
115
+
116
+ variable "create_dns_zone" {
117
+ description = "Create a new DNS zone for domain_name"
118
+ type = bool
119
+ default = false
120
+ }
@@ -0,0 +1,10 @@
1
+ terraform {
2
+ required_version = ">= 1.5.0"
3
+
4
+ required_providers {
5
+ yandex = {
6
+ source = "yandex-cloud/yandex"
7
+ version = "~> 0.100.0"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,16 @@
1
+ import type { FunctionsManifest } from '../build/index.js';
2
+ export interface UploadOptions {
3
+ outputDir: string;
4
+ manifest: FunctionsManifest;
5
+ bucket: string;
6
+ accessKey: string;
7
+ secretKey: string;
8
+ region?: string;
9
+ endpoint?: string;
10
+ dryRun?: boolean;
11
+ verbose?: boolean;
12
+ }
13
+ export declare class Uploader {
14
+ upload(options: UploadOptions): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upload/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,QAAQ;IACb,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CAgDpD"}
@@ -0,0 +1,45 @@
1
+ import path from 'path';
2
+ import fs from 'fs-extra';
3
+ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
4
+ import { Upload } from '@aws-sdk/lib-storage';
5
+ export class Uploader {
6
+ async upload(options) {
7
+ const { outputDir, manifest, bucket, accessKey, secretKey, region, endpoint, dryRun, verbose } = options;
8
+ const s3 = new S3Client({
9
+ region: region ?? 'ru-central1',
10
+ endpoint: endpoint ?? 'https://storage.yandexcloud.net',
11
+ credentials: { accessKeyId: accessKey, secretAccessKey: secretKey },
12
+ });
13
+ for (const fn of manifest.functions) {
14
+ const zipPath = path.join(outputDir, fn.zipPath);
15
+ const key = `functions/${fn.zipPath}`;
16
+ if (verbose) {
17
+ console.log(` Uploading ${fn.zipPath} → s3://${bucket}/${key}`);
18
+ }
19
+ if (!dryRun) {
20
+ const fileStream = fs.createReadStream(zipPath);
21
+ const upload = new Upload({
22
+ client: s3,
23
+ params: { Bucket: bucket, Key: key, Body: fileStream },
24
+ });
25
+ await upload.done();
26
+ }
27
+ }
28
+ // Upload manifest
29
+ const manifestPath = path.join(outputDir, 'functions.manifest.json');
30
+ const manifestKey = 'functions.manifest.json';
31
+ if (verbose) {
32
+ console.log(` Uploading manifest → s3://${bucket}/${manifestKey}`);
33
+ }
34
+ if (!dryRun) {
35
+ const content = await fs.readFile(manifestPath);
36
+ await s3.send(new PutObjectCommand({
37
+ Bucket: bucket,
38
+ Key: manifestKey,
39
+ Body: content,
40
+ ContentType: 'application/json',
41
+ }));
42
+ }
43
+ }
44
+ }
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/upload/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAe9C,MAAM,OAAO,QAAQ;IACnB,KAAK,CAAC,MAAM,CAAC,OAAsB;QACjC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAC5F,OAAO,CAAC;QAEV,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC;YACtB,MAAM,EAAE,MAAM,IAAI,aAAa;YAC/B,QAAQ,EAAE,QAAQ,IAAI,iCAAiC;YACvD,WAAW,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE;SACpE,CAAC,CAAC;QAEH,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;YACjD,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC;YAEtC,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,OAAO,WAAW,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;YACnE,CAAC;YAED,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,UAAU,GAAG,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBAChD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;oBACxB,MAAM,EAAE,EAAE;oBACV,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE;iBACvD,CAAC,CAAC;gBACH,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACtB,CAAC;QACH,CAAC;QAED,kBAAkB;QAClB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC;QACrE,MAAM,WAAW,GAAG,yBAAyB,CAAC;QAE9C,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,+BAA+B,MAAM,IAAI,WAAW,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAChD,MAAM,EAAE,CAAC,IAAI,CACX,IAAI,gBAAgB,CAAC;gBACnB,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,WAAW;gBAChB,IAAI,EAAE,OAAO;gBACb,WAAW,EAAE,kBAAkB;aAChC,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@yc-tools/functions-yc",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool for deploying TypeScript functions to Yandex Cloud",
5
+ "license": "MIT",
6
+ "author": "yc-tools Contributors",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/yc-tools/functions-yc.git"
10
+ },
11
+ "publishConfig": {
12
+ "registry": "https://registry.npmjs.org",
13
+ "access": "public",
14
+ "provenance": true
15
+ },
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "bin": {
19
+ "functions-yc": "./dist/index.js"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc && cp -r src/terraform/project dist/terraform/project && chmod +x dist/index.js",
27
+ "dev": "tsc --watch",
28
+ "clean": "rm -rf dist",
29
+ "lint": "tsc --noEmit",
30
+ "prepublishOnly": "npm run clean && npm run build"
31
+ },
32
+ "dependencies": {
33
+ "@aws-sdk/client-s3": "^3.490.0",
34
+ "@aws-sdk/lib-storage": "^3.490.0",
35
+ "archiver": "^6.0.1",
36
+ "chalk": "^5.3.0",
37
+ "commander": "^11.1.0",
38
+ "esbuild": "^0.19.11",
39
+ "fs-extra": "^11.2.0",
40
+ "glob": "^11.0.1",
41
+ "ora": "^8.0.1"
42
+ },
43
+ "devDependencies": {
44
+ "@types/archiver": "^6.0.2",
45
+ "@types/fs-extra": "^11.0.4",
46
+ "@types/node": "^20.0.0",
47
+ "typescript": "^5.3.3"
48
+ },
49
+ "keywords": [
50
+ "yandex-cloud",
51
+ "serverless",
52
+ "functions",
53
+ "deploy",
54
+ "terraform"
55
+ ],
56
+ "engines": {
57
+ "node": ">=20.0.0"
58
+ }
59
+ }