New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

google-cloud-sql

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

google-cloud-sql - npm Package Compare versions

Package version was removed
This package version has been unpublished, mostly likely due to security reasons
Comparing version
2.0.0
to
3.0.1
dist/index.cjs

Sorry, the diff of this file is too big to display

+39
-31
{
"name": "google-cloud-sql",
"version": "2.0.0",
"description": "Connect to private Google Cloud SQL instance through Cloud SQL Auth Proxy running in GKE cluster.",
"version": "3.0.1",
"description": "Connect to private Google Cloud SQL/AlloyDB instance through Cloud SQL/AlloyDB Auth Proxy running in GKE cluster.",
"type": "module",
"license": "UNLICENSED",

@@ -16,17 +17,23 @@ "author": "Dinko Osrecki <dinko.osrecki@emarsys.com>",

"bin": {
"google-cloud-sql": "dist/index.js"
"google-cloud-sql": "dist/index.cjs"
},
"files": [
"dist"
],
"packageManager": "pnpm@10.28.0",
"engines": {
"node": ">= 20"
"node": "24.12.0",
"pnpm": "10.28.0"
},
"scripts": {
"clean": "rimraf dist bin",
"prebuild": "npm run clean",
"build": "tsc --project tsconfig.build.json",
"bundle": "npm run build && pkg . --targets node18-linux,node18-macos,node18-win --out-dir bin",
"exec:dev": "ts-node src/index.ts",
"exec:dist": "node dist/index.js",
"prebuild": "pnpm clean",
"build": "tsdown",
"bundle": "pkg dist/index.cjs --targets node24-linux,node24-macos,node24-win -o bin/google-cloud-sql",
"exec:dev": "tsx src/index.ts",
"exec:dist": "node dist/index.cjs",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"test": "npm run lint",
"test": "pnpm lint",
"typecheck": "tsc --noEmit",
"prepare": "husky",

@@ -36,39 +43,40 @@ "prettify-package-json": "prettier-package-json --write"

"dependencies": {
"boxen": "5.1.2",
"chalk": "4.1.2",
"boxen": "8.0.1",
"chalk": "5.6.2",
"commander": "14.0.2",
"conf": "10.2.0",
"exit-hook": "2.2.1",
"conf": "15.0.2",
"exit-hook": "5.0.1",
"fuse.js": "7.1.0",
"inquirer": "8.2.5",
"inquirer-autocomplete-prompt": "2.0.0",
"lodash": "4.17.21",
"inquirer": "9.3.8",
"inquirer-autocomplete-prompt": "3.0.1",
"lodash-es": "4.17.22",
"memoizee": "0.4.17",
"shelljs": "0.10.0",
"update-notifier": "5.1.0"
"update-notifier": "7.3.1"
},
"devDependencies": {
"@eslint/js": "9.39.1",
"@eslint/js": "9.39.2",
"@semantic-release/changelog": "6.0.3",
"@semantic-release/exec": "7.1.0",
"@semantic-release/git": "10.0.1",
"@stylistic/eslint-plugin": "5.6.1",
"@tsconfig/node22": "22.0.5",
"@types/inquirer": "8.2.6",
"@types/inquirer-autocomplete-prompt": "2.0.0",
"@types/lodash": "4.17.21",
"@stylistic/eslint-plugin": "5.7.0",
"@tsconfig/node24": "24.0.3",
"@types/inquirer": "^9.0.9",
"@types/inquirer-autocomplete-prompt": "3.0.3",
"@types/lodash-es": "4.17.12",
"@types/memoizee": "0.4.12",
"@types/node": "22.19.0",
"@types/shelljs": "0.8.17",
"@types/update-notifier": "5.1.0",
"eslint": "9.39.1",
"@types/node": "24.10.6",
"@types/shelljs": "0.10.0",
"@types/update-notifier": "6.0.8",
"@yao-pkg/pkg": "6.11.0",
"eslint": "9.39.2",
"husky": "9.1.7",
"pkg": "5.8.1",
"prettier-package-json": "2.8.0",
"rimraf": "6.1.2",
"semantic-release": "25.0.2",
"ts-node": "10.9.2",
"tsdown": "0.19.0",
"tsx": "4.21.0",
"typescript": "5.9.3",
"typescript-eslint": "8.48.1"
"typescript-eslint": "8.52.0"
}
}

@@ -85,4 +85,3 @@ :toc: macro

----
npm run clean
npm run build
pnpm build
----

@@ -92,15 +91,19 @@

=== Pre-configured
Package the app for Node.JS 18 on MacOS, Linux, and Windows.
Package the app for Node.JS 24 on MacOS, Linux, and Windows.
[source,bash]
----
npm run bundle
pnpm bundle
----
=== Manual
See https://www.npmjs.com/package/pkg#targets[pkg] for details.
See https://github.com/yao-pkg/pkg?tab=readme-ov-file#targets[pkg] for details.
[source,bash]
----
npx pkg <NODE_RANGE>-<PLATFORM>-<ARCH>
pnpm build
pnpm exec pkg dist/index.cjs -t '<NODE_RANGE>-<PLATFORM>-<ARCH>' -o 'google-cloud-sql'
# Example
pnpm exec pkg dist/index.cjs -t 'node24-macos-arm64' -o 'google-cloud-sql'
----

@@ -107,0 +110,0 @@

22
# [2.0.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.10.0...v2.0.0) (2026-01-10)
* feat!(alloyDB): add alloyDB ([3653ce2](https://github.com/edosrecki/google-cloud-sql-cli/commit/3653ce2311d3ae24b1dfea8ada5aab88b3a3af5a))
### BREAKING CHANGES
* Configuration file format changes in order to support
both Cloud SQL and AlloyDB instances. Migration from v1 to v2 should
be automatic on the first run of the v2 of the CLI.
Relates SUITEDEV-39317
# [1.10.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.9.4...v1.10.0) (2025-05-20)
### Features
* upgrade to cloud sql auth proxy v2 ([3cc42eb](https://github.com/edosrecki/google-cloud-sql-cli/commit/3cc42eb5b34d176794da3c31bd7114ba459ebf5b))
## [1.9.4](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.9.3...v1.9.4) (2024-11-18)
## [1.9.3](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.9.2...v1.9.3) (2024-01-15)
## [1.9.2](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.9.1...v1.9.2) (2023-11-15)
## [1.9.1](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.9.0...v1.9.1) (2023-10-03)
# [1.9.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.8.2...v1.9.0) (2023-08-15)
### Features
* increase pod wait timeout to 5min ([b80736c](https://github.com/edosrecki/google-cloud-sql-cli/commit/b80736c486d28eaf1ce32c10de3b227b2a8054d8))
## [1.8.2](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.8.1...v1.8.2) (2023-07-24)
## [1.8.1](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.8.0...v1.8.1) (2023-01-11)
### Bug Fixes
* kebab case pod name on kubectl run ([e5253fe](https://github.com/edosrecki/google-cloud-sql-cli/commit/e5253fe3c7a0227d1323161255ea36c02a6ca672))
# [1.8.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.7.5...v1.8.0) (2022-11-11)
### Features
* support IAM login ([e8dcf6d](https://github.com/edosrecki/google-cloud-sql-cli/commit/e8dcf6d796984049e2a3b6a44b1ef458dd538c48))
## [1.7.5](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.7.4...v1.7.5) (2022-11-04)
### Bug Fixes
* **release:** update package.json version ([64c349a](https://github.com/edosrecki/google-cloud-sql-cli/commit/64c349aaeb48645d114e71c531ccfd8bb860cc02))
## [1.7.4](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.7.3...v1.7.4) (2022-11-04)
## [1.7.3](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.7.2...v1.7.3) (2022-11-04)
## [1.7.2](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.7.1...v1.7.2) (2022-11-04)
## [1.7.1](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.7.0...v1.7.1) (2022-05-18)
### Bug Fixes
* service-account flag removed in latest kubectl ([1be49fd](https://github.com/edosrecki/google-cloud-sql-cli/commit/1be49fd859d9ca472b58f582080e49be762e0374))
# [1.7.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.6.1...v1.7.0) (2022-01-31)
### Features
* add update notifier ([7be885c](https://github.com/edosrecki/google-cloud-sql-cli/commit/7be885cbc80da6ee529187c36d586ef6e7075b5c))
## [1.6.1](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.6.0...v1.6.1) (2022-01-31)
### Bug Fixes
* **npm:** add .npmignore ([63405a4](https://github.com/edosrecki/google-cloud-sql-cli/commit/63405a4368dde9ea85a3637fb0d59eb258926699))
# [1.6.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.5.4...v1.6.0) (2022-01-31)
### Bug Fixes
* release from master branch ([9e63d7f](https://github.com/edosrecki/google-cloud-sql-cli/commit/9e63d7f8490c9b275b051de1de8a6091ca13e53a))
### Features
* add safe-to-evict pod annotation ([904fa26](https://github.com/edosrecki/google-cloud-sql-cli/commit/904fa26c4f5aaa380902ca66c1658225bc723f06))
## [1.5.4](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.5.3...v1.5.4) (2022-01-31)
### Bug Fixes
* deploy new GitHub Release ([b4d9c94](https://github.com/edosrecki/google-cloud-sql-cli/commit/b4d9c94fa92c1aa8cb0a4b67b4d8ec73d4c332e7))
## [1.5.3](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.5.2...v1.5.3) (2022-01-26)
### Bug Fixes
* add shebang to index file ([40ddc86](https://github.com/edosrecki/google-cloud-sql-cli/commit/40ddc86a4c167e76e8dee294d71cd10d23ab0605))
## [1.5.2](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.5.1...v1.5.2) (2022-01-26)
## [1.5.1](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.5.0...v1.5.1) (2022-01-25)
### Bug Fixes
* configurations run fails ([6c7d84f](https://github.com/edosrecki/google-cloud-sql-cli/commit/6c7d84fb02580d287892a308ab32fce8af5e00e5))
* throw error if cmd return code not 0 ([79ec7f9](https://github.com/edosrecki/google-cloud-sql-cli/commit/79ec7f9a70748e7fe07b577d583fb7c399bc010a))
# [1.5.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.4.0...v1.5.0) (2022-01-25)
### Features
* print command errors to user ([7f11505](https://github.com/edosrecki/google-cloud-sql-cli/commit/7f11505c13ce9a89bbc790e571748e3d51b25271))
# [1.4.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.3.0...v1.4.0) (2022-01-24)
### Features
* add non-interactive run command ([0edb68d](https://github.com/edosrecki/google-cloud-sql-cli/commit/0edb68db764604b02e235fc7c19c568a0d0c942d))
# [1.3.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.2.0...v1.3.0) (2022-01-23)
### Features
* alias create command as edit ([a6052f7](https://github.com/edosrecki/google-cloud-sql-cli/commit/a6052f7be500769211bf7c615398dfe4f87bdf6d))
# [1.2.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.1.1...v1.2.0) (2022-01-23)
### Features
* support kubectl contexts ([753f711](https://github.com/edosrecki/google-cloud-sql-cli/commit/753f711d7087482141155b976ebf23fa5656793f))
## [1.1.1](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.1.0...v1.1.1) (2022-01-23)
### Bug Fixes
* brew formula ([3f49f8d](https://github.com/edosrecki/google-cloud-sql-cli/commit/3f49f8d6e8f53d6e6e2143c76339c1f2e6761004))
# [1.1.0](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.0.2...v1.1.0) (2022-01-23)
### Features
* support MySQL and SQL Server ([7ff7359](https://github.com/edosrecki/google-cloud-sql-cli/commit/7ff73599ce5ccf55232e1c32891d71a7b0232c79))
## [1.0.2](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.0.1...v1.0.2) (2022-01-23)
### Bug Fixes
* homebrew formula description ([d94f74f](https://github.com/edosrecki/google-cloud-sql-cli/commit/d94f74fd49774e8bd67c72fa96b515cd902eaf7b))
## [1.0.1](https://github.com/edosrecki/google-cloud-sql-cli/compare/v1.0.0...v1.0.1) (2022-01-23)
### Bug Fixes
* homebrew deployment ([9ff5b2a](https://github.com/edosrecki/google-cloud-sql-cli/commit/9ff5b2a38426bc1d92ffa236078365242f3d46a7))
# 1.0.0 (2022-01-23)
### Features
* add semantic release ([bb600da](https://github.com/edosrecki/google-cloud-sql-cli/commit/bb600da2862e52ebdca924febf0955bdfb9afffc))
* initial version of google-cloud-sql CLI app ([f7cd542](https://github.com/edosrecki/google-cloud-sql-cli/commit/f7cd542446cd856f7deb7f7d3fba96c371cc6989))
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createConfiguration = void 0;
const chalk_1 = require("chalk");
const inquirer_1 = __importDefault(require("inquirer"));
const inquirer_autocomplete_prompt_1 = __importDefault(require("inquirer-autocomplete-prompt"));
const configurations_1 = require("../../lib/configurations");
const google_alloydb_instance_1 = require("./prompts/google-alloydb-instance");
const configuration_name_1 = require("./prompts/configuration-name");
const confirmation_1 = require("./prompts/confirmation");
const database_type_1 = require("./prompts/database-type");
const google_cloud_project_1 = require("./prompts/google-cloud-project");
const google_cloud_sql_instance_1 = require("./prompts/google-cloud-sql-instance");
const kubernetes_context_1 = require("./prompts/kubernetes-context");
const kubernetes_namespace_1 = require("./prompts/kubernetes-namespace");
const kubernetes_service_account_1 = require("./prompts/kubernetes-service-account");
const local_port_1 = require("./prompts/local-port");
const createConfiguration = async () => {
inquirer_1.default.registerPrompt('autocomplete', inquirer_autocomplete_prompt_1.default);
const answers = await inquirer_1.default.prompt([
google_cloud_project_1.googleCloudProjectPrompt,
database_type_1.databaseTypePrompt,
google_cloud_sql_instance_1.googleCloudSqlInstancePrompt,
google_alloydb_instance_1.googleAlloyDbInstancePrompt,
kubernetes_context_1.kubernetesContextPrompt,
kubernetes_namespace_1.kubernetesNamespacePrompt,
kubernetes_service_account_1.kubernetesServiceAccountPrompt,
local_port_1.localPortPrompt,
configuration_name_1.configurationNamePrompt,
confirmation_1.confirmationPrompt,
]);
if (answers.confirmation) {
(0, configurations_1.saveConfiguration)(answers);
console.log((0, chalk_1.green)(`Saved configuration '${(0, chalk_1.bold)(answers.configurationName)}'.`));
}
else {
console.log((0, chalk_1.red)('You are excused.'));
}
};
exports.createConfiguration = createConfiguration;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addConfigurationsCommands = addConfigurationsCommands;
const commander_1 = require("commander");
const error_1 = require("../../lib/util/error");
const create_1 = require("./create");
const path_1 = require("./path");
const remove_1 = require("./remove");
const run_1 = require("./run");
const show_1 = require("./show");
async function addConfigurationsCommands(program) {
const configurations = new commander_1.Command('configurations');
configurations.description('create and run SQL auth proxy configurations');
configurations
.command('create')
.alias('edit')
.description('create or edit Cloud SQL Auth Proxy configuration')
.action(async () => {
try {
await (0, create_1.createConfiguration)();
}
catch (error) {
(0, error_1.logError)(error);
}
});
configurations
.command('show')
.description('show Cloud SQL Auth Proxy configuration')
.action(async () => {
try {
await (0, show_1.showConfiguration)();
}
catch (error) {
(0, error_1.logError)(error);
}
});
configurations
.command('remove')
.alias('rm')
.description('remove Cloud SQL Auth Proxy configuration')
.action(async () => {
try {
await (0, remove_1.removeConfiguration)();
}
catch (error) {
(0, error_1.logError)(error);
}
});
configurations
.command('run')
.argument('[name]', 'configuration name, optional')
.description('run Cloud SQL Auth Proxy configuration')
.action(async (name) => {
try {
if (name) {
(0, run_1.runConfigurationByName)(name);
}
else {
await (0, run_1.runConfiguration)();
}
}
catch (error) {
(0, error_1.logError)(error);
}
});
configurations
.command('path')
.description('show path to local configurations file')
.action(() => {
(0, path_1.showConfigurationPath)();
});
program.addCommand(configurations);
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.showConfigurationPath = void 0;
const chalk_1 = require("chalk");
const configurations_1 = require("../../lib/configurations");
const showConfigurationPath = () => {
console.log((0, chalk_1.bold)(configurations_1.configurationPath));
};
exports.showConfigurationPath = showConfigurationPath;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.configurationNamePrompt = void 0;
exports.configurationNamePrompt = {
type: 'input',
name: 'configurationName',
message: 'Enter configuration name:',
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.configurationPrompt = void 0;
const configurations_1 = require("../../../lib/configurations");
const search_1 = require("../../../lib/util/search");
const error_1 = require("../../../lib/util/error");
const formatConfiguration = (configuration) => {
return {
name: configuration.configurationName,
short: configuration.configurationName,
value: configuration,
};
};
const source = (0, error_1.tryCatch)((answers, input) => {
const configurations = (0, configurations_1.getConfigurations)();
const filtered = (0, search_1.searchByKey)(configurations, 'configurationName', input);
return filtered.map(formatConfiguration);
});
exports.configurationPrompt = {
type: 'autocomplete',
name: 'configuration',
message: 'Choose configuration:',
source,
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.confirmationPrompt = void 0;
exports.confirmationPrompt = {
type: 'confirm',
name: 'confirmation',
message: 'Do you want to proceed?',
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.databaseTypePrompt = void 0;
exports.databaseTypePrompt = {
type: 'list',
name: 'databaseType',
message: 'Choose database type:',
choices: [
{ name: 'Cloud SQL', value: 'cloudsql' },
{ name: 'AlloyDB', value: 'alloydb' },
],
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.googleAlloyDbInstancePrompt = void 0;
const lodash_1 = require("lodash");
const alloydb_instances_1 = require("../../../lib/gcloud/alloydb-instances");
const search_1 = require("../../../lib/util/search");
const error_1 = require("../../../lib/util/error");
const formatInstance = (instance) => {
const { name, region, cluster } = instance;
return {
name: `${name} (cluster: ${cluster}, region: ${region})`,
short: name,
value: (0, lodash_1.pick)(instance, 'connectionName', 'port'),
};
};
const source = (0, error_1.tryCatch)((answers, input) => {
const instances = (0, alloydb_instances_1.fetchGoogleAlloyDbInstances)(answers.googleCloudProject);
const filtered = (0, search_1.searchByKey)(instances, 'connectionName', input);
return filtered.map(formatInstance);
});
exports.googleAlloyDbInstancePrompt = {
type: 'autocomplete',
name: 'databaseInstance',
message: 'Choose Google AlloyDB instance:',
source,
when: (answers) => answers.databaseType === 'alloydb',
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.googleCloudProjectPrompt = void 0;
const projects_1 = require("../../../lib/gcloud/projects");
const error_1 = require("../../../lib/util/error");
const search_1 = require("../../../lib/util/search");
const source = (0, error_1.tryCatch)((answers, input) => {
const projects = (0, projects_1.fetchGoogleCloudProjects)();
return (0, search_1.search)(projects, input);
});
exports.googleCloudProjectPrompt = {
type: 'autocomplete',
name: 'googleCloudProject',
message: 'Choose Google Cloud project:',
source,
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.googleCloudSqlInstancePrompt = void 0;
const lodash_1 = require("lodash");
const sql_instances_1 = require("../../../lib/gcloud/sql-instances");
const search_1 = require("../../../lib/util/search");
const error_1 = require("../../../lib/util/error");
const formatInstance = (instance) => {
const { name, region } = instance;
return {
name: `${name} (${region})`,
short: name,
value: (0, lodash_1.pick)(instance, 'connectionName', 'port'),
};
};
const source = (0, error_1.tryCatch)((answers, input) => {
const instances = (0, sql_instances_1.fetchGoogleCloudSqlInstances)(answers.googleCloudProject);
const filtered = (0, search_1.searchByKey)(instances, 'connectionName', input);
return filtered.map(formatInstance);
});
exports.googleCloudSqlInstancePrompt = {
type: 'autocomplete',
name: 'databaseInstance',
message: 'Choose Google Cloud SQL instance:',
source,
when: (answers) => answers.databaseType === 'cloudsql',
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.kubernetesContextPrompt = void 0;
const contexts_1 = require("../../../lib/kubectl/contexts");
const search_1 = require("../../../lib/util/search");
const error_1 = require("../../../lib/util/error");
const source = (0, error_1.tryCatch)((answers, input) => {
const instances = (0, contexts_1.fetchKubernetesContexts)();
return (0, search_1.search)(instances, input);
});
exports.kubernetesContextPrompt = {
type: 'autocomplete',
name: 'kubernetesContext',
message: 'Choose Kubernetes context:',
source,
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.kubernetesNamespacePrompt = void 0;
const namespaces_1 = require("../../../lib/kubectl/namespaces");
const search_1 = require("../../../lib/util/search");
const error_1 = require("../../../lib/util/error");
const source = (0, error_1.tryCatch)((answers, input) => {
const instances = (0, namespaces_1.fetchKubernetesNamespaces)(answers.kubernetesContext);
return (0, search_1.search)(instances, input);
});
exports.kubernetesNamespacePrompt = {
type: 'autocomplete',
name: 'kubernetesNamespace',
message: 'Choose Kubernetes namespace:',
source,
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.kubernetesServiceAccountPrompt = void 0;
const service_accounts_1 = require("../../../lib/kubectl/service-accounts");
const search_1 = require("../../../lib/util/search");
const error_1 = require("../../../lib/util/error");
const source = (0, error_1.tryCatch)((answers, input) => {
const instances = (0, service_accounts_1.fetchKubernetesServiceAccounts)(answers.kubernetesContext, answers.kubernetesNamespace);
return (0, search_1.search)(instances, input);
});
exports.kubernetesServiceAccountPrompt = {
type: 'autocomplete',
name: 'kubernetesServiceAccount',
message: 'Choose Kubernetes service account:',
source,
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.localPortPrompt = void 0;
exports.localPortPrompt = {
type: 'number',
name: 'localPort',
message: 'Enter local port for port-forwarding:',
};
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeConfiguration = void 0;
const chalk_1 = require("chalk");
const inquirer_1 = __importDefault(require("inquirer"));
const inquirer_autocomplete_prompt_1 = __importDefault(require("inquirer-autocomplete-prompt"));
const configurations_1 = require("../../lib/configurations");
const configuration_1 = require("./prompts/configuration");
const confirmation_1 = require("./prompts/confirmation");
const removeConfiguration = async () => {
inquirer_1.default.registerPrompt('autocomplete', inquirer_autocomplete_prompt_1.default);
const { configuration, confirmation } = await inquirer_1.default.prompt([
configuration_1.configurationPrompt,
confirmation_1.confirmationPrompt,
]);
if (confirmation) {
(0, configurations_1.deleteConfiguration)(configuration.configurationName);
console.log((0, chalk_1.green)(`Deleted configuration '${(0, chalk_1.bold)(configuration.configurationName)}'.`));
}
else {
console.log((0, chalk_1.red)('You are excused.'));
}
};
exports.removeConfiguration = removeConfiguration;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runConfigurationByName = exports.runConfiguration = void 0;
const chalk_1 = require("chalk");
const inquirer_1 = __importDefault(require("inquirer"));
const inquirer_autocomplete_prompt_1 = __importDefault(require("inquirer-autocomplete-prompt"));
const configurations_1 = require("../../lib/configurations");
const configuration_1 = require("./prompts/configuration");
const confirmation_1 = require("./prompts/confirmation");
const runConfiguration = async () => {
inquirer_1.default.registerPrompt('autocomplete', inquirer_autocomplete_prompt_1.default);
const { configuration, confirmation } = await inquirer_1.default.prompt([
configuration_1.configurationPrompt,
confirmation_1.confirmationPrompt,
]);
if (confirmation) {
(0, configurations_1.execConfiguration)(configuration);
}
else {
console.log((0, chalk_1.red)('You are excused.'));
}
};
exports.runConfiguration = runConfiguration;
const runConfigurationByName = (name) => {
const configuration = (0, configurations_1.getConfiguration)(name);
if (!configuration) {
throw new Error(`Configuration '${name}' does not exist.`);
}
(0, configurations_1.execConfiguration)(configuration);
};
exports.runConfigurationByName = runConfigurationByName;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.showConfiguration = void 0;
const inquirer_1 = __importDefault(require("inquirer"));
const inquirer_autocomplete_prompt_1 = __importDefault(require("inquirer-autocomplete-prompt"));
const configuration_1 = require("./prompts/configuration");
const showConfiguration = async () => {
inquirer_1.default.registerPrompt('autocomplete', inquirer_autocomplete_prompt_1.default);
const { configuration } = await inquirer_1.default.prompt([
configuration_1.configurationPrompt,
]);
console.dir(configuration);
};
exports.showConfiguration = showConfiguration;
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const configurations_1 = require("./commands/configurations");
const updates_1 = require("./lib/updates");
const version_1 = require("./lib/version");
async function main() {
(0, updates_1.notifyForUpdates)();
const program = new commander_1.Command();
program.name('google-cloud-sql').version(version_1.version);
(0, configurations_1.addConfigurationsCommands)(program);
program.parse(process.argv);
}
main().catch((error) => {
console.error(error);
});
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.currentVersion = exports.configurationsKey = exports.versionKey = void 0;
exports.versionKey = 'version';
exports.configurationsKey = 'configurations';
// Must be semver ('conf' library requirement)
exports.currentVersion = '2.0.0';
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.execConfiguration = exports.deleteConfiguration = exports.saveConfiguration = exports.getConfiguration = exports.getConfigurations = exports.configurationPath = void 0;
const exit_hook_1 = __importDefault(require("exit-hook"));
const lodash_1 = require("lodash");
const pods_1 = require("../kubectl/pods");
const array_1 = require("../util/array");
const string_1 = require("../util/string");
const store_1 = require("./store");
const constants_1 = require("./constants");
const searchKey = 'configurationName';
const excludeProperties = ['googleCloudProject', 'confirmation'];
exports.configurationPath = store_1.store.path;
const getConfigurations = () => {
return store_1.store.get(constants_1.configurationsKey);
};
exports.getConfigurations = getConfigurations;
const getConfiguration = (name) => {
const configurations = (0, exports.getConfigurations)();
return (0, array_1.findByKey)(configurations, searchKey, name);
};
exports.getConfiguration = getConfiguration;
const saveConfiguration = (answers) => {
const configuration = (0, lodash_1.omit)(answers, excludeProperties);
const configurations = store_1.store.get(constants_1.configurationsKey);
(0, array_1.appendOrReplaceByKey)(configurations, configuration, searchKey);
store_1.store.set(constants_1.configurationsKey, configurations);
};
exports.saveConfiguration = saveConfiguration;
const deleteConfiguration = (configuratioName) => {
const configurations = store_1.store.get(constants_1.configurationsKey);
(0, array_1.deleteByKey)(configurations, searchKey, configuratioName);
store_1.store.set(constants_1.configurationsKey, configurations);
};
exports.deleteConfiguration = deleteConfiguration;
const execConfiguration = (configuration) => {
const pod = {
name: `${configuration.databaseType}-proxy-${(0, lodash_1.kebabCase)(configuration.configurationName)}-${(0, string_1.randomString)()}`,
context: configuration.kubernetesContext,
namespace: configuration.kubernetesNamespace,
serviceAccount: configuration.kubernetesServiceAccount,
instance: configuration.databaseInstance.connectionName,
localPort: configuration.localPort,
remotePort: configuration.databaseInstance.port,
databaseType: configuration.databaseType,
};
(0, exit_hook_1.default)(() => {
(0, pods_1.deletePod)(pod);
});
(0, pods_1.runProxyPod)(pod);
(0, pods_1.waitForPodReady)(pod);
(0, pods_1.portForward)(pod);
};
exports.execConfiguration = execConfiguration;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.migrateV1ToV2 = void 0;
const migrateConfigurationV1ToV2 = (v1) => ({
configurationName: v1.configurationName,
databaseType: 'cloudsql',
databaseInstance: {
connectionName: v1.googleCloudSqlInstance.connectionName,
port: v1.googleCloudSqlInstance.port,
},
kubernetesContext: v1.kubernetesContext,
kubernetesNamespace: v1.kubernetesNamespace,
kubernetesServiceAccount: v1.kubernetesServiceAccount,
localPort: v1.localPort,
});
const migrateV1ToV2 = (store) => {
const v1Configurations = store.get('configurations');
const v2Configurations = v1Configurations.map(migrateConfigurationV1ToV2);
// store.set('version', '2')
store.set('configurations', v2Configurations);
};
exports.migrateV1ToV2 = migrateV1ToV2;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.store = void 0;
const conf_1 = __importDefault(require("conf"));
const migrate_v1_v2_1 = require("./migrations/migrate-v1-v2");
const constants_1 = require("./constants");
exports.store = new conf_1.default({
configName: 'configurations',
projectSuffix: '',
projectVersion: constants_1.currentVersion,
migrations: {
'2.0.0': store => (0, migrate_v1_v2_1.migrateV1ToV2)(store),
},
schema: {
version: {
type: 'string',
default: constants_1.currentVersion,
},
configurations: {
type: 'array',
default: [],
items: {
type: 'object',
properties: {
configurationName: { type: 'string' },
databaseType: { type: 'string', enum: ['cloudsql', 'alloydb'] },
databaseInstance: {
type: 'object',
properties: {
connectionName: { type: 'string' },
port: { type: 'number' },
},
},
kubernetesContext: { type: 'string' },
kubernetesNamespace: { type: 'string' },
kubernetesServiceAccount: { type: 'string' },
localPort: { type: 'number' },
},
},
},
},
});
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchGoogleAlloyDbInstances = void 0;
const memoizee_1 = __importDefault(require("memoizee"));
const exec_1 = require("../util/exec");
const parseInstance = (connectionName) => {
// projects/{project}/locations/{region}/clusters/{cluster}/instances/{instance}
const nameParts = connectionName.split('/');
const region = nameParts[3];
const cluster = nameParts[5];
const instance = nameParts[7];
return {
name: instance,
region,
cluster,
connectionName,
port: 5432,
};
};
exports.fetchGoogleAlloyDbInstances = (0, memoizee_1.default)((project) => {
try {
const instances = (0, exec_1.execCommandMultiline)(`
gcloud alloydb instances list \
--project=${project} \
--format='csv(name)' \
--quiet
`);
return instances.slice(1).map(parseInstance);
}
catch {
return [];
}
});
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchGoogleCloudProjects = void 0;
const memoizee_1 = __importDefault(require("memoizee"));
const exec_1 = require("../util/exec");
exports.fetchGoogleCloudProjects = (0, memoizee_1.default)(() => {
return (0, exec_1.execCommandMultiline)(`
gcloud projects list --format='value(projectId)'
`);
});
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchGoogleCloudSqlInstances = void 0;
const memoizee_1 = __importDefault(require("memoizee"));
const exec_1 = require("../util/exec");
const versionNameToPort = (version) => {
const lowercased = version.toLowerCase();
if (lowercased.includes('mysql'))
return 3306;
if (lowercased.includes('postgres'))
return 5432;
return 1433;
};
const parseInstance = (instance) => {
const [connectionName, version] = instance.split(',');
const [, region, name] = connectionName.split(':');
const port = versionNameToPort(version);
return { name, region, connectionName, port };
};
exports.fetchGoogleCloudSqlInstances = (0, memoizee_1.default)((project) => {
const instances = (0, exec_1.execCommandMultiline)(`
gcloud sql instances list \
--project=${project} \
--format='csv(connectionName,databaseVersion)' \
--quiet
`);
// skip header line
return instances.slice(1).map(parseInstance);
});
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchKubernetesContexts = exports.fetchKubernetesCurrentContext = void 0;
const memoizee_1 = __importDefault(require("memoizee"));
const exec_1 = require("../util/exec");
const fetchKubernetesCurrentContext = () => {
return (0, exec_1.execCommand)(`kubectl config current-context`);
};
exports.fetchKubernetesCurrentContext = fetchKubernetesCurrentContext;
exports.fetchKubernetesContexts = (0, memoizee_1.default)(() => {
return (0, exec_1.execCommandMultiline)(`
kubectl config get-contexts --output='name'
`);
});
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchKubernetesNamespaces = void 0;
const memoizee_1 = __importDefault(require("memoizee"));
const exec_1 = require("../util/exec");
exports.fetchKubernetesNamespaces = (0, memoizee_1.default)((context) => {
return (0, exec_1.execCommandMultiline)(`
kubectl get namespaces \
--context="${context}" \
--output='jsonpath={range .items[*]}{.metadata.name}{"\\n"}{end}'
`);
});
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.portForward = exports.waitForPodReady = exports.deletePod = exports.runProxyPod = exports.runAlloyDbProxyPod = exports.runCloudSqlProxyPod = void 0;
const chalk_1 = require("chalk");
const exec_1 = require("../util/exec");
const runCloudSqlProxyPod = (pod) => {
return (0, exec_1.execCommand)(`
kubectl run \
--image=gcr.io/cloud-sql-connectors/cloud-sql-proxy \
--context="${pod.context}" \
--namespace="${pod.namespace}" \
--overrides='{"spec": {"serviceAccount": "${pod.serviceAccount}"}}' \
--annotations="cluster-autoscaler.kubernetes.io/safe-to-evict=true" \
--labels=app=google-cloud-sql \
${pod.name} \
-- --auto-iam-authn --auto-ip '${pod.instance}?port=${pod.remotePort}'
`);
};
exports.runCloudSqlProxyPod = runCloudSqlProxyPod;
const runAlloyDbProxyPod = (pod) => {
return (0, exec_1.execCommand)(`
kubectl run \
--image=gcr.io/alloydb-connectors/alloydb-auth-proxy \
--context="${pod.context}" \
--namespace="${pod.namespace}" \
--overrides='{"spec": {"serviceAccount": "${pod.serviceAccount}"}}' \
--annotations="cluster-autoscaler.kubernetes.io/safe-to-evict=true" \
--labels=app=google-cloud-alloydb \
${pod.name} \
-- --address=0.0.0.0 --port=${pod.remotePort} --auto-iam-authn --structured-logs '${pod.instance}'
`);
};
exports.runAlloyDbProxyPod = runAlloyDbProxyPod;
const runProxyPod = (pod) => {
if (pod.databaseType === 'alloydb') {
(0, exports.runAlloyDbProxyPod)(pod);
}
else {
(0, exports.runCloudSqlProxyPod)(pod);
}
};
exports.runProxyPod = runProxyPod;
const deletePod = (pod) => {
console.log(`Deleting pod '${(0, chalk_1.bold)((0, chalk_1.cyan)(pod.name))}'.`);
(0, exec_1.execCommand)(`
kubectl delete pod ${pod.name} \
--context="${pod.context}" \
--namespace="${pod.namespace}"
`);
console.log(`Pod '${(0, chalk_1.bold)((0, chalk_1.cyan)(pod.name))}' deleted.`);
};
exports.deletePod = deletePod;
const waitForPodReady = (pod) => {
console.log(`Waiting for pod '${(0, chalk_1.bold)((0, chalk_1.cyan)(pod.name))}'.`);
(0, exec_1.execCommand)(`
kubectl wait pod ${pod.name} \
--for=condition=ready \
--timeout=300s \
--context="${pod.context}" \
--namespace="${pod.namespace}"
`);
console.log(`Pod '${(0, chalk_1.bold)((0, chalk_1.cyan)(pod.name))}' is ready.`);
};
exports.waitForPodReady = waitForPodReady;
const portForward = (pod) => {
console.log(`Starting port forwarding to pod '${(0, chalk_1.bold)((0, chalk_1.cyan)(pod.name))}'.`);
(0, exec_1.execCommandAttached)(`
kubectl port-forward ${pod.name} ${pod.localPort}:${pod.remotePort} \
--context="${pod.context}" \
--namespace="${pod.namespace}"
`);
};
exports.portForward = portForward;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchKubernetesServiceAccounts = void 0;
const memoizee_1 = __importDefault(require("memoizee"));
const exec_1 = require("../util/exec");
exports.fetchKubernetesServiceAccounts = (0, memoizee_1.default)((context, namespace) => {
return (0, exec_1.execCommandMultiline)(`
kubectl get serviceaccounts \
--namespace="${namespace}" \
--context="${context}" \
--output='jsonpath={range .items[*]}{.metadata.name}{"\\n"}{end}'
`);
});
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.notifyForUpdates = void 0;
const boxen_1 = __importDefault(require("boxen"));
const update_notifier_1 = __importDefault(require("update-notifier"));
const chalk_1 = require("chalk");
const version_1 = require("./version");
const name = 'google-cloud-sql';
const oneDayMs = 24 * 60 * 60 * 1000;
const notifyForUpdates = () => {
const { update } = (0, update_notifier_1.default)({
pkg: {
name,
version: version_1.version,
},
updateCheckInterval: oneDayMs,
});
if (update && update.current !== update.latest) {
const text = `${(0, chalk_1.yellow)(update.current)} ↦ ${(0, chalk_1.green)(update.latest)}\n\n`
+ `${(0, chalk_1.blue)('brew')} upgrade ${name}\n`
+ `${(0, chalk_1.blue)('npm')} i -g ${name}`;
const box = (0, boxen_1.default)(text, {
title: 'Update Available',
titleAlignment: 'center',
borderColor: 'yellow',
padding: 1,
margin: 1,
});
console.log(box);
}
};
exports.notifyForUpdates = notifyForUpdates;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deleteByKey = exports.appendOrReplaceByKey = exports.findByKey = void 0;
const findIndexByKey = (items, key, value) => items.findIndex(item => item[key] === value);
const findByKey = (items, key, value) => items.find(item => item[key] === value);
exports.findByKey = findByKey;
const appendOrReplaceByKey = (items, item, key) => {
const index = findIndexByKey(items, key, item[key]);
if (index >= 0) {
items[index] = item;
}
else {
items.push(item);
}
};
exports.appendOrReplaceByKey = appendOrReplaceByKey;
const deleteByKey = (items, key, value) => {
const index = findIndexByKey(items, key, value);
if (index >= 0)
items.splice(index, 1);
};
exports.deleteByKey = deleteByKey;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.logError = exports.tryCatch = exports.CommandExecutionError = void 0;
const chalk_1 = require("chalk");
class CommandExecutionError extends Error {
data;
constructor(command, stderr, stdout) {
super('Error while executing command.');
this.data
= `${(0, chalk_1.bold)((0, chalk_1.red)(this.message))}\n`
+ ` ${(0, chalk_1.bold)('command')}: ${command.trim()}\n`
+ ` ${(0, chalk_1.bold)('stderr')}: ${stderr.trim()}\n`
+ (stdout ? ` ${(0, chalk_1.bold)('stdout')}: ${stdout.trim()}\n` : '');
}
}
exports.CommandExecutionError = CommandExecutionError;
const tryCatch = (fn) => {
return (a, b) => {
try {
return fn(a, b);
}
catch (error) {
(0, exports.logError)(error);
throw error;
}
};
};
exports.tryCatch = tryCatch;
const logError = (error) => {
if (error instanceof CommandExecutionError) {
console.error(error.data);
}
else {
console.error(error);
}
};
exports.logError = logError;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.execCommandAttached = exports.execCommandMultiline = exports.execCommand = void 0;
const os_1 = require("os");
const shelljs_1 = __importDefault(require("shelljs"));
const error_1 = require("./error");
const execCommand = (command) => {
const { stdout, stderr, code } = shelljs_1.default.exec(command, { silent: true });
if (code !== 0) {
throw new error_1.CommandExecutionError(command, stderr, stdout);
}
return stdout.trim();
};
exports.execCommand = execCommand;
const execCommandMultiline = (command) => {
return (0, exports.execCommand)(command).split(os_1.EOL);
};
exports.execCommandMultiline = execCommandMultiline;
const execCommandAttached = (command) => {
shelljs_1.default.exec(command);
};
exports.execCommandAttached = execCommandAttached;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toBoolean = exports.toInt = void 0;
const toInt = (value) => parseInt(value, 10);
exports.toInt = toInt;
const trueValues = new Set(['true', 'yes', 'on', '1']);
const toBoolean = (value) => trueValues.has(value);
exports.toBoolean = toBoolean;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchByKey = exports.search = exports.fuzzy = void 0;
const fuse_js_1 = __importDefault(require("fuse.js"));
// Interval [0, 1], where 0 = 100% match
const THRESHOLD = 0.2;
const MIN_CHARS = 3;
const fuzzy = (fuse, data, input) => {
if (!input || input.length < MIN_CHARS) {
return data;
}
return fuse.search(input).map(({ item }) => item);
};
exports.fuzzy = fuzzy;
const search = (data, input) => {
const fuse = new fuse_js_1.default(data, { threshold: THRESHOLD });
return (0, exports.fuzzy)(fuse, data, input);
};
exports.search = search;
const searchByKey = (data, key, input) => {
const fuse = new fuse_js_1.default(data, { threshold: THRESHOLD, keys: [key] });
return (0, exports.fuzzy)(fuse, data, input);
};
exports.searchByKey = searchByKey;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.randomString = void 0;
const crypto_1 = __importDefault(require("crypto"));
const randomString = (size = 4) => {
return crypto_1.default.randomBytes(size).toString('hex').slice(0, size);
};
exports.randomString = randomString;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = void 0;
exports.version = '2.0.0';
// @ts-check
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import stylistic from '@stylistic/eslint-plugin';
export default tseslint.config({
files: ['**/*.ts'],
extends: [
eslint.configs.recommended,
...tseslint.configs.recommended,
stylistic.configs.recommended,
]
}, {
files: ["**/*.spec.ts"],
rules: {
"@typescript-eslint/no-unused-expressions": "off"
}
})

Sorry, the diff of this file is not supported yet

{
"extends": "./tsconfig.json",
"exclude": ["src/**/*.spec.ts"]
}
{
"extends": "@tsconfig/node22/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"module": "CommonJS",
"moduleResolution": "Node"
}
}